我是Common Lisp的新手,正在尝试从Clojure实现repeatedly
。例如
(repeatedly 5 #(rand-int 11))
这将收集5个(rand-int 11)调用,并返回一个列表:
(10 1 3 0 2)
当前,这是我正在做的:
(defun repeatedly (n f args)
(loop for x from 1 to n
collect (apply f args)))
看起来不太好,我必须这样称呼:(repeatedly 5 #'random '(11))
。有没有办法像Clojure的语法那样使函数更直观?
然后,代码可能会变得很丑陋:(repeatedly 5 #'function (list (- x 1)))
。
答案 0 :(得分:6)
我不确定我是否正确理解了您的问题,但也许仅仅是这样:
(defun repeatedly (n function)
(loop repeat n collect (funcall function)))
因为#(…)
只是Clojure中lambda的简写。
CL-USER> (repeatedly 5 (lambda () (random 11)))
(0 8 3 6 2)
但这甚至更短:
CL-USER> (loop repeat 5 collect (random 11))
(5 4 6 2 3)
答案 1 :(得分:6)
尽管丹尼尔斯回答完美,但我想指出关于CL的一个很酷的问题。您可以实现与Clojure类似的阅读器宏。现在,由于#(1 2 3)
是CL中的数组文字,因此我将同时实现具有[...]
和[...]
功能的#(...)
。
(set-macro-character #\[
(lambda (stream char)
(declare (ignore char))
(let ((body (read-delimited-list #\] stream t)))
`(lambda (&optional %1 %2 %3 %4 %5 &aux (_ %1)) ,body))))
(set-macro-character #\]
(get-macro-character #\)))
我没有花时间搜索%s
和_
,所以它不是最佳选择。以下是一些示例:
(mapcar [sqrt (+ (* %1 %1) (* %2 %2))] '(1 3 5) '(2 4 6))
; ==> (2.236068 5 7.81025)
(mapcar [* _ 2] '(2 4 6))
; ==> (4 8 12)
(repeatedly 5 [rand-int 11])
; ==> (10 1 3 0 2)
答案 2 :(得分:6)
其他参数
一个人也可以写成
(defun repeatedly (n f &rest args)
(loop repeat n collect (apply f args)))
因此无需自己创建参数列表。
然后称之为:
> (repeatedly 5 #'random (1- x))
(7 2 3 1 4)
代替(repeatedly 5 #'random (list (1- x)))
通过宏缩写的符号
出于某些目的,也可以通过宏实现短的lambda表示法:
> (defun repeatedly (n function)
(loop repeat n collect (funcall function)))
REPEATEDLY
> (repeatedly 5 (lambda () (random 10)))
(1 7 1 7 8)
> (defmacro ⧨ (&body body) `(lambda () ,@body))
⧨
> (repeatedly 5 (⧨ (random 10)))
(9 3 0 7 0)
or alternatively:
> (defmacro ⧩ (&body body) `(lambda () ,body))
⧩
> (repeatedly 5 (⧩ random 10))
(9 7 7 7 5)
样式
通常也没有必要,也不希望将语言结构编写为 reader宏。最好将s-expressions的扩展名留在编程语言级别以下-> s-expressions主要是一种数据语法。
在大多数Lisp代码中,实际上可以找到使用的lambda
表达式而没有任何缩写。通常的Lisp风格是使用符号名称,而不使用太多特殊字符或特殊词法/令牌语法。它使文本稍长,但具有其他优点。例如,人们在文字,阅读甚至运行的代码中看到相同的lambda
...