==== file: newCounter.m ==== function [incre] = newCounter(init) val = init; incre = @counter; function result = counter() val = val + 1; result = val; end end ==== command lines ==== >> f = newCounter(0); >> f() >> ans = 1 >> f() >> ans = 2 >> f() >> ans = 3 >> fInfo = functions(f); >> fInfo.workspace{1} >> ans = 包含以下字段的 struct: init: 0 incre: @newCounter/counter val: 3
我写matlab习惯写一堆匿名函数,一来是因为在运行脚本里不给定义函数,二来可以降低代码复写率,提高效率。虽然看起来还是挺丑的。前几天突发奇想,想看下matlab的闭包机制。由于之前写JS和Python,一上来理所当然的写出了下面的代码:
f = @(a) @(x) x + a; fCell = cell(10,1); for i = 1 : 10 fCell{i} = f(i); end ==== command lines ==== >> fCell{1}(5) >> 6 >> fCell{6}(5) >> 11
这样看是没什么问题,但是像下面这样写也是没问题的:
fCell = cell(10,1); for i = 1 : 10 fCell{i} = @(x) x + i; end ==== command lines ==== >> fCell{1}(5) >> 6 >> fCell{6}(5) >> 11
第二种是完全没有问题的,因为在封装这个匿名函数的时候,会将函数表达式内用到的外部变量封进一个workspace里,看下下面这个例子,以及函数信息:
i = 2; j = 3; f = @(x) x * i + j; f(10) i = 4; f(10) ==== command lines ==== >> ans = 23 >> ans = 23 >> fInfo = functions(f) >> fInfo = 包含以下字段的 struct: function: '@(x)x*i+j' type: 'anonymous' file: 'C:\Users\nizek\Desktop\matlab PRO\Untitled.m' workspace: {[1×1 struct]} within_file_path: 'Untitled' >> fInfo.workspace{1} >> ans = 包含以下字段的 struct: i: 2 j: 3
可以看到,在封装完函数之后,对外部值的改变并不会改变该匿名函数内部变量的值。而且也没办法改变(对自定义函数有办法改,见下面)。
下面用函数封装一个计数器,每次调用都会加一:
如果中途想改这个值了怎么办,可以做个hook传个函数句柄出来:
==== file: newCounter.m ==== function [incre, setter] = newCounter(init) val = init; %clear init; incre = @counter; setter = @setVal; function result = counter() val = val + 1; result = val; end function [] = setVal(n) val = n; end end ==== command lines ==== >> [f, setter] = newCounter(0); >> f() >> ans = 1 >> f() >> ans = 2 >> f() >> ans = 3 >> setter(100) >> f() >> ans = 101
当然这样看起来太丑了,美化一下:
==== file: newCounter.m ==== function [clss] = newCounter(varargin) try val = varargin{1}; catch val = 0; end %clear varargin; clss = struct('incre',@counter,'setter',@setVal); function result = counter() val = val + 1; result = val; end function [] = setVal(n) val = n; end end ==== command lines ==== >> a=newCounter(); >> a.incre() >> ans = 1 >> a.incre() >> ans = 2 >> a.setter(10) >> a.incre() >> ans = 11 >> a.incre() >> ans = 12
效率:
同样的环境下,创建10w个:
带struct的函数计数器: 3.97秒
不带struct的函数计数器: 2.45秒
封装的计数器类: 0.70秒
但是用起来倒是速度一样。
#总结
matlab里变量的作用域还是很严格的,封装匿名函数时直接把相关变量一起打包隔离workspace当你用匿名函数生成匿名函数时,甚至会隔多一层空的workspace。
当然如果实际中用的大项目计算量大的话,当然还是写一个class的运行效率高,不建议用写函数的方法来封装,而且抛弃struct的使用,速率也会更快。