alo*_*ols 4 lisp scheme functional-programming common-lisp racket
当我尝试使用不可变对象编写函数样式时,顺序操作最终会从内到外编写,如下所示:
(thing-operation3
(thing-operation2
(thing-operation1 thing extra-arg1)
extra-arg2)
extra-arg3)
Run Code Online (Sandbox Code Playgroud)
我开始看到这个模式重复了我的代码,我发现它很难阅读.使用咖喱和撰写等高阶程序可以略微提高:
((compose1
(curryr thing-operation3 extra-arg3)
(curryr thing-operation2 extra-arg2)
(curryr thing-operation1 extra-arg1))
thing)
Run Code Online (Sandbox Code Playgroud)
也许更好,但它仍然是颠倒的,并且需要一些额外的认知负荷来弄清楚发生了什么.而且我不确定这是否是意识形态的Lisp代码.
面向对象的风格更容易阅读:
thing.operation1(extra-arg1).operation2(extra-arg2)
.operation3(extra-arg3)
Run Code Online (Sandbox Code Playgroud)
它以自然顺序读取,也可以使用不可变对象实现.
在Lisp中编写这样的顺序操作的理想方法是什么,以便它们易于阅读?
Rai*_*wig 10
Common Lisp中的常用方法是使用 LET*
(let* ((thing1 (thing-operation0 thing0 extra-arg0))
(thing2 (thing-operation1 thing1 extra-arg1))
(thing3 (thing-operation2 thing2 extra-arg2)))
(thing-operation3 thing3 extra-arg3))
Run Code Online (Sandbox Code Playgroud)
这样就可以命名返回值,从而提高可读性,并且可以为这些值编写声明.
也可以编写一个宏,可以像下面这样使用:
(pipe
(thing-operation1 thing extra-arg1)
(thing-operation2 _2 extra-arg2)
(thing-operation3 _3 extra-arg3)
(thing-operation4 _4 extra-arg4))
Run Code Online (Sandbox Code Playgroud)
某些语言提供类似的宏,而Lisp库可能提供它的变体.我们来写一个简单的版本:
(defmacro pipe (expression &rest expressions)
(if (null expressions)
expression
(destructuring-bind ((fn arg &rest args) &rest more-expressions)
expressions
(declare (ignorable arg))
`(pipe
(,fn ,expression ,@args)
,@more-expressions))))
Run Code Online (Sandbox Code Playgroud)
对于上面的pipe表达式,生成以下代码:
(THING-OPERATION4
(THING-OPERATION3
(THING-OPERATION2
(THING-OPERATION1 THING EXTRA-ARG1)
EXTRA-ARG2)
EXTRA-ARG3)
EXTRA-ARG4)
Run Code Online (Sandbox Code Playgroud)
一个变种:
(defmacro pipe (expression &rest expressions)
(if (null expressions)
expression
(destructuring-bind ((fn arg &rest args) &rest more-expressions)
expressions
`(pipe
(let ((,arg ,expression))
(,fn ,arg ,@args))
,@more-expressions))))
Run Code Online (Sandbox Code Playgroud)
这会让你写:
(pipe (+ 1000 pi)
(+ arg1 arg1) ; use the previous result multiple times
(+ arg2 (sqrt arg2))) ; use the previous result multiple times
Run Code Online (Sandbox Code Playgroud)