Lisp有类似Haskell的takeWhile函数吗?

时间:2011-05-29 13:51:05

标签: haskell functional-programming lisp common-lisp

我是Common Lisp的新手。在Haskell中,你可以做一些像这样的事情:

Prelude> takeWhile (<= 10) [k | k <- [1..]]
[1,2,3,4,5,6,7,8,9,10]

这在Lisp中是否可行?不一定有无限列表,但有任何列表。

5 个答案:

答案 0 :(得分:13)

您可以使用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)

答案 1 :(得分:4)

这应该......

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

然而,这种“自然”实现不是尾递归的,并且可能会因大型列表而崩溃。

明确的推 - 逆方法(一种常见模式)可能是

(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实现将处理尾调用优化。

答案 2 :(得分:4)

CL-LAZY library实现了对Common Lisp的延迟调用,并提供了一个懒惰的take-while函数。您可以使用Quicklisp进行安装并试用。

答案 3 :(得分:3)

有些语言提供Haskell样式列表API作为第三方库,无论是否支持无限流。

一些例子:

请记住,takeWhile在序列上相对容易实现,并在Haskell中给出:

takeWhile _ []          =  []
takeWhile p (x:xs)
            | p x       =  x : takeWhile p xs
            | otherwise =  []

答案 4 :(得分:0)

您可以使用闭包(来自Paul Graham's On Lisp)在常见的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))