Lisp中的默契编程

Lisp中的默契编程,lisp,common-lisp,tacit-programming,Lisp,Common Lisp,Tacit Programming,是否可以在Lisp中使用/实现(也称为无点编程)?如果答案是肯定的,那么它已经完成了吗?是的,通常使用正确的功能是可能的。例如,以下是Wikipedia页面上Racket实现sum的一个示例: #lang racket (define sum (curry foldr + 0)) 由于过程在默认情况下不是curry,因此使用curry或以明确的curry样式编写函数会有所帮助。您可以使用一个新的使用currying的define宏对其进行抽象。这种编程风格原则上在CL中是可能的,但是,作为一个

是否可以在Lisp中使用/实现(也称为无点编程)?如果答案是肯定的,那么它已经完成了吗?

是的,通常使用正确的功能是可能的。例如,以下是Wikipedia页面上Racket实现
sum
的一个示例:

#lang racket
(define sum (curry foldr + 0))

由于过程在默认情况下不是curry,因此使用
curry
或以明确的curry样式编写函数会有所帮助。您可以使用一个新的使用currying的
define
宏对其进行抽象。

这种编程风格原则上在CL中是可能的,但是,作为一个Lisp-2,必须添加几个
#'
funcall
s。此外,与Haskell相反,例如,函数不在CL中出现,也没有隐式的部分应用程序。总的来说,我认为这样的风格不会很地道

例如,您可以这样定义部分应用程序和组合:

(defun partial (function &rest args)
  (lambda (&rest args2) (apply function (append args args2))))

(defun comp (&rest functions)
  (flet ((step (f g) (lambda (x) (funcall f (funcall g x)))))
    (reduce #'step functions :initial-value #'identity)))
(defun compose (&rest fns)
  (destructuring-bind (fn1 . rest) (reverse fns)
    #'(lambda (&rest args)
        (reduce #'(lambda (v f) (funcall f v))
                rest
                :initial-value (apply fn1 args)))))

(defun curry (fn &rest args)
  #'(lambda (&rest args2)
      (apply fn (append args args2))))
(这些只是我快速编写的示例——它们并没有针对不同的用例进行真正的测试或深思熟虑。)

有了这些,Haskell中的
map((*2)。(+1))xs
变成:

CL-USER> (mapcar (comp (partial #'* 2) #'1+) '(1 2 3))
(4 6 8)
sum
示例:

CL-USER> (defparameter *sum* (partial #'reduce #'+))
*SUM*
CL-USER> (funcall *sum* '(1 2 3))
6
(在本例中,您还可以设置符号的函数单元格,而不是将函数存储在值单元格中,以便绕过funcall。)

顺便说一下,在Emacs Lisp中,部分应用程序内置为
apply partial

在Qi/Shen中,函数是curried,并且支持隐式部分应用(当使用一个参数调用函数时):

(41-) (define comp F G -> (/. X (F (G X))))
comp

(42-) ((comp (* 2) (+ 1)) 1)
4

(43-) (map (comp (* 2) (+ 1)) [1 2 3])
[4 6 8]
Clojure中也有语法线程糖,给人一种类似的“流水线”感觉:


是的,这是可能的,@danlei已经解释得很好了。我将总结Paul Graham的《ANSI Common Lisp》一书中关于函数生成器的第6.6章中的一些示例:

您可以这样定义函数生成器:

(defun partial (function &rest args)
  (lambda (&rest args2) (apply function (append args args2))))

(defun comp (&rest functions)
  (flet ((step (f g) (lambda (x) (funcall f (funcall g x)))))
    (reduce #'step functions :initial-value #'identity)))
(defun compose (&rest fns)
  (destructuring-bind (fn1 . rest) (reverse fns)
    #'(lambda (&rest args)
        (reduce #'(lambda (v f) (funcall f v))
                rest
                :initial-value (apply fn1 args)))))

(defun curry (fn &rest args)
  #'(lambda (&rest args2)
      (apply fn (append args args2))))
像这样使用它

(mapcar (compose #'list #'round #'sqrt)
        '(4 9 16 25))
返回

((2) (3) (4) (5))
compose
函数调用:

(compose #'a #'b #'c)
等于

#'(lambda (&rest args) (a (b (apply #'c args))))
这意味着compose可以接受任意数量的参数,是的

制作一个向参数添加3的函数:

(curry #'+ 3)

请参阅本书中的更多内容。

您可以使用类似的内容(这比中的
->
做得稍微多一点) Clojure):

(还必须小心从中的软件包导出符号
$
) 您将其放置在
->
-让我们将其称为包
默认
在您计划使用
->
的任何包的
使用
子句中的
默认
,因此继承
->
$

用法示例:

(-> "TEST"
    string-downcase
    reverse)

(-> "TEST"
    reverse
    (elt $ 1))
这更像是F#的
(和shell管道),而不是Haskell的
,但是 都差不多(我更喜欢
|>
,但这是个人品味的问题)


要查看
->
正在做什么,只需将最后一个示例宏展开三次(在SLIME中,将光标放在第一个
在示例中,并键入
C-C RET
三次)即可.

这个问题是关于通用Lisp的,这个答案对于Scheme是正确的,但对于CLWell来说不是,实际上这个问题是关于Lisp的。我添加了CL作为标记,因为我最熟悉的是Lisp方言,但是这个使用Scheme的答案也很有用。但是它没有太多意义。它会导致糟糕的代码。更难阅读和理解debug.Plus-几乎每个CL编译器都会为此生成缓慢的代码(consed arg list等)。@RainerJoswig你是对的。这可以说明CL是多么灵活,闭包可以帮助我们…你的
compose
示例也适用于我的版本。我只是在
comp
中有一点复制粘贴错误或打字错误(在
步骤
中函数调用的顺序错误,REPL中的工作版本)现在已经修复。而且,我认为Rainer是对的:虽然它肯定能够在CL中这样做很好,但它不是很地道。Haskell更适合这种编程风格。(顺便说一句,格雷厄姆的风格被许多CL程序员认为非常独特。)从Clojure 1.5开始,您可以使用
作为->
,或者
->
/
->
作为->
组合使用: