==== 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的使用,速率也会更快。