在MATLAB中,是否可以在一个文件中定义多个函数,并从该文件外部访问它们?

226

我在攻读电气工程本科学位时,MATLAB要求每个函数都必须定义在自己的文件中,即使它只有一行代码。

现在我正在攻读研究生学位,需要使用MATLAB编写一个项目。在新版本的MATLAB中,这个规定是否仍然存在?

如果可以将多个函数放入同一个文件中,那么是否有任何限制?例如,文件中的所有函数是否都可以从文件外部访问,还是只有与文件同名的函数可被访问?

注:我正在使用MATLAB R2007b版本。

9个回答

279
当调用m文件时,m文件中的第一个函数(即主函数)将被调用。主函数不一定需要与m文件同名,但为了清晰起见,最好这样做。当函数名和文件名不同时,必须使用文件名来调用主函数。
在m文件中的所有随后的函数称为本地函数(或旧术语中的“子函数”),只能由主函数和该m文件中的其他本地函数调用。其他m文件中的函数无法调用它们。从R2016b开始,你也可以将本地函数添加到脚本中,尽管作用域行为仍然相同(即它们只能从脚本内部调用)。
此外,你还可以在其他函数内部声明函数。这些称为嵌套函数,只能从它们所嵌套的函数内部调用。它们还可以访问嵌套它们的函数中的变量,这使它们非常有用,尽管稍微棘手。
更多思考的食粮...
上述常规函数作用域行为有一些变通方法,例如像SCFrenchJonas的答案中提到的将函数句柄作为输出参数传递(从R2013b开始,这可以通过localfunctions函数实现)。然而,我不建议养成使用此类技巧的习惯,因为可能有更好的选项来组织您的函数和文件。
例如,假设您在一个m文件A.m中有一个主函数A,以及本地函数DEF。现在假设您还有两个其他相关的函数BC,分别在m文件B.mC.m中,您也希望能够调用DEF。这里有一些选项可供选择:
  • Put D, E, and F each in their own separate m-files, allowing any other function to call them. The downside is that the scope of these functions is large and isn't restricted to just A, B, and C, but the upside is that this is quite simple.

  • Create a defineMyFunctions m-file (like in Jonas' example) with D, E, and F as local functions and a main function that simply returns function handles to them. This allows you to keep D, E, and F in the same file, but it doesn't do anything regarding the scope of these functions since any function that can call defineMyFunctions can invoke them. You also then have to worry about passing the function handles around as arguments to make sure you have them where you need them.

  • Copy D, E and F into B.m and C.m as local functions. This limits the scope of their usage to just A, B, and C, but makes updating and maintenance of your code a nightmare because you have three copies of the same code in different places.

  • Use private functions! If you have A, B, and C in the same directory, you can create a subdirectory called private and place D, E, and F in there, each as a separate m-file. This limits their scope so they can only be called by functions in the directory immediately above (i.e. A, B, and C) and keeps them together in the same place (but still different m-files):

    myDirectory/
        A.m
        B.m
        C.m
        private/
            D.m
            E.m
            F.m
    

所有这些都有点超出了您的问题范围,可能比您需要的细节还要多,但我认为涉及到组织所有m文件的更一般关注点可能是有好处的。 ;)


3
最喜欢的答案选项看起来像这样 ^,@idigas - embert
1
@embert 我认为他的意思是类似于收藏一个问题,这可以独立于点赞而进行。 - OJFord

83

通常来说,对于你的问题,答案是否定的,你不能在一个文件中定义多于一个外部可见的函数。不过,你可以返回对于本地函数的函数句柄,并且一个方便的方法是将其作为结构体字段。以下是一个示例:

function funs = makefuns
  funs.fun1=@fun1;
  funs.fun2=@fun2;
end

function y=fun1(x)
  y=x;
end

function z=fun2
  z=1;
end

这里是它的使用方法:

>> myfuns = makefuns;
>> myfuns.fun1(5)    
ans =
     5
>> myfuns.fun2()     
ans =
     1

38
在同一文件中拥有多个可单独访问的函数的唯一方法是使用静态方法来定义面向对象编程。你可以通过myClass.static1(), myClass.static2()等方式访问这些函数。
自R2008a以来,仅支持OOP功能,因此除非您想使用旧的、未记录的OOP语法,否则这对您来说是不行的,正如@gnovice所解释的那样。 编辑 在一个文件中定义多个外部可访问的函数的另一种方法是创建一个返回多个函数句柄的函数。换句话说,您可以调用您的定义函数作为[fun1,fun2,fun3]=defineMyFunctions,然后您可以使用out1=fun1(inputs)等。

对于这个目的我不会使用面向对象编程,它会增加相当大的开销,尤其是对于静态方法。(https://dev59.com/ynI-5IYBdhLWcg3wwLS3) - Daniel
1
@Daniel:只有在进行大量函数调用并且方法中的计算几乎瞬间完成时,才会注意到开销。这两个条件通常指向不良设计-没有矢量化和没有意义的函数。因此,我不会太担心。 - Jonas

24

我非常喜欢SCFrench的回答 - 我想要指出,可以使用assignin函数将这些函数直接导入工作区进行简单修改。(这样做让我想起了Python中“从y中导入x”的做法)

function message = makefuns
  assignin('base','fun1',@fun1);
  assignin('base','fun2',@fun2);
  message='Done importing functions to workspace';
end

function y=fun1(x)
  y=x;
end

function z=fun2
  z=1;
end

然后像这样使用:

>> makefuns
ans =
Done importing functions to workspace

>> fun1(123)
ans =
   123

>> fun2()
ans =
     1

1
assignin('caller',...) 更为准确。您可能希望在另一个函数中使用这些函数。 - Cris Luengo

12

与SCFrench的回答类似,但采用更符合C#风格的方式。

我会(并经常这样做)创建一个包含多个静态方法的类。例如:

classdef Statistics

    methods(Static)
        function val = MyMean(data)
            val = mean(data);
        end

        function val = MyStd(data)
            val = std(data);
        end
    end

end
作为静态方法,您不需要实例化该类。 调用函数的方式如下:
data = 1:10;

mean = Statistics.MyMean(data);
std = Statistics.MyStd(data);     

4

我使用Octave在一个.m文件中定义多个函数,然后从需要使用该文件中的函数的位置内部使用命令:

source("mycode.m");

不确定Matlab是否支持此功能。
octave:8> help source
'source' is a built-in function

 -- Built-in Function:  source (FILE)
     Parse and execute the contents of FILE.  This is equivalent to
     executing commands from a script file, but without requiring the
     file to be named `FILE.m'.

1
不好意思,Matlab中没有source命令 :( - Martin Pecka

3
你也可以将函数分组到一个主文件中,主函数的示例如下:
function [varargout] = main( subfun, varargin )
[varargout{1:nargout}] = feval( subfun, varargin{:} ); 

% paste your subfunctions below ....
function str=subfun1
str='hello'

那么调用subfun1的代码看起来像这样: str=main('subfun1')

0

截至R2017b版本,官方并不支持这种操作。相关文档中指出:

程序文件可以包含多个函数。如果文件只包含函数定义,则第一个函数是主函数,并且是MATLAB与文件名关联的函数。在主函数或脚本代码之后的函数称为本地函数。本地函数仅在文件内部可用。

然而,其他答案中提出的解决方法可以实现类似的功能。


这并不完全是Gnovice在他的回答开头所说的吗? - Adiel
@Adiel 或许吧,但是自那个回答以来已经过去了几年,有人可能会想知道是否有任何变化。 - Dev-iL
我还不确定是否有任何变化...? :) - Adiel
没有。除了可能添加一些文档来解决这个特定的问题之外,没有其他的东西。 - Dev-iL
我写这篇答案的原因是因为几个版本之前,他们引入了可以添加到脚本末尾的函数 - 因此人们可能会想知道在这方面是否有任何变化(答案:没有)。 - Dev-iL

-2

我已经尝试使用Octave进行SCFRenchRu Hasha测试。

最终它可以运行了:但是我做了一些修改。

function message = makefuns
    assignin('base','fun1', @fun1);   % Ru Hasha
    assignin('base', 'fun2', @fun2);  % Ru Hasha
    message.fun1=@fun1;               % SCFrench
    message.fun2=@fun2;               % SCFrench
end

function y=fun1(x)
    y=x;
end

function z=fun2
    z=1;
end

可以在其他的'm'文件中调用:

printf("%d\n", makefuns.fun1(123));
printf("%d\n", makefuns.fun2());

更新:

我添加了一个答案,因为在Octave中,无论是+72还是+20都不能正常工作。 我编写的代码完美地运行(上周五我测试过并在稍后写了这篇文章)。


4
如果你能解释这个与你从中复制的两个现有答案不同的地方,我会取消我的踩。抱歉之前没有评论。我只是不明白它们之间有什么不同,除了你将两种方法合并成一个函数,因此做了一些多余的事情。另外,请插入正确的链接到你引用的答案中,“+72”和“+20”相当晦涩,我花了一段时间才意识到你在引用投票计数,这会随时间而变化,使你的参考不可理解。 - Cris Luengo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接