18

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

4

4 回答 4

16

这种编程风格在 CL 中原则上是可能的,但是作为 Lisp-2,必须添加几个#'s 和funcalls。此外,与 Haskell 相比,CL 中没有对函数进行柯里化,也没有隐式的偏应用。总的来说,我认为这样的风格不会是很地道的 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)))

(这些只是我快速制作的示例——它们没有针对不同的用例进行真正的测试或深思熟虑。)

有了这些,map ((*2) . (+1)) xsHaskell 中的内容就变成了:

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

(在此示例中,您还可以设置符号的函数单元格,而不是将函数存储在值单元格中,以绕过函数调用。)

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

在 Qi/Shen 中,函数是柯里化的,并且支持隐式偏应用(当函数调用一个参数时):

(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 中还有句法线程糖,给人一种类似“流水线”的感觉:

user=> (-> 0 inc (* 2))
2
于 2012-06-17T19:57:00.087 回答
8

->你可以使用类似的东西(这比在 Clojure中做得更多):

(defmacro -> (obj &rest forms)
  "Similar to the -> macro from clojure, but with a tweak: if there is
  a $ symbol somewhere in the form, the object is not added as the
  first argument to the form, but instead replaces the $ symbol."
  (if forms
      (if (consp (car forms))
          (let* ((first-form (first forms))
                 (other-forms (rest forms))
                 (pos (position '$ first-form)))
            (if pos
                `(-> ,(append (subseq first-form 0 pos)
                              (list obj)
                              (subseq first-form (1+ pos)))
                     ,@other-forms)
                `(-> ,(list* (first first-form) obj (rest first-form))
                     ,@other-forms)))
          `(-> ,(list (car forms) obj)
               ,@(cdr forms)))
      obj))

(您还必须小心$从您放置的包中导出符号->- 让我们称之为该包tacit- 并放入 您计划使用的任何包tacit的子句中,因此并且被继承)use->->$

使用示例:

(-> "TEST"
    string-downcase
    reverse)

(-> "TEST"
    reverse
    (elt $ 1))

这更像是 F# |>(和 shell 管道)而不是 Haskell 的.,但它们几乎是一样的(我更喜欢|>,但这是个人品味的问题)。

要查看->在做什么,只需将最后一个示例宏展开三遍(在 SLIME 中,这是通过将光标放在(示例中的第一个示例上并键入C-c RET三遍来完成的)。

于 2012-06-18T09:25:01.163 回答
3

是的,这是可能的,@danlei 已经很好地解释了。我将从 Paul Graham 的 ANSI Common Lisp 一书中添加一些示例,第 6.6 章关于函数构建器:

你可以像这样定义一个函数构建器:

(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)

在书中查看更多内容。

于 2012-06-17T20:53:34.170 回答
2

是的,这通常可以通过正确的功能实现。例如,这是sum从 Wikipedia 页面实现的 Racket 中的示例:

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

由于过程默认情况下不柯里化,因此以curry显式柯里化风格使用或编写函数会有所帮助。define您可以使用使用柯里化的新宏对其进行抽象。

于 2012-06-17T19:25:19.350 回答