自定义函数

MATLAB和Python除了使用内部提供的函数和第三方提供的函数外,还可以自己定义函数来实现一定的功能。自定义函数同样可以被反复调用,从而节省代码量并提高编程效率。[大谦MATLAB,dqmatlab点com]

函数定义格式

【MATLAB】

下面是自定义函数比较标准的格式,其中黑体加粗的文字表示自定义函数的基本组成部分:

code.matlab
function [x, y]=myfun(a, b, c)        函数定义行
% H1行——用一行文字来综述函数的功能
% 帮助文本——用一行或多行文本解释如何使用函数
% 在命令行中输入"help <functionname>"时可以使用它,对于主函数
% 函数体——一般从第一个空白行后开始。
% 注释 – 描述函数的行为,输入输出的类型等。
% 在命令行中输入"help <functionname>"时不会显示这些文本。
x=prod(a, b); %:开始编写函数代码

所以,一个完整的函数应该包括函数定义行、H1行、帮助文本、函数体、注释和函数代码等方面的内容,其中函数定义行和函数代码是必需的。

【Python】

Python中自定义函数的语法格式为:

code.python
def functionname(parameters):
   "函数说明文档"
   函数体
   return [表达式]

其中,def和return是关键字,functionname为函数名,parameters为参数列表。注意小括号后面有1个冒号。冒号后面第1行添加注释,说明函数的功能,可以使用help函数进行查看。函数体各语句用代码定义函数的功能。def关键字打头,return语句结束,有表达式时返回函数的返回值,没有表达式时返回None。

函数定义好后,可以在模块中其他位置进行调用,调用时指定函数名和参数,如果有返回值,指定引用返回值的变量。

没有返回值的情况

函数可以没有参数,也可以没有返回值。下面定义1个函数,用一连串的星号作为输出内容的分隔行。定义该函数后进行3种运算,并在输出结果时调用该函数绘制星号分隔行分隔各种运算结果。

【MATLAB】

在MATLAB中新建脚本文件startline.m,文件代码为:

code.matlab
a=1;b=2;
sprintf("a=%d,b=%d",a,b)
sprintf("a+b=%d",a+b)
startline0    %第1次调用startline0函数
sprintf("a-b=%d",a-b)
startline0    %第2次调用startline0函数
sprintf("a*b=%d",a*b)
function startline0()
  disp("**********");
end

运行脚本,在命令窗口输出以下结果。

code.matlab
>> startline
ans=
    "a=1,b=2"
ans=
    "a+b=3"
**********
ans=
    "a-b=-1"
**********
ans=
    "a*b=2"

可见,函数定义好以后,可以进行重复调用,提高编程效率。

【Python】

在Python IDLE中新建脚本文件sam14-001.py,文件中的代码为:

code.python
def starline():  #定义starline函数,绘制星号分隔行
    "星号分隔行"  #函数的功能说明
    print('*&#x27;*40)  #输出40个*
    return
a=1;b=2
print('a={},b={}'.format(1,2))
print('a+b={}'.format(a+b))  #对两个数作加法运算
starline()  #调用starline函数绘制分隔行
print('a={},b={}'.format(1,2))
print('a-b={}'.format(a-b))  #对两个数作减法运算
starline()  #调用starline函数绘制分隔行
print('a={},b={}'.format(1,2))
print('a*b={}'.format(a*b))  #对两个数作乘法运算
help(starline)  #输出starline函数的功能说明

在Python IDLE文件脚本窗口,在"Run"菜单中单击"Run Module"选项,则IDLE命令行窗口显示下面的结果:

code.python
>>> =RESTART: …/Samples/ch14/sam14-001.py
a=1,b=2
a+b=3
****************************************
a=1,b=2
a-b=-1
****************************************
a=1,b=2
a*b=2
Help on function starline in module __main__:
starline()
    星号分隔行

可见,函数定义好以后,可以进行重复调用,提高编程效率。最后显示了starline函数的功能说明。

有一个返回值的情况

上面定义的starline函数没有参数,也没有返回值。下面定义一个mysum函数,对两个给定的数求和。该函数有2个输入参数和1个返回值。

【MATLAB】

在MATLAB中新建脚本文件mysum.m,文件代码为:

code.matlab
sprintf("3+6=%d",mysum0(3,6))  %计算和输出3和6的和
sprintf("12+9=%d",mysum0(12,9))  %计算和输出12和9的和
function s=mysum0(a,b)  %求和
    %求两个数的和
    s=a+b;
end

运行脚本,在命令窗口输出以下结果。

code.matlab
>> mysum
ans=
    "3+6=9"
ans=
    "12+9=21"

【Python】

在Python IDLE中新建脚本文件sam14-002.py,文件中的代码为:

code.python
def mysum(a,b):  #求和
    "求两个数的和"
    return a+b
print('3+6={}'.format(mysum(3,6)))  #计算和输出3和6的和
print('12+9={}'.format(mysum(12,9)))  #计算和输出12和9的和

在Python IDLE文件脚本窗口,在"Run"菜单中单击"Run Module"选项,则IDLE命令行窗口显示下面的结果:

code.python
>>> =RESTART: …/Samples/ch14/ sam14-002.py
3+6=9
12+9=21

有多个返回值的情况

函数有多个返回值时,也可以将这多个返回值添加到数组中,返回该数组。

【MATLAB】

在MATLAB中新建脚本文件mycomp.m,文件代码为:

code.matlab
[c,d]=mycomp0(2,3);  %调用mycomp0函数,计算2和3的和和差
sprintf("2+3=%d&quot;,c)  %输出和
sprintf("2-3=%d&quot;,d)  %输出差
function [c,d]=mycomp0(a,b)  %计算两个给定值的和和差
  c=a+b;
  d=a-b;
end

运行脚本,在命令窗口输出以下结果。

code.matlab
>> mycomp
ans=
    "2+3=5"
ans=
    "2-3=-1"

【Python】

函数有多个返回值时可以用return语句直接返回,也可以将各返回值写入列表,然后返回列表。

下面定义1个函数,指定2个参数值,返回它们的和和差。该文件路径为Samples\ch10\Python\sam14-003.py。

code.python
def mycomp(a,b):  #计算两个给定值的和和差
    c=a+b
    d=a-b
    return c,d
c,d=mycomp(2,3)  #调用mycomp函数,计算2和3的和和差
print('2+3={}'.format(c))  #输出和
print('2-3={}'.format(d))  #输出差

在Python IDLE文件脚本窗口,在"Run"菜单中单击"Run Module"选项,则IDLE命令行窗口显示下面的结果:

code.python
>>> =RESTART: …/Samples/ch14/sam14-003.py
2+3=5
2-3=-1

有多个返回值时,也可以将这多个返回值添加到列表中,用return语句返回该列表。下面改写上例。该文件路径为Samples\ch14\sam14-004.py。

code.python
def mycomp(a,b):  #计算两个给定值的和和差
    data=[]
    data.append(a+b)
    data.append(a-b)
    return data  #和和差以列表返回
data=mycomp(2,3)  #调用mycomp函数,计算2和3的和和差
print(data)  #输出元素为和和差的列表

在Python IDLE文件脚本窗口,在"Run"菜单中单击"Run Module"选项,则IDLE命令行窗口显示下面的结果:

code.python
>>> =RESTART: …/Samples/ch14/sam14-004.py
 [5, -1]

MATLAB中的主函数和子函数

任何M文件中的第1个函数都被称为主函数。主函数后面可以有任意多个子函数。

在大多数情况下,主函数是M文件中可以从MATLAB命令行或从另一个M文件函数中调用的唯一函数。在调用该函数时,使用定义该函数的M文件的名称。

打开M文件编辑器并输入一个average函数:

code.matlab
function y=average(x)
  % 计算向量元素的平均值
  y=sum(x)/length(x);

保存为M文件average.m。在MATLAB命令窗口用下面的语句计算3个数的平均值。

code.matlab
>> average([12 60 42])
ans =
    38

注意:一般要使主函数名与M文件名相同。如果主函数名与文件名不同,则必须用文件名调用主函数。

M文件中可以包含一个以上的函数。在M文件中,除主函数外的其他函数被称为子函数,并且它们只对主函数或同一个文件中的其他子函数可见。

将主函数放在最上面,子函数放在下面,各子函数位置不分先后。

下面编写子函数mean和median用来计算输入数据的均值和中值。主函数newstats确定参数列表的长度并调用子函数,并将列表长度n传递给它们。

code.matlab
function [avg, med]=newstats(u) % 主函数
  % 本函数用内部函数计算均值和中值
  n=length(u);
  avg=mean(u, n);
  med=median(u, n);

  function a=mean(v, n)          % 子函数
  % 计算均值
  a=sum(v)/n;

  function m=median(v, n)        % 子函数
  % 计算中值
  w=sort(v);
  if rem(n, 2) == 1
     m=w((n+1) / 2);
  else
     m=(w(n/2) + w(n/2+1)) / 2;
  end

保存为M文件newstats.m。在MATLAB命令窗口用下面的语句计算给定数据的均值和中值。

code.matlab
>> [avg,med]=newstats([1 2 3 4 5 6 7 8 9])
avg =
     5
med =
     5

即使在同一个M文件中,子函数也不能获取用于主函数或其他子函数的参数,除非将它们声明为全局变量,或者作为参数传递。

从M文件内部调用函数时,MATLAB首先检查文件,看函数是否为子函数。然后检查该名称的私有函数,最后搜索路径上的独立M文件或内部函数。

可选参数和默认参数

可选参数是非必需参数,可以有,也可以没有。默认参数是定义了默认值的参数,它们必须是可选参数,不能给必需参数定义默认值。对于可选参数而言,调用函数时如果没有赋值,就使用预先定义的默认值,如果赋了新值,就覆盖默认值。

【MATLAB】

利用nargin可以确定函数输入参数的个数。然后可以根据参数个数用条件语句完成不同的任务。在MATLAB中新建脚本文件testarg.m,文件代码为:

code.matlab
a=testarg1(3)    %1个输入参数
b=testarg1(3,10)    %2个输入参数
function c=testarg1(a, b)
  if(nargin==1)
     b=5;    %只有1个输入参数时,b的默认值为5
     c=a+b;
  elseif(nargin==2)
     c=a + b;
  end
end

运行脚本,在命令窗口输出以下结果。

code.matlab
>> testargs
a =
     8
b =
    13

【Python】

定义函数时,对函数参数使用赋值语句可以指定该参数的默认值。下面定义para函数,该函数有两个参数,即a和b,指定b参数的默认值为5。

code.python
def para(a,b=5):  #指定b参数的默认值为5
    return a+b
print(para(3))
print(para(3,10))

在Python IDLE文件脚本窗口,在"Run"菜单中单击"Run Module"选项,则IDLE命令行窗口显示下面的结果:

code.python
>>> =RESTART: …/Samples/ch14/para.py
8
13

可见,没有传入b参数的值时,取了默认值5。

可变参数

所谓可变参数,指的是参数的个数是不确定的,可以是0个、1个直至任意个。

【MATLAB】

使用varargin可以传递任意个数的输入参数给函数。

code.matlab
function [out1,out2]=example(a,b,varargin)

使用元胞数组索引来提取数据。例如

code.matlab
y(n)=varargin{n}(2);

其中,索引表达式{n}获取varargin的第n个单元。表达式(2)表示元胞内容的第二个元素。

当允许有任意多个输出参数时,必须将所有输出打包到varargout元胞数组中。

code.matlab
function [i,j,varargout]=example2(x1,y1,x2,y2,flag)

利用nargout可以确定函数输出参数的个数。

varargin或varargout必须出现在参数列表的最后,即函数调用必须首先指定必要的参数。

在MATLAB中新建脚本文件mysum2.m,文件代码为:

code.matlab
a=mysum(1,2)
b=mysum(1,2,3,4,5)
c=mysum(1,2,3,4,5,6,7,8,9,10)
function c=mysum(a,b,varargin)
  c=a+b;
  for i=1:nargin-2
    c=c+varargin{i}(1);    %元胞数组的第1个元素
  end
end

运行脚本,在命令窗口输出以下结果。

code.matlab
>> mysum2
a =
     3
b =
    15
c =
    55

【Python】

Python中包含可变参数的函数的定义如下所示:

code.python
def functionname([args,] *args_tuple ):
    函数体
    return [表达式]

其中,[args,]定义必选参数,*args_tuple定义可变参数。*args_tuple是作为1个元组传递进来的。

下面定义1个函数进行求和运算,该运算的第1个数据是确定的,后面的数据不确定,数据个数和数据大小都不确定。该文件路径为Samples\ch10\Python\sam14-006.py。

code.python
def mysum(arg1,arg2,*vartuple):  # arg1,arg2为必选参数,*vartuple为可变参数
   sum=arg1+arg2
   for var in vartuple:  #累加求和
      sum+=var
   return sum
a=mysum(1,2)  #调用mysum函数,指定参数求和
b=mysum(1,2,3,4,5)
c=mysum(1,2,3,4,5,6,7,8,9,10)
print(a)
print(b)
print(c)

在Python IDLE文件脚本窗口,在"Run"菜单中单击"Run Module"选项,则IDLE命令行窗口显示下面的结果:

code.python
>>> =RESTART: …/Samples/ch14/mysum.py
3
15
55

传值还是传址

在函数中,对象作为参数传递时,需要搞清楚函数传递的是对象的地址还是对象的值。传址和传值的主要区别在于,如果在函数体中对参数的值进行了修改,调用该函数前后,如果是传址方式传递,该参数的值会改变,如果是传值方式传递,该参数的值不变。

【MATLAB】

在MATLAB中新建脚本文件TP.m,文件代码为:

code.matlab
b="hello";  %给变量b赋初值"hello"
TP0(b);  %将变量b作为参数调用函数
b  %输出变量b的值
function TP0(a)
    a="MATLAB";  %修改参数的值为"MATLAB"
end

运行脚本,在命令窗口输出以下结果。

code.matlab
>> TP
b=
    "hello"

可见,调用函数前后变量的值不变,MATLAB中参数按照传值方式传递。

【Python】

在Python中,对于不可变类型,包括字符串、元组和数字,作为函数参数时是按传值方式传递的。此时传递的是对象的值,修改的是一个复制的对象,不影响对象本身。对于可变类型,包括列表和字典,作为函数参数时是按传址方式传递的。此时传递对象本身,修改它后在函数外部也会受影响。

下面举例进行说明。对于不可变类型,下面的函数传递1个字符串,查看调用该函数前后参数的值有没有变化。该文件路径为Samples\ch14\sam14-008.py。

code.python
def TP(a):
    a='python'  #修改参数的值为"python"
b='hello'  #给变量b赋初值"hello"
TP(b)  #将变量b作为参数调用函数
print(b)  #输出变量b的值

在Python IDLE文件脚本窗口,在"Run"菜单中单击"Run Module"选项,则IDLE命令行窗口显示下面的结果:

code.python
>>> =RESTART: …/Samples/ch14/sam14-008.py
hello

可见,调用函数前后变量的值不变,参数按照传值方式传递。

对于可变类型,下面的函数传递1个列表,在函数体中给列表添加1个列表元素。该文件路径为Samples\ch14\sam14-009.py。

code.python
def TP(lst):  #参数为列表
    lst.append([6,7,8,9])  #给传入的列表添加1个列表元素
    return
lst=[1,2,3,4,5]
print(lst)
TP(lst)  #将列表作为参数调用函数
print(lst)

在Python IDLE文件脚本窗口,在"Run"菜单中单击"Run Module"选项,则IDLE命令行窗口显示下面的结果:

code.python
>>> =RESTART: …/Samples/ch14/sam14-009.py
 [1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, [6, 7, 8, 9]]

可见,调用函数前后列表发生了变化,参数按照传址方式传递。

匿名函数

顾名思义,匿名函数就是没有显式命名的函数。它用更简洁的方式定义函数。

【MATLAB】

使用匿名函数,可以在不必创建M文件的情况下快速创建简单函数。可以在MATLAB命令行或任何M文件函数或脚本中创建匿名函数。

创建匿名函数的语法格式为:

code.matlab
fhandle=@(arglist) expr

其中,expr表示函数体,即函数要完成主要工作的代码;arglist为要传递给函数的逗号间隔的输入参数列表。

【Python】

Python中使用lambda关键字创建匿名函数,语法格式为:

code.python
fn=lambda [arg1 [,arg2, …, argn]]: 表达式

其中,lambda为关键字,在它后面声明参数,然后在冒号后面书写函数表达式。fn可作为函数的名称使用,调用格式为:

code.python
v=fn(arg1 [,arg2, …, argn])

下面在命令行中定义1个对2个数求积的匿名函数。

code.python
>>> rt=lambda a,b: a*b

该函数有2个参数a和b,函数表达式为a*b。

调用该函数,计算和输出给定数据的积:

code.python
>>> print(rt(2,5))
10