我是Common Lisp的新手。在Haskell中,你可以做一些這樣的事情:Lisp有Haskell的takeWhile函數嗎?
Prelude> takeWhile (<= 10) [k | k <- [1..]]
[1,2,3,4,5,6,7,8,9,10]
這在Lisp中可能嗎?不一定與無限列表,但與任何列表。
我是Common Lisp的新手。在Haskell中,你可以做一些這樣的事情:Lisp有Haskell的takeWhile函數嗎?
Prelude> takeWhile (<= 10) [k | k <- [1..]]
[1,2,3,4,5,6,7,8,9,10]
這在Lisp中可能嗎?不一定與無限列表,但與任何列表。
你可以使用LOOP:
(setq *l1* (loop for x from 1 to 100 collect x))
(loop for x in *l1* while (<= x 10) collect x)
如果你真的需要它作爲一個獨立的功能:
(defun take-while (pred list)
(loop for x in list
while (funcall pred x)
collect x))
在這裏,我們有:
T1> (take-while (lambda (x) (<= x 10)) *l1*)
(1 2 3 4 5 6 7 8 9 10)
但是,如果我們比較:
(loop for x in *l1* while (<= x 10) collect x)
(take-while (lambda (x) (<= x 10)) *l1*)
我想我會堅持循環。
對於無窮序列,你可以在Series看一看:
T1> (setq *print-length* 20)
20
T1> (setq *l1* (scan-range :from 1))
#Z(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ...)
T1> (until-if (lambda (x) (> x 10)) *l1*)
#Z(1 2 3 4 5 6 7 8 9 10)
這應該做...
(defun take-while (list test)
(and list (funcall test (car list))
(cons (car list) (take-while (cdr list) test))))
(take-while '(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) (lambda (x) (< x 10)))
--> (1 2 3 4 5 6 7 8 9)
但是這種「自然」的實施是不是尾遞歸和可能崩潰對於大名單。
的顯式推入nreverse的方法(共同的圖案)可以是
(defun take-while (list test)
(do ((res nil))
((or (null list) (not (funcall test (car list))))
(nreverse res))
(push (car list) res)
(setf list (cdr list))))
遞歸(但尾遞歸,因此大概確定與大多數CL實現)可以IMO是下列:
(defun take-while (list test)
(labels ((rec (res x)
(if (and x (funcall test (car x)))
(rec (cons (car x) res) (cdr x))
(nreverse res))))
(rec nil list)))
請注意,但不能保證一個通用的lisp實現將處理尾呼優化。
某些語言提供了一個Haskell風格的列表API作爲第三方庫,支持或不支持無限流。
一些例子:
記住takeWhile
是比較容易實現的一個序列,並在哈斯克爾給出如下:
takeWhile _ [] = []
takeWhile p (x:xs)
| p x = x : takeWhile p xs
| otherwise = []
雖然它並沒有確定我的直接問題,但這個答案非常有見地。謝謝! – Mike 2011-05-29 14:15:14
CL-LAZY library實現對Common Lisp的惰性調用,並提供懶惰感知的take-while函數。您可以使用Quicklisp進行安裝並試用。
嘿,這很酷!謝謝! – Mike 2011-05-29 16:39:56
你可以有(從Paul Graham's On Lisp)使用閉包在Common Lisp的一個懶惰的評價:
(defun lazy-right-fold (comb &optional base)
"Lazy right fold on lists."
(labels ((rec (lst)
(if (null lst)
base
(funcall comb
(car lst)
#'(lambda() (rec (cdr lst)))))))
#'rec))
然後,取而變爲:
(defun take-while (pred lst)
(lazy-right-fold #'(lambda (x f) (
(if (test x)
(cons x (funcall f))
(funcall f)))
nil))
不是一個好主意。它甚至不是尾遞歸。它會吹出更多列表上的堆棧... – 2011-05-29 14:41:33
感謝您接受,但請注意,可能danlei答案更好...這甚至不是尾遞歸 – 6502 2011-05-29 14:41:34
@rainer joswig:我同意循環更好 – 6502 2011-05-29 14:42:38