做为一位标榜的non-trivial的Common Lisp开发人员,在代码的这时候时常会碰到更让人不融洽的地方性,当中两个即是LET。
几段众所周知的LET的实例标识符如下表所示
绝大多数这时候,LET不能多于两个存取。因此,也不能而已存取两个自变量所以单纯,而应是下面此种的
有这时候就要想看一看某两个存取的值——最合适是在它排序完后立刻查阅。假如要查阅foo表达式的codice,能此种写
假如初始化foo和bar都成功的话下面的标识符也就够了。较为头疼的情形是,假如a的值不合乎市场预期,会引致b的排序操作过程出情况(虽然在下面的标识符中貌似不能)。此种情形多再次出现在LET*的采用中,如下表所示面右图
假如严重错误的a会引致bar的初始化手忙脚乱,所以在初始化function1以后才初始化print列印a早已树须了——即便初始化bar的这时候就放出condition往初始化链的下游走了。一类方式是写出下面这模样
这也相差太大了!不然写出下面这模样?
本来两个LET就能做到的事情,这下子用了两个,还引致缩进更深了一级。假如有十个变量需要列印,就会增加十个LET和十层缩进。如果心血来潮想查阅两个变量的值,还要大幅调整标识符。
问题大概就出在LET和LET*的语法上。以LET为例,它由截然分开的bindings和forms组成,两者不能互相穿插。因此,假如想在bindings中求值几段表达式,只能将bindings切开,写出两个LET的形式。好像写两个新的宏能解决这个问题?是的,vertical-let就是。
vertical-let是两个我自己写的宏,源标识符在此。其用法如下
它借鉴了LOOP中存取变量的方式(即:with和=),存取变量和用于求值的标识符还能交织在一起,如下表所示
vertical-let最终会展开为LET,比如下表所示面的标识符,会展开为如下表所示的标识符
vertical-let的算法很单纯。它遍历表达式列表,当碰到:with时就把接下来的三个元素分别视为变量名、等号,以及待求值的表达式,将三者打包进两个列表中,再压栈;当碰到其它值时,就视为待求值的表达式(将会组成LET的forms部分),也放进列表中再压栈(具体方式参见源标识符)。
将所有值都遍历并压栈后,接下来要遍历这个栈中的元素。先准备两个空的栈——两个存放bindings,两个存放forms。接着,对于每两个从栈中弹出的元素,分为如下表所示两种情形:
假如表示binding,则直接压入存放bindings的栈,否则;假如是待求值的表达式,因此上两个出栈的元素是binding,则说明早已有几段完整的LET的内容被集齐。因此,将目前在两个栈中的内容全部弹出,组合为两个LET表达式再压入存放forms的栈中。然后将方才弹出的表达式也压入forms。重复上述操作过程直至所有元素都被处理,最后将还在两个栈中的内容也组合为两个LET表达式便结束了。全文完