是否可以在Lisp中使用/实现tacit programming(也称为无点编程)?如果答案是肯定的,那么它已经完成了吗?
答案 0 :(得分:14)
这种编程风格原则上可以在CL中实现,但是,作为一个Lisp-2,必须添加几个#'
和funcall
s。此外,与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)))
(这些只是我掀起的简单例子 - 对于不同的用例,它们并没有真正经过测试或深思熟虑。)
有了这些,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-partially
。
在Qi / Shen中,函数是curry,并且支持隐式部分应用(当使用一个参数调用函数时):
(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
答案 1 :(得分:7)
你可以使用类似的东西(这比->
更多
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
来完成。三次)。
答案 2 :(得分: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))))
这意味着撰写可以采取任何数量的论点,是的。
创建一个向参数添加3的函数:
(curry #'+ 3)
在书中查看更多内容。
答案 3 :(得分:2)
是的,这通常可以通过正确的功能实现。例如,以下是Racket在维基百科页面中实现sum
的示例:
#lang racket
(define sum (curry foldr + 0))
由于默认情况下程序不是curry,因此使用curry
或以明确的curried风格编写函数会很有帮助。您可以使用一个使用currying的新define
宏来对其进行抽象。