更过程式的let——vertical-let

2023-09-06 0 481

做为一位标榜的non-trivial的Common Lisp开发人员,在代码的这时候时常会碰到更让人不融洽的地方性,当中两个即是LET

几段众所周知的LET的实例标识符如下表所示

(let ((a 1)) a)

绝大多数这时候,LET不能多于两个存取。因此,也不能而已存取两个自变量所以单纯,而应是下面此种的

(let ((a (foo x y)) (b (bar z))) (function1 a b) (function2 a b))

有这时候就要想看一看某两个存取的值——最合适是在它排序完后立刻查阅。假如要查阅foo表达式的codice,能此种写

(let ((a (foo x y)) (b (bar z))) (print a) (function1 a b) (function2 a b))

假如初始化foo和bar都成功的话下面的标识符也就够了。较为头疼的情形是,假如a的值不合乎市场预期,会引致b的排序操作过程出情况(虽然在下面的标识符中貌似不能)。此种情形多再次出现在LET*的采用中,如下表所示面右图

(let* ((a (foo x y)) (b (bar a))) (function1 a b) (function2 a b))

假如严重错误的a会引致bar的初始化手忙脚乱,所以在初始化function1以后才初始化print列印a早已树须了——即便初始化bar的这时候就放出condition往初始化链的下游走了。一类方式是写出下面这模样

(let* ((a (let ((tmp (foo x y))) (print tmp) tmp)) (b (bar a))) (function1 a b) (function2 a b))

这也相差太大了!不然写出下面这模样?

(let ((a (foo x y))) (print a) (let ((b (bar a))) (function1 a b) (function2 a b)))

本来两个LET就能做到的事情,这下子用了两个,还引致缩进更深了一级。假如有十个变量需要列印,就会增加十个LET和十层缩进。如果心血来潮想查阅两个变量的值,还要大幅调整标识符。

问题大概就出在LET和LET*的语法上。以LET为例,它由截然分开的bindings和forms组成,两者不能互相穿插。因此,假如想在bindings中求值几段表达式,只能将bindings切开,写出两个LET的形式。好像写两个新的宏能解决这个问题?是的,vertical-let就是。

vertical-let是两个我自己写的宏,源标识符在此。其用法如下

(vertical-let :with a = 1 a)

它借鉴了LOOP中存取变量的方式(即:with和=),存取变量和用于求值的标识符还能交织在一起,如下表所示

(vertical-let :with a = 1 (print a) :with b = 2 (+ a b))

vertical-let最终会展开为LET,比如下表所示面的标识符,会展开为如下表所示的标识符

(LET ((A 1)) (PRINT A) (LET ((B 2)) (+ A B)))

vertical-let的算法很单纯。它遍历表达式列表,当碰到:with时就把接下来的三个元素分别视为变量名、等号,以及待求值的表达式,将三者打包进两个列表中,再压栈;当碰到其它值时,就视为待求值的表达式(将会组成LET的forms部分),也放进列表中再压栈(具体方式参见源标识符)。

将所有值都遍历并压栈后,接下来要遍历这个栈中的元素。先准备两个空的栈——两个存放bindings,两个存放forms。接着,对于每两个从栈中弹出的元素,分为如下表所示两种情形:

假如表示binding,则直接压入存放bindings的栈,否则;假如是待求值的表达式,因此上两个出栈的元素是binding,则说明早已有几段完整的LET的内容被集齐。因此,将目前在两个栈中的内容全部弹出,组合为两个LET表达式再压入存放forms的栈中。然后将方才弹出的表达式也压入forms。重复上述操作过程直至所有元素都被处理,最后将还在两个栈中的内容也组合为两个LET表达式便结束了。

全文完

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务