找回密码
 注册
Simdroid-非首页
查看: 118|回复: 10

分享Loren关于匿名函数高级用法的博客

[复制链接]
发表于 2014-1-21 15:24:45 | 显示全部楼层 |阅读模式 来自 北京
本帖最后由 rocwoods 于 2014-1-21 15:29 编辑

http://blogs.mathworks.com/loren ... s-functions-part-1/
http://blogs.mathworks.com/loren ... s-functions-part-2/
http://blogs.mathworks.com/loren ... s-functions-part-3/
查找资料时还发现了中文翻译注解版,原帖链接:
http://anony3721.blog.163.com/blog/static/511974201331402843580/
对翻译并注解的作者表示感谢!,方便大家阅读贴出来:

阿英讲matlab匿名函数传入函数指针,varargin  

2013-04-14 12:36:52|  分类: 编程 |  标签:matlab  匿名函数  |举报|字号 订阅





即以此功德,庄严佛净土。上报四重恩,下济三途苦。惟愿见闻者,悉发菩提心。在世富贵全,往生极乐国。

缘起:这篇文章是matlab博客里叫Loren的一个作者写的,里面讲了匿名函数的高级用法,写的很不错,看了很受益,所以贴出来希望对大家有帮助。文章和代码是前天花了一晚上,昨天花了一上午调试,注释的。由于是高级编程技巧,在没有学佛以前我是很自私的,大家也不可能看到这篇文章;目前,大家能看到的原因是本人有幸听闻佛陀正法,我秉承佛菩萨,自利利他,自觉觉他,心量广大,无贪嗔痴,上求佛道,下化众生的的宗旨指导自己的行为,言语,思想。   学佛,就是要比别人强,比别人更懂得感恩,比别人更珍惜时间,比别人的生命更有价值,比别人心量大,比别人有智慧,比别人人品高尚,比别人早觉悟,南无阿弥陀佛!
  1. %匿名函数的优点在于使用方便,不需要保存成一个单独的文件。
  2. %对于一些只使用一次,并且结构简单的函数来说,
  3. %这样做可以避免整个工程的文件系统过于混乱。

  4. %% 最大最小值函数
  5. % 我们来写一个函数,找到数组中的最大和最小值并且将其存在另一个数组里。如下,
  6. % ------- 我以前不知道如何用匿名函数返回两个以上的返回值 ----------
  7. min_and_max = @(x) [min(x), max(x)];
  8. min_and_max([3 4 1 6 2])
  9. min_and_max = @(x) {min(x), max(x)}; % another version
  10. celldisp(min_and_max([3 4 1 6 2])) % min_and_max后面依次执行2个函数指针,类似c#委托后面带了2个回调函数
  11. % 这个函数很简单。我们再写一个复杂的,让函数不但返回最大最小值,而且要返回这些值的位置。
  12. % 这样,我们的匿名函数就得有两个输出,用下面的形式可以实现这个功能
  13. % ----- 下面cellfun中的f是个函数指针(函数句柄), cellfun完成函数指针的调用 -------
  14. [extrema, indices] = cellfun(@(f) f([3 4 1 6 2]), {@min, @max})
  15. % 这个函数看起来很奇怪,首先,我们调用了cellfun这个函数。第一个参数是一个函数句柄,
  16. % 第二个参数是任何一个cell。这里,第二个参数的cell里面是两个函数句柄,原来cellfun还可以这样用!
  17. % 这样,第二个输入中的函数句柄会一次传递给第一个输入中的f,从而实现多个输出。
  18. % 我们可以把上面那个句子中的 f([3 4 1 6 2])替换成f(x),这样,这个min_and_max就成了一个可以使用的函数
  19. % ----- cellfun的第一个参数是个函数指针,下面匿名函数@(f) f(x)就是函数指针,这个匿名函数的参数也是函数指针-------
  20. % ---- 我以前不会这样将函数名作为参数的函数调用,C#中的delegate就是以函数名为参数的 -------------------------
  21. min_and_max = @(x) cellfun(@(f) f(x), {@min, @max});
  22. % 我们可以使用这个函数来求极值
  23. y = randi(10, 1, 10)
  24. just_values = min_and_max(y)
  25. [~, just_indices] = min_and_max(y)
  26. [extrema, indices] = min_and_max(y)

  27. %% 映射
  28. % 我们定义val为一个cell数组,里面包含了我们的输入数据,fcns是一系列函数句柄
  29. % ------ 我以前没想到传递函数指针给匿名函数@(f) f(val{:})是这么爽 -------
  30. map = @(val, fcns) cellfun(@(f) f(val{:}), fcns); % cellfun以函数指针为参数时有点像C#中的委托delegate
  31. % 这个写法有什么用呢?我们可以通过这种方式改写一下上面的min_and_max函数,让它变得更短一些。我们现在要将x通过max和min函数进行映射
  32. x = [3 4 1 6 2];
  33. [extrema, indices] = map({x}, {@min, @max})
  34. map({1, 2}, {@plus, @minus, @times})
  35. % --- 下面的例子的目的:给函数传入数据的同时传入操作这些数据的函数指针,真是爽 -------------
  36. mapc = @(val, fcns) cellfun(@(f) f(val{:}), fcns, 'UniformOutput', false);
  37. % 我们做个实验,将pi送到多个函数里面,输出完全不兼容的结果,包括数字和字符串
  38. mapc({pi}, {@(x) 2 * x, ...                     % Multiply by 2
  39.             @cos, ...                           % Find cosine
  40.             @(x) sprintf('x is %.5f...', x)})   % Return a string
  41. % 用了这个mapc,一个顶过去N个,一行代码搞定很多问题,简洁。

  42. %% 内嵌条件语句
  43. % 有时候我们也许想在匿名函数里面使用if else之类的语句。然而,普通的Matlab语法并不允许在匿名函数中写入这样的句子。
  44. % 为了解决这一问题,我们单独写一个“inline if”这样的内嵌条件函数,
  45. iif = @(varargin) varargin{2 * find([varargin{1:2:end}], 1, 'first')}(); %是一个很好用的条件判断函数
  46.         % I = find(X,K) returns at most the first K indices corresponding to
  47.         %    the nonzero entries of the array X.  K must be a positive integer
  48.         % I = find(X,K,'first') is the same as I = find(X,K).
  49. % 这个函数看起来就更难以理解了,在理解它之前,先看看它有多好用,这样就有理解的动力了。
  50. %  [out1, out2, ...] = iif( if this,      then run this, ...
  51. %                             else if this, then run this, ...
  52. %                             ...
  53. %                             else,         then run this );
  54. %
  55. % "if this" 条件中,第一个取true的条件,其后面的 "then run this" 的句子 就会被执行,其他的都不会被执行。不管写多少行都没问题。
  56. %
  57. % 我们可以用这个函数来做一个安全的归一化功能,如下
  58. % 如果不是所有x都为有限值,则报错
  59. % 否则,如果所有x都为0,则返回0
  60. % 否则,返回 x/norm(x).

  61. % 下面就是这个功能的写法。注意,每个动作语句前面都加上了 @(). 这是为了将一个语句变成一个匿名函数的句柄,
  62. % 这样才可以正确的输入到 iif 函数里面。下面的philosophy是对数据进行判断,然后进行相应的操作,其中@() x/norm(x)都是匿名函数句柄
  63. normalize = @(x) iif( ~all(isfinite(x)), @() error('Must be finite!'), ...
  64.                       all(x == 0),       @() zeros(size(x)), ...
  65.                       true,              @() x/norm(x) );
  66. %测试一下
  67. normalize([1 1 0])
  68. % 如果有inf值呢,
  69. try
  70.     normalize([0 inf 2]), catch err, disp(err.message);
  71. end
  72. % 全0的情况
  73. normalize([0 0 0])

  74. % 虽然这个函数完全不必要用匿名函数这样写,但是这个例子说明了 iif 这个函数映射的作用还是很强大的。我们下面看一下它的原理,
  75. %
  76. % 首先,iif 函数通过varargin接受任意数量的输入. 这些输入将会被解析为条件,行为;条件,行为;……这样的形式。
  77. % 然后,iif 选择了所有的条件,也就是varargin中奇数位置的那些项,进行判断,对于我们上面的例子,取了这些条件 [~all(isfinite(x)), all(x == 0), true]
  78. % 下一步,它找到了第一个为true的条件的位置,例如, if~all(isfinite(x)) 是 false, 但是 all(x == 0) 是 true, 所以位置是2
  79. % 最后,我们通过位置定位到相应的动作语句,然后执行。由于我们把语句定义成了函数句柄,所以在后面家一个()就可以执行了。也就是
  80. % varargin{...}()
  81. %清楚了吧

  82. %% 匿名函数递归
  83. % 由于递归函数是调用自身的函数,那么我们就需要一种能够让函数引用自己的办法。
  84. % 然而,当我们写匿名函数的时候,它并没有函数名,我们如何来调用自己呢?
  85. % 先看一个简单的菲波纳契数列的例子。菲波纳契数列从1,1开始,之后每一个数等于前面两个数之和。
  86. % 用计算机实现这个是再简单不过的。我们试试下面这种递归方式
  87. % fib = @(n) iif(n <= 2,    1, ...                    % First two numbers
  88. %                   true,   @() fib(n-1) + fib(n-2)); % All later numbers
  89. % 不对,我们还没有定义fib,怎么就在里面引用了?这种方式肯定是不行的,
  90. % 我们不能直接调用fib,得使用另一个输入,也就是调用一个函数句柄 f!
  91. %---- 这个例子说明同时传入函数句柄和数据参数的妙用 -----------
  92. fib = @(f, n) iif(n <= 2, 1, ...                      % First two numbers
  93.                   true,   @() f(f, n-1) + f(f, n-2)); % All later numbers
  94. % 现在,我们将 fib句柄以及一个数字传入这个fib函数,它将自身调用两次,从而实现递归(递归是"回溯"+"递推")。
  95. % 最终,我们的答案得到了
  96. fib(fib, 6)
  97. % 这样,我们实现了这个功能。但是这个函数看起来很别扭。我们需要将自己作为参数传给自己?
  98. % 写起来就很麻烦。我们来写另一个简单一点的函数,可以帮我们做这个工作。我们之需要指定n,它可以自己调用上面的语句,这样看起来就舒服多了
  99. fib2 = @(n) fib(fib, n); % Facade(外观模式):为子系统的一组接口提供一个一致的界面。定义一个高层的接口,使得这个子系统更加容易使用,我们的子模块一般都是这么做的。
  100. fib2(4)
  101. fib2(5)
  102. fib2(6)
  103. % 从上面这个例子我们大概知道了匿名函数是怎么递归的,我们现在写一个通用点儿的函数 recur 来将一个函数句柄和其他参数传给自己。
  104. recur = @(g, varargin) g(g, varargin{:});
  105. % 这个函数的实现原理和上一篇提到的iif类似。如果用这个函数实现那个数列的功能,我们可以这样写,f是函数句柄
  106. fib = @(n) recur(@(f, k) iif(k <= 2, 1, ...  % 递归出口
  107.                            true, @() f(f, k-1) + f(f, k-2)), ... % 不断递归调用
  108.                             n); % 这里n就是recur中的varargin,递归调用后n传给k
  109. % 这里面,
  110. % @(f, k) iif(k <= 2, 1, ...
  111. %                  true, @() f(f, k-1) + f(f, k-2))
  112. % 对应的是recur函数定义中的 函数句柄 g,这里将g定义成了匿名的菲波纳契函数。通过使用arrayfun,我们可以直接获得数列的前n项
  113. arrayfun(fib, 1:10)
  114. % 另一个例子,级数,也可以类似的实现。 (f(n) = 1 * 2 * 3 * ... n), 注意recur是自己调用自己!!!
  115. factorial = @(n) recur(@(f, k) iif(k == 0, 1, ... % 递归出口
  116.                                    true,   @() k * f(f, k-1)), ...
  117.                                            n); % 这里n就是recur中的varargin,递归调用后n传给k
  118. arrayfun(factorial, 1:7)
  119. % 为什么要在匿名函数里进行这么复杂的递归操作呢?首先,类似我们前面提的映射,以及iif,recur函数,
  120. % 可以很好的帮我们理解匿名函数的工作原理。另外,递归可以帮助我们在匿名函数里实现循环,这一点是最重要的。
  121. % 这个我们将在下一篇里介绍。这里,我们首先要介绍另一个函数,来帮助我们在匿名函数中执行多条语句。

  122. %% 辅助函数
  123. % 这个小函数在很多地方都会很有用,我们以后会经常用到 curly . 注意下面这两个函数是有区别的,
  124. paren = @(x, varargin) x(varargin{:});
  125. curly = @(x, varargin) x{varargin{:}};
  126. % 他们让我们可以通过 paren(x, 3, 4)这样的方式来实现x(3, 4) 的功能, 如paren(@plus, 3, 4)。对于 curly ,花括号,也类似。
  127. % 也就是说,圆括号和花括号在这里被我们定义成了函数。有什么用呢?比如,我们要写一个函数返回屏幕的宽度和高度,
  128. x = get(0, 'ScreenSize')
  129. % 然而,我们不需要前面两个输出,因此我们可以写x(3:4)。但是如果在匿名函数中调用呢,我们没法把输出保存到一个变量里面,
  130. % 那怎么从函数的调用部分直接获得部分输出?其中一种实现方式就是使用 paren 和 curly 函数,这两个函数也和别的语言中实现这一功能语法相似
  131. %
  132. % 我们写这样一个函数
  133. screen_size = @() paren(get(0, 'ScreenSize'), 3:4);
  134. screen_size()
  135. % 这样,我们就可以随意索引函数的输出了,例如
  136. magic(3)
  137. paren(magic(3), 1:2, 2:3)
  138. paren(magic(3), 1:2, :)

  139. % 对于花括号,也是类似的做法。比如下面这个例子,正则表达式会匹配rain和Spain,但是我们只取第二个输出,
  140. spain = curly(regexp('The rain in Spain....', '\s(\S+ain)', 'tokens'), 2)
  141. % 直接传递冒号也可以,但是在curly函数中,直接传递冒号的时候要加上单引号
  142. [a, b] = curly({'the_letter_a', 'the_letter_b'}, ':')
  143. tmp = {'the_letter_a', 'the_letter_b'}; tmp{:}
  144. %% 执行多条语句
  145. % 除了curly, 我们来看看下面一些不同的实现方式. 比如下面这个:
  146. do_three_things = @() {fprintf('This is the first thing.\n'), ...
  147.                        fprintf('This is the second thing.\n'), ...
  148.                        max(eig(magic(3)))};
  149. do_three_things()  % 没有参数的函数句柄do_three_things后面依次执行三个函数指针,类似委托后面带了3个回调函数
  150. % 我们通过一行语句执行了三条命令,所有的输出都被存在一个cell数组里面。
  151. % 前两个输出都是fprintf产生的垃圾,我们希望得到最后一个输出,15,这才是我们要的最大特征值。所以,我们可以借助curly,只取得最后一个值。
  152. do_three_things = @() curly({fprintf('This is the first thing.\n'), ...
  153.                              fprintf('This is the second thing.\n'), ...
  154.                              max(eig(magic(3)))}, 3);
  155. do_three_things()
  156. % %一个复杂一点的例子,例如我们想写一个函数实现下面的功能
  157. %
  158. % 在屏幕中间现实一张小图像
  159. % 在图像上画一些随机的点
  160. % 返回图像窗口和内容的句柄
  161. % 我们可以通过将所有返回值存在一个cell里面,然后用curly来索引我们想要的结果。这及行代码可以通过一个匿名函数实现
  162. dots = @() curly({...   % {} 中括起来的是函数一系列函数指针,或者一系列无返回值的procedure
  163.     figure('Position', [0.5*screen_size() - [100 50], 200, 100], ...
  164.            'MenuBar',  'none'), ...                % Position the figure (the first thing)
  165.     plot(randn(1, 100), randn(1, 100), '.')}, ...  % Plot random points (the second thing)
  166.     ':');                                          % Return everything

  167. [h_figure, h_dots] = dots()
  168. %% 循环
  169. % 我们上一篇用递归来实现的功能,也可以用一个for循环来实现,例如对于求n的级数,
  170. % factorial = 1;
  171. % for k = 1:n
  172. %    factorial = k * factorial;
  173. % end
  174. % 一般来说,递归都可以使用循环的迭代来实现。然而,在匿名函数中,我们无法使用for或者while这样的语句,因此,我们得考虑如何将循环反转为递归实现

  175. %% 循环与递归
  176. % 要写一个好的循环语句,我们必须知道下面的条件
  177. %
  178. % 1.每次循环中要做什么
  179. % 2.这个过程是否还需要进行下一次的循环
  180. % 3.循环的起始条件是什么
  181. %
  182. % 如果我们将“要做什么”定义成一个函数 fcn(x) , 将“是否该继续”定义成另一个函数 cont(x), 然后将“起始条件”定义成一个 x0。
  183. % 这样我们就可以写一个循环函数了。
  184. %
  185. % 我们说的再详细一点,在每一步,我们调用cont函数,传入状态x的所有元素,如 cont(x{:})。如果结果返回false, 我们不继续,
  186. % 返回当前状态x。否则,我们调用 fcn 函数,传入状态 fcn(x{:}),然后将所有的输出传入下一次迭代。
  187. %
  188. % 将上一段说的这一次循环定义为一个函数 f, 那么我们就可以使用我们的 recur 函数来定义一个匿名的 loop 函数
  189. % ---- 我以前没看出来下面 数据x0, 函数句柄condition 和 函数句柄fcn 都是传入函数句柄recur的参数 -------
  190. % --- iif(~condition(x{:}), x,@() f(f, fcn(x{:}))) 就是recur中的函数句柄f , 注意recur是自己调用自己!!!----
  191. loop = @(x0, condition, fcn) ...                                             % Header
  192.        recur(@(f, x) iif(~condition(x{:}), x, ...                            % 当 k = n时, ~(k < n)为真, 递归出口
  193.                          true,        @() f(f, fcn(x{:}))), ...              %   recursive invocation
  194.              x0); % 这里x0就是recur中的varargin,递归调用后x0传给x           % from x0.
  195. % 整个loop函数句柄就是给recur提供一个初值x0; 而整个recur又是给iif提供一个初值x; iif 条件后的都是函数句柄
  196. % 对上面这个简单的例子来说,状态x就是一个循环计数。我们增加计数直到其大于等于n,然后返回最终的循环次数。
  197. % 因此,上面这个函数做的就是从0数到了n。虽然不是很有意思,但是它展示了循环如何来实现
  198. count = @(n) loop({0}, ...          % Initialize state, k, to 0
  199.                   @(k) k < n, ...   % While k < n
  200.                   @(k) {k + 1});    %   k = k + 1 (returned as cell array)

  201. arrayfun(count, 1:10)
  202. % 看到我们写{0},为什么我们要使用cell来存储状态呢?有两个原因,第一,如果x是一个cell,那么我们将x传入fcn的时候,
  203. % 就可以实现传递多参数。即,fcn(x{:}) 等同于 fcn(x{1}, x{2}, ...)。
  204. % 第二,这样做就允许了函数返回多个元素,供下一次迭代使用。多个元素将会通过一个cell来返回。
  205. % 例如下面这个级数的例子,我们的状态是两个部分,迭代次数k和上一次计算出的级数x。这两个数字都是我们的输入,
  206. factorial = @(n) loop({1, 1}, ...          % Start with k = 1 and x = 1
  207.                       @(k, x) k <= n, ...  % While k <= n
  208.                       @(k, x) {k + 1, ...  %   k = k + 1;
  209.                                k * x});    %   x = k * x;
  210. factorial(5)
  211. % 函数的返回也是两个,迭代次数和最终的结果。然而,我们可能不想要第一个输出,因为没什么用,那么我们修改一下loop程序,
  212. % 在其中增加了一个 cleanup 函数,当循环执行完,会执行这个函数来去掉不用的输出
  213. loop = @(x0, cont, fcn, cleanup) ...                            % Header
  214.        recur(@(f, x) iif(~cont(x{:}), cleanup(x{:}), ...        % Continue?
  215.                          true,        @() f(f, fcn(x{:}))), ... %   Iterate
  216.              x0);
  217. % 用新的loop程序实现一下级数功能
  218. factorial = @(n) loop({1, 1}, ...         % Start with k = 1 and x = 1
  219.                       @(k,x) k <= n, ...  % While k <= n
  220.                       @(k,x) {k + 1, ...  %   k = k + 1;
  221.                               k * x}, ... %   x = k * x;
  222.                       @(k,x) x);          % End, returning x, 此行对应cleanup

  223. factorial(5)
  224. % 我们可以通过arrayfun来直接获得多个输入的输出
  225. arrayfun(factorial, 1:7)

  226. % 看起来不错。
  227. % 不过,必须得承认,这种循环要比用普通的for循环实现来的复杂的多。但是另一方面,这是通过匿名函数实现的,
  228. % 它在语法上的复杂带来的好处是它具有自己的数据区域,并不会改变循环外任何的变量。
  229. % 最重要的,也是通过这个例子学习一下匿名函数的高级使用方法。
  230. % 最终看一个例子,总结一下这三篇帖子
  231. % 我们来模拟一个谐振过程。使用一个结构体存储异类状态,包括谐振的完整历史记录。这个可以用来模拟,
  232. % 例如地震时挂在房顶的吊灯摇摆的幅度

  233. % 首先,计算一个状态转移矩阵,用来表示一个有阻尼谐振过程。
  234. % 给其乘以状态|x|,就得到了x在下一时刻的取值。
  235. Phi = expm(0.5*[0 1; -1 -0.2]);

  236. % 创建循环
  237. x   = loop({[1; 0], 1}, ...                  % Initial state, x = [1; 0]
  238.            @(x,k) k <= 100, ...              % While k <= 100
  239.            @(x,k) {[x, Phi * x(:, end)], ... %   Update x
  240.                    k + 1}, ...               %   Update k
  241.            @(x,k) x);                        % End, return x

  242. % 创建画图函数
  243. plot_it = @(n, x, y, t) {subplot(2, 1, n), ...            % Select subplot.
  244.                          plot(x(n, :)), ...               % Plot the data.
  245.                          iif(nargin==4, @() title(t), ... % If there's a
  246.                              true,      []), ...          % title, add it.
  247.                          ylabel(y), ...                   % Label y
  248.                          xlabel('Time (s)')};             % and x axes.

  249. % 绘制结果
  250. plot_it(1, x, 'Position (m)', 'Harmonic Oscillator');
  251. plot_it(2, x, 'Velocity (m/s)');
复制代码



评分

2

查看全部评分

发表于 2014-1-21 18:36:00 | 显示全部楼层 来自 新疆乌鲁木齐
Simdroid开发平台
本帖最后由 bainhome 于 2014-1-21 18:40 编辑

Loren的blog值得大家读读,这自然是极好的...要是把翻译人家Loren文章,然后就在开头各种吹牛逼的话去掉,加上“有删节改动”,就更好了!:lol
写完了又有点担心,万一这是吴总自己化名普度众生,我不是太唐突了些?:Q

点评

我还没有遁入佛门呢,没那么高的觉悟  发表于 2014-1-22 11:23
回复 不支持

使用道具 举报

发表于 2014-1-22 04:13:35 | 显示全部楼层 来自 英国
本帖最后由 nwcwww 于 2014-1-22 04:44 编辑

loren和steve最近的blog好像拉壮丁做guest blogger反而比自己写的多。还是cleve moler老先生最勤快。:lol

这几篇是Tucker写的,去年我也拜读过。Matlab里面没有iff()确实挺难受,这里的提法是个挺好的补充,也很有想法。文中的许多思路更是值得大家学习。不过吹毛求疵的讲,是否炫技意味大于其实用性了?我记得bruno luong对此的评价是语法丑陋,效率亦低。我多多少少也有同感。




学佛,就是要比别人强,比别人更懂得感恩,比别人更珍惜时间,比别人的生命更有价值,比别人心量大,比别人有智慧,比别人人品高尚,比别人早觉悟,南无阿弥陀佛!

看来学佛啥都好,就是没学会谦虚。

点评

感觉翻译作者刚开始学佛,还没深入。  发表于 2014-1-22 11:25
回复 不支持

使用道具 举报

 楼主| 发表于 2014-1-22 11:22:05 | 显示全部楼层 来自 北京
nwcwww 发表于 2014-1-22 04:13
loren和steve最近的blog好像拉壮丁做guest blogger反而比自己写的多。还是cleve moler老先生最勤快。

...

嗯,培养灵活应用性,拓宽思路有好处。但是单就上面匿名函数的用法而言,的确炫技成分更多。实用的话效率肯定低,尤其是匿名函数递归调用,效率肯定低得难以想象。

评分

1

查看全部评分

回复 不支持

使用道具 举报

发表于 2014-1-27 14:19:59 | 显示全部楼层 来自 河北廊坊
在C#里面也有匿名函数,不过那个匿名函数可以在里面写表达式的
Matlab的匿名函数和C#有些相像的
这篇文章估计一般人难以一下子看明白,最好懂的方式还是通过例子来解释
吴兄,还记得之前写的分离块的处理函数blockproc函数的那篇帖子吗,基本上没有人回应
回复 不支持

使用道具 举报

发表于 2014-1-27 14:51:07 | 显示全部楼层 来自 河北廊坊
吾兄悟到了Matlab之于C#的“同构”之处,我也写一点自己的见解,时间有限,先写一点,后面的慢慢补上
1. 最简单的min_and_max返回数组的写法类似于C#下面的代码
  1.     class Program
  2.     {
  3.         delegate double[] FunctionHandle(double[] x);
  4.         static void Main(string[] args)
  5.         {
  6.             // 类似于Matlab min_and_max = @(x)[max(x),min(x)];
  7.             // 由于Matlab不需要声明类型,因此FunctionHandle 不需要写
  8.             // @符号相当于delegate关键字
  9.             // (double[] x)相当于(x)
  10.             // {...}中的内容相当于[...]
  11.             FunctionHandle min_and_max = delegate(double[] x)
  12.             {
  13.                 double[] res = new double[2];
  14.                 res[0] = x.Max();
  15.                 res[1] = x.Min();
  16.                 return res;
  17.             };

  18.             double[] d = new double[]{3,4,1,6,2};

  19.             double[] res1 = min_and_max(d);

  20.             Console.WriteLine("最大值={0}", res1[0]);
  21.             Console.WriteLine("最小值={0}", res1[1]);
  22.         }
  23.     }
复制代码

点评

赞!不过不是我悟的,哈哈,C#我不懂,只能鼓掌吆喝了!  发表于 2014-1-29 15:12

评分

1

查看全部评分

回复 不支持

使用道具 举报

发表于 2014-1-28 09:33:35 | 显示全部楼层 来自 河北廊坊
本帖最后由 qibbxxt 于 2014-1-28 09:37 编辑

2. 实现 min_and_max = @(x) cellfun(@(f) f(x), {@min, @max});
  1. class Program
  2. {
  3. // 1.定义min_and_max委托,输入数组,返回可变数组
  4. // min_and_max = @(x) cellfun(@(f) f(x), {@min, @max});
  5. public delegate double[][] min_and_max(double[] x);
  6. // 2.定义min,max函数,输入数组,输出数组
  7. public delegate double[] minmax(double[] y);
  8. // 3.定义@(f)f(x),输入委托,输出数组
  9. public delegate double[] funcF(minmax f,double[] z);


  10. static void Main(string[] args)
  11. {
  12. min_and_max myminmax = delegate(double[] x)
  13. {
  14. // @min,亦可以用Func
  15. minmax minf = delegate(double[] x1)
  16. {
  17. double[] dres = new double[2];
  18. dres[0] = x1.Min();
  19. dres[1] = x1.ToList().FindIndex(item => item == dres[0]);
  20. return dres;
  21. };

  22. // @max
  23. minmax maxf = delegate(double[] x2)
  24. {
  25. double[] dres = new double[2];
  26. dres[0] = x2.Max();
  27. dres[1] = x2.ToList().FindIndex(item => item == dres[0]);
  28. return dres;
  29. };
  30. // @(f)f(x)
  31. funcF myfunc = delegate(minmax f, double[] z)
  32. {
  33. double[] dres = new double[2];
  34. dres = f(z);
  35. return dres;
  36. };
  37. // 定义返回值
  38. double[][] res = new double[2][];
  39. // 定义委托链
  40. minmax myf = minf;
  41. myf += maxf;
  42. // 实现cellfun
  43. for (int i = 0; i < myf.GetInvocationList().Length; i++)
  44. {
  45. Delegate d = myf.GetInvocationList()[i];
  46. object rr = d.DynamicInvoke(x);
  47. res[i] = (double[])rr;
  48. }
  49. return res;
  50. };

  51. double[] data = new double[] { 3, 4, 1, 6, 2 };

  52. double[][] res1 = myminmax(data);

  53. Console.WriteLine("最小值={0},最小值位置{1}", res1[0][0],res1[0][1]);
  54. Console.WriteLine("最大值={0},最大值位置{1}", res1[1][0], res1[1][1]);
  55. }

  56. }
复制代码
回复 不支持

使用道具 举报

发表于 2014-1-28 09:49:20 | 显示全部楼层 来自 河北廊坊
为了和Matlab的代码保持一致性,稍加修改
  1. class Program
  2. {
  3. // 1.定义min_and_max委托,输入数组,返回可变数组
  4. // min_and_max = @(x) cellfun(@(f) f(x), {@min, @max});
  5. public delegate double[][] min_and_max(double[] x);
  6. // 2.定义min,max函数,输入数组,输出数组
  7. public delegate double[] minmax(double[] y);
  8. // 3.定义@(f)f(x),输入委托,输出数组
  9. public delegate double[] funcF(minmax f);


  10. static void Main(string[] args)
  11. {
  12. min_and_max myminmax = delegate(double[] x)
  13. {
  14. // @min,亦可以用Func来实现
  15. minmax minf = delegate(double[] x1)
  16. {
  17. double[] dres = new double[2];
  18. dres[0] = x1.Min();
  19. dres[1] = x1.ToList().FindIndex(item => item == dres[0]);
  20. return dres;
  21. };

  22. // @max
  23. minmax maxf = delegate(double[] x2)
  24. {
  25. double[] dres = new double[2];
  26. dres[0] = x2.Max();
  27. dres[1] = x2.ToList().FindIndex(item => item == dres[0]);
  28. return dres;
  29. };
  30. // @(f)f(x)
  31. funcF myfunc = delegate(minmax f)
  32. {
  33. double[] dres = new double[2];
  34. dres = f(x);
  35. return dres;
  36. };
  37. // 定义返回值
  38. double[][] res = new double[2][];
  39. // 定义委托链
  40. minmax myf = minf;
  41. myf += maxf;
  42. // 实现cellfun
  43. for (int i = 0; i < myf.GetInvocationList().Length; i++)
  44. {
  45. Delegate d = myf.GetInvocationList();
  46. object rr = d.DynamicInvoke(x);
  47. res = (double[])rr;
  48. }
  49. return res;
  50. };
  51. double[] data = new double[] { 3, 4, 1, 6, 2 };
  52. double[][] res1 = myminmax(data);
  53. Console.WriteLine("最小值={0},最小值位置{1}", res1[0][0],res1[0][1]);
  54. Console.WriteLine("最大值={0},最大值位置{1}", res1[1][0], res1[1][1]);
  55. }
  56. }
复制代码

评分

1

查看全部评分

回复 不支持

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

Simapps系列直播

Archiver|小黑屋|联系我们|仿真互动网 ( 京ICP备15048925号-7 )

GMT+8, 2024-10-1 21:35 , Processed in 0.056323 second(s), 20 queries , Gzip On, MemCache On.

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表