簡體   English   中英

Lisp 是否有類似 Haskell 的 takeWhile function 的東西?

[英]Does Lisp have something like Haskell's takeWhile function?

我是 Common Lisp 的新手。 在 Haskell 中,您可以執行以下操作:

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)

如果你真的需要它作為單獨的 function:

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

然而,這種“自然”實現不是尾遞歸的,並且可能會因大列表而崩潰。

一個明確的 push-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 實現將處理尾調用優化。

CL-LAZY 庫實現了 Common Lisp 的延遲調用,並提供了一個可感知延遲的 function。 您可以使用Quicklisp安裝它並嘗試一下。

某些語言提供 Haskell 樣式列表 API 作為 3rd 方庫,支持或不支持無限流。

一些例子:

請記住,在序列上實現takeWhile相對容易,在 Haskell 中給出如下:

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

您可以使用閉包(來自Paul Graham 的 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))

然后,take-while 變成:

(defun take-while (pred lst)
  (lazy-right-fold #'(lambda (x f) (
                       (if (test x)
                           (cons x (funcall f))
                           (funcall f)))
                   nil))

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM