4.5 脚本和函数(2)
4.5.7 内联函数
内联函数(Inline Function)的属性和编写方式与普通函数文件相同,但相对来说,内联函数的创建简单得多。其调用格式如下。
● inline('CE'):其功能为把字符串表达式“CE”转化为输入变量自动生成的内联函数。本语句将自动对字符串CE进行辨识,其中除了“预定义变量名”(如圆周率pi)、“常用函数名”(如sin、rand等),其他由字母和数字组成的连续字符辨识为变量,连续字符后紧接左括号的,也不会被识别为变量,例如array(1)。
● inline('CE',arg1,arg2,…):其功能为把字符串表达式“CE”转换为arg1、arg2等指定的输入变量的内联函数。本语句创建的内联函数最为可靠,输入变量的字符串用户可以随意改变,但是由于输入变量已经规定,因此生成的内联函数不会出现辨识失误等错误。
● inline('CE',n):其功能为把字符串表达式“CE”转化为n个指定的输入变量的内联函数。本语句对输入变量的字符是有限制的,其字符只能是x,P1 ,…,Pn等,其中P一定为大写字母。
说明:
(1)字符串表达式CE中不能包含赋值符号“=”。
(2)内联函数是沟通eval和feval两个函数的桥梁,只要是eval函数可以操作的表达式,都可以通过inline指令转化为内联函数,这样,内联函数总可以被feval函数调用。MATLAB中的许多内置函数就是通过被转换为内联函数,从而具备了根据被处理的方式不同而变换不同函数形式的能力。
MATLAB中关于内联函数的属性的相关指令如表4-6所示,读者可以根据需要使用。
表4-6 内联函数属性指令集
指令句法 |
功能 |
class(inline_fun) |
提供内联函数的类型 |
char(inline_fun) |
提供内联函数的计算公式 |
argnames(inline_fun) |
提供内联函数的输入变量 |
vectorize(inline_fun) |
使内联函数适用于数组运算的规则 |
例4-22:内联函数的简单运用示例。
(1)示例说明:内联函数的第一种创建格式是使内联函数适用于“数组运算”。
在命令行窗口中输入:
Fun1 = inline('mod(12,5)')
输出结果:
Fun1 = 内联函数: Fun1(x) = mod(12,5)
在命令行窗口中输入:
Fun2 = vectorize(Fun1)
输出结果:
Fun2 = 内联函数: Fun2(x) = mod(12,5)
在命令行窗口中输入:
Fun3 = char(Fun2)
输出结果:
Fun3 = 'mod(12,5)'
(2)示例说明:第一种内联函数创建格式的缺陷在于不能使用多标量构成的向量进行赋值,而使用第二种内联函数创建格式则可以。
在命令行窗口中输入:
Fun4 = inline('m * exp(n(1)) * cos(n(2))'),Fun4(1,[-1 , pi / 2])
输出结果:
Fun4 = 内联函数: Fun4(m) = m * exp(n(1)) * cos(n(2)) 错误使用 inline/subsref (line 14) 内联函数的输入数目太多。
在命令行窗口中输入:
Fun5 = inline('m * exp(n(1)) * cos(n(2))' , 'm' , 'n'),Fun5(1,[-1 , pi / 2])
输出结果:
Fun5 = 内联函数: Fun5(m,n) = m * exp(n(1)) * cos(n(2)) ans = 2.2526e-17
(3)示例说明:产生向量输入、向量输出的内联函数。
在命令行窗口中输入:
y = inline('[3 * x(1) * x(2) ^ 3 ; sin(x(2))]')
输出结果:
y = 内联函数: y(x) = [3 * x(1) * x(2) ^ 3 ; sin(x(2))]
在命令行窗口中输入:
Y = inline('[3 * x(1) * x(2) ^ 3 ; sin(x(2))]')
输出结果:
Y = 内联函数: Y(x) = [3 * x(1) * x(2) ^ 3 ; sin(x(2))]
在命令行窗口中输入:
argnames(Y)
输出结果:
ans = 1×1 cell 数组 {'x'}
在命令行窗口中输入:
x = [10 , pi * 5 / 6];y = Y(x)
输出结果:
y = 538.3034 0.5000
(4)示例说明:以最简练的格式创建内联函数;内联函数可被feval指令调用。
在命令行窗口中输入:
Z = inline('floor(x) * sin(P1) * exp(P2 ^ 2)' , 2)
输出结果:
Z = 内联函数: Z(x,P1,P2) = floor(x) * sin(P1) * exp(P2 ^ 2)
在命令行窗口中输入:
z = Z(2.3 , pi / 8 , 1.2) , fz = feval(Z , 2.3 , pi / 8 , 1.2)
输出结果:
z = 3.2304 fz = 3.2304
4.5.8 向量化和预分配
1.向量化
要想让MATLAB最高速地工作,重要的是在M文件中把算法向量化。其他程序语言可以用for或DO循环,MATLAB则可用向量或矩阵运算。下面的代码用于创立一个算法表:
x = 0.01; for k = 1 : 1001 y(k) = log10(x); x = x + 0.01; end
同样代码的向量化翻译如下:
x = 0.01 : 0.01 : 10; y = log10(x);
对于更复杂的代码,矩阵化选项不总是那么明显。当速度重要时,应该想办法把算法向量化。
2.预分配
若一条代码不能向量化,则可以通过预分配任何输出结果已保存在其中的向量或数组以加快for 循环。例如,下面的代码用zeros函数把for循环产生的向量预分配,这使得for循环的执行速度显著加快。
r = zeros(32,1); for n = 1 : 32 r(n) = rank(magic(n)); end
上例中若没有使用预分配,则MATLAB的注释器利用每次循环扩大r向量。向量预分配排除了该步骤以使执行加快。
4.5.9 函数的函数
一种以标量为变量的非线性函数称为“函数的函数”,即以函数名为自变量的函数。这类函数包括求零点、最优化、求积分和常微分方程等。
MATLAB通过M文件的函数表示该非线性函数。
例如,下例为一个简化的humps函数(humps函数可在路径MATLAB\demos下获得)。
例4-23:函数的函数的简单运用示例。
创建M文件并命名为ex4_23.m,利用M文件编辑器在M文件中输入:
a = 0 : 0.002 : 1; b = humps(a); plot(a , b) % 做出图像 function b = humps(x) % 在区间[0 , 1]内求此函数的值 b = 1 ./ ((x - .3) .^ 2 + .01) + 1 ./((x - .9) .^ 2 + .04) - 6; end
运行文件,输出图像如图4-7所示。
图4-7 运行结果
图像表明函数在x=0.6附近有局部最小值。接下来用函数fminsearch可以求出局部最小值及此时x的值。函数fminsearch第一个参数是函数句柄,第二个参数是此时x的近似值。
在命令行窗口中输入:
p = fminsearch(@humps,.5)
输出结果:
p = 0.6370
在命令行窗口中输入:
humps(p) % 求出此局部最小值
输出结果:
ans = 11.2528
4.5.10 P码文件
一个M文件首次被调用(包括在M文件编辑器中被打开或者在命令行窗口中运行文件名)时,MATLAB将首先对该M文件进行语法分析,并把生成的相应内部伪代码(Psedocode,P码)文件存放在内存中。
当M文件再次被调用时,将直接调用该M文件在内存中的P码文件,而不会再对原M文件进行重复的语法分析。需要注意的是,MATLAB的分析器(Parser)总是把M文件连同在该M文件中被调用的所有函数文件一起转变成P码文件。
P码文件与原码文件具有相同的文件名,但是其扩展名为“.p”;P码文件的运行速度高于原码文件,但对于小规模的文件而言,用户一般体会不到这种速度上的差异。
在MATLAB环境中,假如存在同名的P码文件和原码文件,那么当该文件名被调用时,被执行的一定是P码文件。
P码文件并不是仅当M文件被调用时才能生成的,用户也可以使用MATLAB中的内设指令在M文件中生成P码文件,其调用格式如下。
● pcode FunName:该指令的功能为在当前文件夹中生成FunName.p。
● pcode FunName –inplace:该指令的功能为在Filename.m所在的目录下生成FunName.p。
说明:P码文件相对于原码文件来说有以下两个优点。
(1)运行速度快。
(2)P码文件中的数据为二进制保存,阅读困难,增加了程序的保密性。
MATLAB中还内置了P码文件的相关操作指令,如表4-7所示。
表4-7 P码文件的相关操作指令
指令名 |
功能 |
inmem |
罗列内存中所有P码文件的文件名 |
clear FunName |
清除内存中名为FunName.p的P码文件 |
clear functions |
清除内存中所有的P码文件 |
例4-24:查询内存中所有的P码文件名,并清除指定名称的P码文件。
首先调用inmem函数,查询当前内存中所有的P码文件名,得到如下结果:
inmem ans = 796×1 cell 数组 {'pathdef' } {'userpath' } {'usejava' } {'general\private\openm' } {'general\private\catdirs' } {'initdesktoputils' } {'codetools\private\dataviewerhelper' } {'path' } {'mtree.isempty' } ……
然后使用clear函数,清除名为path的P码文件:
clear path
再次调用inmem函数,查看当前内存中所有的P码文件,发现名为path的P码文件不存在,得到如下结果:
inmem ans = 795×1 cell 数组 {'pathdef' } {'userpath' } {'usejava' } {'general\private\openm' } {'general\private\catdirs' } {'initdesktoputils' } {'codetools\private\dataviewerhelper' } {'mtree.isempty' } {'ispc' } ……