matlab有趣的代码_CodyNote024:“Pipeline”里的代码意境

马良,祁彬彬


0 引

先剧透:今天问题本质上是对“大于”号,也就是内置gt.m函数的重载,如果从重载运算符和前述优先级检索的角度看,它和021中的乘操作没有本质区别,之所以专门将这个问题摘出来,除了会在本篇笔记分析的小节里,开辟篇幅总结MATLAB对优先级的检索顺序,更重要的原因是,该问题一定程度上折射出作者LY CAO的一些代码哲学,这才是促使我将其作为重载系列笔记完结篇的主因。

我们说,MATLAB作为一个历史已颇为悠久的数值软件,大多数使用方法已经固化,多数人正在以mathworks公司所期望的方式,正确地使用这一产品,按照确定的语法要求,组合各种基本函数或者调用工具箱函数,编写出不会超出普通用户理解范畴太多的代码。

不过,总有极少数人,并不愿意完全按照他人既定的方式去写代码,思维中拥有我以为异常宝贵的创造力特质。他们能用别家的软件和语言,编写出带有个人风格烙印的代码,在众多求解方案中,往往有醒目的辨识度。阅读这些代码,我们可以清晰感觉到对既有函数的组合、衔接与搭配,那种游刃有余的掌控力,尤其是编写困难问题的解决代码,完全没有一般选手(比如我)时常感到的那种勉为其难的滞涩与狼狈不堪。这样的人,绝非那些满嘴潮流名词,跑到公众号上,写几篇花里胡哨的科普文章,收割有机后浪的忽悠,他们是真正站在MATLAB编程水准顶峰,把MATLAB代码写进DNA里的人。

这样的MATLAB Coder凤毛麟角,但在我眼里,LY CAO绝对是其中之一。

阅读他在Cody的代码,风格简约妖异,往往我需要很长时间思考,才能搞清代码衔接中堪称绝妙的搭配智慧,即使抛开这些不谈,毕竟为适应Cody的size导向,有时会迫使一些顶级高手,对自身代码作出必要的润饰,但仅从他所出的这道问题,我们可以侧面推断出在LY CAO眼中,一个期望的程序语言背后隐约的操作逻辑。

所以才说,写这篇笔记的初衷比较有趣。

1 原题

1.1 英文原文

链接地址:

Pipeline - MATLAB Cody - MATLAB Central​www.mathworks.com

Do something that makes

x

Equal to

fn

- x : MATLAB object

- fi: function handle

Example:

>> 

1.2 译文

从题目示例直观理解,问题42817是重载运算符“>”,使之实现函数句柄功能,还要支持连续重载——复合句柄的操作形式,形式上看起来就像是管线一段段的拼接,比如:

x

等价于:

fn

1.3 函数名称与格式

function 
问题42817是姊妹问题之一,可惜因为一方面Cody为防止滥用dos命令!echo,把这种借用系统操作函数重新生成函数的机制给禁止了,而且另一问题也没像42817一样,专门提供入口函数foo,如果不cheat,大概率成为死题,不过为让这个系列完整,在本篇最后还会给出这个题目的链接以及我自己的解法,该解法在本机是可以通过的,只是不能提交到Cody服务器而已。

1.4 测试序列

%% 1

2 分析

2.1 重载函数优先级检索顺序的总结

之前说过,这是个跑题的小节:作为系列重载问题的第3篇,也是重载这个子系列的终结,首先有必要重新总结MATLAB调用函数或成员类方法时,其检索的优先级路线。彬彬4月时,曾按对这一问题的理解,绘制了一幅MATLAB检索函数顺序的思维导图,个人感觉这幅图对重载函数时,MATLAB检索优先级问题的理解,有参考和借鉴意义。

9faecffbc79f6fdd90766f2f6a6bcf80.png

重新查看CodyNote021,结合上图搜索流程,发现关于3×2=5的代码,走的是“新建@类文件夹→新建mtimes.m文件”的套路,所以搜索路线是N→Y→Y,即:系统不存在classdef定义的double类→允许搜索路径包含“@类”文件夹→有对应方法(*操作符);

CodyNote021问题的分析提到:double和string虽同为数据类型,但前者作为最早的MATLAB数据类型之一,并不是classdef所定义的类,后者则是r2016b推出,全面面向对象,以classdef定义的新数据类型,因此搜索路线之中,并没有找到通过classdef定义的double类,因此才继续在@类文件夹下搜索对应的成员方法函数mtimes.m。

问题44313对string重载mtimes函数,是针对string类型的,系统搜索路径中能找到classdef定义的string类,因此第1步搜索结果是Y,只是string没有定义对应的“乘”方法,所以第2步结果是“N”,继续在搜索路径中检查,搜到当前工作路径包含自定义mtimes.m,故第3步结果是“Y”,汇总下来,MATLAB检索可执行函数的路线是Y→N→Y

按上述分析,实际上大可不必像之前CodyNote021那样,通过cd路径指定搜索位置,即:

function

上述代码定义PATH、判断是否存在路径以及cd(PATH)的步骤都能省略,直接走“Y→N→Y”的函数搜索路线,优化方案如下:

function

利用fopen、fpintf等文件读写函数,在当前路径“产生”mtimes.m函数,不需要mkdir生成private私有文件夹。既然切断和“短接”了MATLAB的检索路径,我们自己的mtimes函数自然就李代桃僵,承担了原本没有、或者已有却并不相同的“乘法”功能。

2.2 对“大于”操作符的重载

问题42817的重载部分其实比较简单,尤其经过前两篇笔记后——期望利用“>”符号,也就是内置的gt.m函数实现类似句柄的功能,所以自定义gt函数,会用feval调用输入句柄,当然在函数中写成f(x)的等价形式也可以。

还应留意“>”的重载,针对的输入数据,其类型为函数句柄,所以才在搜索路径(一般是当前工作路径)建立名为:“@function_handle”,新的类文件夹,这么做的意图,实际上就是为句柄这种数据类型,提供了对gt方法的重载渠道。

2.3 可变输出、逗号表达式与varargout/nargout

注意到测试算例中,输出变量的个数并不相同:测试2和测试3返回变量个数为1;测试4利用find函数返回矩阵索引,返回值个数是2;而最后一个算例的assert,返回值为0,是的,assert仅测试是否通过,如果失败也只是给出“assertion fail”的警示信息,并没有确定的返回值。

所以这个问题另一个难点是:第1输入参数,也就是句柄f,究竟可以返回多少个变量,事先并不知道,为适应几种不同的输出变量形式,需要以varargout结合nargout来控制返回值的个数,由于varargout对输出结果的cell打包属性,还是需要以逗号表达式来处理多个输出变量的并列,例如:

function

这样来编写代码,无论输入的句柄f对输出有什么样的要求,甚至是0个返回值,也都是可以适应的。

>> 

3 代码

3.1 方案1

老办法仍然是可选的,操控fprintf等函数,在Cody sever直接生成一个gt函数,当然,假如没有前面的分析,可以选择把gt文件产生在private文件夹中。

% by Ma

但通过分析一节的总结,我们知道这个PATH操控的部分是可以省略不写的:

% by Ma

3.2 方案2

目前cody sever已经不再接受通过“!”调用系统的dos命令echo,直接产生新m函数的方式,但在本机仍然可以执行,而这种形成m函数的方式,可能会在其他一些特定场景下得到应用,所以还是列出这种代码方案。

% by Alfonso Nieto-Castanon

按分析2.2的相关描述,上述代码结合mkdir函数产生了类文件夹@function_handle,在该路径下,调用系统echo命令,为句柄类型产生了一个新的gt.m方法,以备重载。

同样的道理,根据之前导图关于函数检索优先级的分析,这个mkdir的步骤也是可以省略的:

function

在当前文件夹下生成gt.m方法,当MATLAB检索到代码中的“>”操作符,首先查看对应的数据类型为function_handle,在工具箱内置路径datatype文件夹下没有找到对应function_handle的“>”成员方法,于是改为搜索当前路径,找到同名函数gt.m,这其实就是Y→N→Y的搜索路线。

1 

4 PipeLine姊妹题:Cody44249

这道题由于没提供入口函数,如果不直接cheat,可能已经成为死题,但问题解法和之前的PipeLine类似,为保持完整起见,我将问题列于本篇末尾,供参考和对比。

4.1 题目

链接:

Problem 44249. Pipeline - Variable-length Input

Pipeline - Variable-length Input - MATLAB Cody - MATLAB Central​ww2.mathworks.cn

Design the gt(>) method of function_handle so that:

>> 

The gt.m you submitted will be moved to the class folder @function_handle:

mkdir 

4.2 TestSuite

%% 1

4.3 Function Fomat

function

由于解法类似,仅提供其中的一个代码方案:

function 

假设提供了名为gtMaker的入口函数,就能用函数gtMaker在当前路径新增“@function_handler”类文件夹(非必要),并在该文件夹下产生与内置gt重名的重载方法函数,方便起见,列出由dos命令产生的该gt函数代码:

function

5 结论

正如全文的基调,问题42817的难度在前两篇笔记铺垫之下,已不再是突出的着重点,更令我印象深刻的,其实是出题人LY CAO对">"操作符的理解,正如这个题目的名称:PipeLine,一条用“>”重载所构造的“排排站”语法体系,更像是作者对程序语言逻辑的一种二次诠释,这个外形有点“萌”的句柄接龙,按笔者个人理解,是以牺牲一定语法直观性,而强化了句柄内在的逻辑表述。这也是我对出题人LY CAO心生敬意的真正原因——用他人的语言,居然解读出属于自己的另一番景象和意境。

以前曾翻阅过AutoCAD的二次开发语言AutoLisp,它是一个奇怪的,带一大票括号的表处理语言,用过一段时间后的感受,可以用“崩溃”二字来形容,一个数学上直观的“1+1”,也要操作符在前、括号内带数字紧随其后,现在看来,其实如果用数据结构来解释,AutoLisp是更符合树的生成逻辑的语言格式,因为它在取悦计算机,而不是向用户献媚。

于是在对“>”操作符的重载中,我又找到了当年找虐的乐趣,屏幕前洒下一串银铃般的笑声。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值