2011-05-29 19 views

Respuesta

11

Usted podría utilizar LOOP:

(setq *l1* (loop for x from 1 to 100 collect x)) 
(loop for x in *l1* while (<= x 10) collect x) 

Si realmente lo necesita como una función separada:

(defun take-while (pred list) 
    (loop for x in list 
     while (funcall pred x) 
     collect x)) 

Y aquí estamos:

T1> (take-while (lambda (x) (<= x 10)) *l1*) 
(1 2 3 4 5 6 7 8 9 10) 

Pero si comparamos:

(loop for x in *l1* while (<= x 10) collect x) 
(take-while (lambda (x) (<= x 10)) *l1*) 

Creo que simplemente me quedaré con el bucle.

Para las secuencias infinitas, que podría echar un vistazo a 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) 
4

Esto debería hacer ...

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

Sin embargo, esta aplicación "natural" no es recursiva de cola y podría estrellarse para listas grandes

Un enfoque explícito push-nreverse (un patrón común) podría ser

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

Un recursiva (pero recursiva de cola, por lo tanto, probablemente bien con la mayoría de las implementaciones CL) podría ser la OMI lo siguiente:

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

Tenga en cuenta que, sin embargo, no se garantiza que una implementación común de lisp manejará las optimizaciones de cola de llamada.

+1

No es una buena idea. Ni siquiera es cola recursiva. Va a explotar la pila en cualquier lista más larga ... –

+1

Gracias por aceptar, pero tenga en cuenta que probablemente danlei respuestas es mejor ... esto ni siquiera es recursivo cola – 6502

+1

@rainer joswig: Estoy de acuerdo que el bucle es mejor – 6502

3

Algunos idiomas proporcionan una API de lista de estilo Haskell como bibliotecas de terceros, con o sin soporte para flujos infinitos.

Algunos ejemplos:

Recuerde que takeWhile es relativamente fácil de implementar más de una secuencia, y se da en Haskell como:

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

Si bien no aclara mi pregunta directa, esta respuesta es muy perspicaz. ¡Gracias! – Mike

3

El CL-LAZY library implementa llamadas diferidas para Common Lisp y proporciona una función take-while que es consciente de la pereza. Puede instalarlo con Quicklisp y probarlo.

+0

¡Oye, esto es genial! ¡Gracias! – Mike

0

puede ordenar una evaluación perezosa en Common Lisp usando cierres (de Paul Graham's On 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)) 

Entonces, para llevar mientras se convierte en:

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