Functional programming Lisp中的顺序过程

Functional programming Lisp中的顺序过程,functional-programming,scheme,lisp,common-lisp,racket,Functional Programming,Scheme,Lisp,Common Lisp,Racket,当我尝试使用不可变对象以函数式编程时,顺序操作最终由内而外写入,如下所示: (thing-operation3 (thing-operation2 (thing-operation1 thing extra-arg1) extra-arg2) extra-arg3) 我开始看到这种模式在我的代码中反复出现,我发现很难阅读。使用高阶程序,如curry和compose,可以稍微改善这一点: ((compose1 (curryr thing-operation3 extr

当我尝试使用不可变对象以函数式编程时,顺序操作最终由内而外写入,如下所示:

(thing-operation3
  (thing-operation2
    (thing-operation1 thing extra-arg1)
    extra-arg2)
  extra-arg3)
我开始看到这种模式在我的代码中反复出现,我发现很难阅读。使用高阶程序,如curry和compose,可以稍微改善这一点:

((compose1
  (curryr thing-operation3 extra-arg3)
  (curryr thing-operation2 extra-arg2)
  (curryr thing-operation1 extra-arg1))
 thing)
也许更好,但它仍然是颠倒写的,需要一些额外的认知负荷才能弄清楚到底发生了什么。我不确定这是否是意识形态的Lisp代码

面向对象的样式更易于阅读:

thing.operation1(extra-arg1).operation2(extra-arg2)
    .operation3(extra-arg3)
它以自然顺序读取,也可以用不可变对象实现

用Lisp编写这样的顺序操作以使其易于阅读,是什么样的表意方式?

您可以使用常见的Lisp特殊形式


或者,您也可以定义自己的Lisp宏以适合自己的口味。

Clojure有一个线程操作符,它可以实现您所期望的:

(-> thing
    (thing-operation1 extra-arg1)
    (thing-operation2 extra-arg2)
    (thing-operation3 extra-arg3))
在其他Lisp方言中,可以将其作为宏轻松实现。例如,格雷格·亨德肖特(Greg Hendershott)的库中有一个
~>
表单,它在Racket中做同样的事情


rackjure中的
->
(或
~>
)宏将结果拼接为每个子窗体的第一个参数。如果您想将结果拼接为最后一个参数,则在rackjure中有一个宏(
~>

公共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))
这样可以命名返回值,从而提高可读性,还可以为这些返回值编写声明

还可以编写一个宏,其使用方式如下所示:

(pipe
 (thing-operation1 thing extra-arg1)
 (thing-operation2 _2    extra-arg2)
 (thing-operation3 _3    extra-arg3)
 (thing-operation4 _4    extra-arg4))
有些语言提供类似的宏,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))))
对于上述
管道
表达式,生成以下代码:

(THING-OPERATION4
 (THING-OPERATION3
  (THING-OPERATION2
   (THING-OPERATION1 THING EXTRA-ARG1)
   EXTRA-ARG2)
  EXTRA-ARG3)
 EXTRA-ARG4)
一种变体:

(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))))
这会让你写:

(pipe (+ 1000 pi)
      (+ arg1 arg1)         ; use the previous result multiple times
      (+ arg2 (sqrt arg2))) ; use the previous result multiple times
怎么样

(reduce (lambda (a b) (funcall b a)) 
    (list thing 
          (partial-apply op1 arg1) 
          (partial-apply op2 arg2) 
          ... 
          (partial-apply opn argn) ))
(in)。在,


关于术语,要么是
((curry-fun)arg)
,要么是
(partial-apply-fun arg)
,谢谢。可能问题还不清楚,每个操作都需要一个“thing”和一些额外的参数,并返回一个新的“thing”,下一个操作应该使用它。我希望以一种清晰且不过于冗长的方式链接这些操作。有一个名为
arrows
的库实现了这些功能,可从Quicklisp获得。库
arrows
对Common Lisp实现了类似的功能。
(foldl (lambda (a b) (a b))
   thing (list 
          (partial-apply op1 arg1) 
          (partial-apply op2 arg2) 
          ... 
          (partial-apply opn argn) ))