Szukaj…


Pętle ograniczone

Możemy powtórzyć akcję kilka razy za pomocą repeat .

CL-USER> (loop repeat 10 do (format t "Hello!~%"))
Hello!
Hello!
Hello!
Hello!
Hello!
Hello!
Hello!
Hello!
Hello!
Hello!
NIL
CL-USER> (loop repeat 10 collect (random 50))
(28 46 44 31 5 33 43 35 37 4)

Pętla nad sekwencjami

(loop for i in '(one two three four five six)
      do (print i))
(loop for i in '(one two three four five six) by #'cddr
      do (print i)) ;prints ONE THREE FIVE

(loop for i on '(a b c d e f g)
      do (print (length i))) ;prints 7 6 5 4 3 2 1
(loop for i on '(a b c d e f g) by #'cddr
      do (print (length i))) ;prints 7 5 3 1
(loop for i on '(a b c)
      do (print i)) ;prints (a b c) (b c) (c)

(loop for i across #(1 2 3 4 5 6)
      do (print i)) ; prints 1 2 3 4 5 6
(loop for i across "foo"
      do (print i)) ; prints #\f #\o #\o
(loop for element across "foo"
      for i from 0
      do (format t "~a ~a~%" i element)) ; prints 0 f\n1 o\n1 o

Oto podsumowanie słów kluczowych

Słowo kluczowe Typ sekwencji Typ zmiennej
w lista element listy
na lista trochę cdr listy
przez wektor element wektora

Pętle nad tabelami skrótów

(defvar *ht* (make-hash-table))
(loop for (sym num) on 
        '(one 1 two 2 three 3 four 4 five 5 six 6 seven 7 eight 8 nine 9 ten 10)
        by #'cddr
      do (setf (gethash sym *ht*) num))

(loop for k being each hash-key of *ht*
      do (print k)) ; iterate over the keys
(loop for k being the hash-keys in *ht* using (hash-value v)
      do (format t "~a=>~a~%" k v))
(loop for v being the hash-value in *ht*
      do (print v))
(loop for v being each hash-values of *ht* using (hash-key k)
      do (format t "~a=>~a~%" k v))

Prosta forma PĘTLI

Prosty formularz LOOP bez specjalnych słów kluczowych:

(loop forms...)

Aby wyjść z pętli, możemy użyć (return <return value>) `

Kilka przykładów:

(loop (format t "Hello~%"))  ; prints "Hello" forever
(loop (print (eval (read)))) ; your very own REPL
(loop (let ((r (read)))
        (typecase r
         (number (return (print (* r r))))
         (otherwise (format t "Not a number!~%")))))

Pętle nad pakietami

(loop for s being the symbols in 'cl
      do (print s))
(loop for s being the present-symbols in :cl
      do (print s))
(loop for s being the external-symbols in (find-package "COMMON LISP")
      do (print s))
(loop for s being each external-symbols of "COMMON LISP"
      do (print s))
(loop for s being each external-symbol in pack ;pack is a variable containing a package
      do (print s))

Pętle arytmetyczne

(loop for i from 0 to 10
      do (print i)) ; prints 0 1 2 3 4 5 6 7 8 9 10
(loop for i from 0 below 10
      do (print i)) ; prints 0 1 2 3 4 5 6 7 8 9 10
(loop for i from 10 above 0
      do (print i)) ; prints 10 9 8 7 6 5 4 3 2 1
(loop for i from 10 to 0
      do (print i)) ; prints nothing
(loop for i from 10 downto 0
      do (print i)) ; prints 10 9 8 7 6 5 4 3 2 1 0
(loop for i downfrom 10 to 0
      do (print i)) ; same as above
(loop for i from 1 to 100 by 10
      do (print i)) ; prints 1 11 21 31 41 51 61 71 81 91
(loop for i from 100 downto 0 by 10
      do (print i)) ; prints 100 90 80 70 60 50 40 30 20 10 0
(loop for i from 1 to 10 by (1+ (random 3))
      do (print i)) ; note that (random 3) is evaluated only once
(let ((step (random 3)))
  (loop for i from 1 to 10 by (+ step 1)
        do (print i))) ; equivalent to the above
(loop for i from 1 to 10
      for j from 11 by 11
      do (format t "~2d ~3d~%" i j)) ;prints 1 11\n2 22\n...10 110

Restrukturyzacja w wyciągach FOR

Możemy niszczyć listy obiektów złożonych

CL-USER> (loop for (a . b) in '((1 . 2) (3 . 4) (5 . 6)) collect a)
(1 3 5)
CL-USER> (loop for (a . b) in '((1 . 2) (3 . 4) (5 . 6)) collect b)
(2 4 6)
CL-USER> (loop for (a b c) in '((1 2 3) (4 5 6) (7 8 9) (10 11 12)) collect b)
(2 5 8 11)

Możemy również zniszczyć samą listę

CL-USER> (loop for (a . b) on '(1 2 3 4 5 6) collect a)
(1 2 3 4 5 6)
CL-USER> (loop for (a . b) on '(1 2 3 4 5 6) collect b)
((2 3 4 5 6) (3 4 5 6) (4 5 6) (5 6) (6) NIL)

Jest to przydatne, gdy chcemy iterować tylko niektóre elementy

CL-USER> (loop for (a . b) on '(1 2 3 4 5 6) by #'cddr collect a)
(1 3 5)
CL-USER> (loop for (a . b) on '(1 2 3 4 5 6) by #'cdddr collect a)
(1 4)

Używanie NIL do zignorowania terminu:

(loop for (a nil . b) in '((1 2 . 3) (4 5 . 6) (7 8 . 9))
      collect (list a b)) ;=> ((1 3) (4 6) (7 9))
(loop for (a b) in '((1 2) (3 4) (5 6)) ;(a b) == (a b . nil)
      collect (+ a b)) ;=> (3 7 11)

; iterating over a window in a list
(loop for (pre x post) on '(1 2 3 4 5 3 2 1 2 3 4)
      for nth from 1
      while (and x post) ; checks that we have three elements of the list
      if (and (<= post x) (<= pre x)) collect (list :max x nth)
      if (and (>= post x) (>= pre x)) collect (list :min x nth))
; The above collects local minima/maxima

PĘTLA jako wyrażenie

W przeciwieństwie do pętli w prawie każdym innym obecnie używanym języku programowania, LOOP w Common Lisp może być używany jako wyrażenie:

(let ((doubled (loop for x from 1 to 10
                     collect (* 2 x))))
    doubled) ;; ==> (2 4 6 8 10 12 14 16 18 20)

(loop for x from 1 to 10 sum x)

MAXIMIZE powoduje, że LOOP zwraca największą oszacowaną wartość. MINIMIZE jest przeciwieństwem MAXIMIZE .

(loop repeat 100
      for x = (random 1000)
      maximize x)

COUNT mówi, ile razy wyrażenie zostało ocenione na wartość NIL niż NIL podczas pętli:

(loop repeat 100
      for x = (random 1000)
      count (evenp x))

LOOP ma również równoważników some , every , a notany funkcji:

(loop for ch across "foobar"
     thereis (eq ch #\a))

(loop for x in '(a b c d e f 1)
    always (symbolp x))

(loop for x in '(1 3 5 7)
    never (evenp x))

... z wyjątkiem tego, że nie ograniczają się do iteracji sekwencji:

(loop for value = (read *standard-input* nil :eof)
   until (eq value :eof)
   never (stringp value))

Czasowniki generujące wartości LOOP można również pisać z sufiksem -ing:

(loop repeat 100
      for x = (random 1000)
      minimizing x)

Możliwe jest również przechwycenie wartości generowanej przez te czasowniki do zmiennych (które są tworzone pośrednio przez makro LOOP ), dzięki czemu można wygenerować więcej niż jedną wartość naraz:

 (loop repeat 100
     for x = (random 1000)
     maximizing x into biggest
     minimizing x into smallest
     summing x into total
     collecting x into xs
     finally (return (values biggest smallest total xs)))

Możesz mieć więcej niż jedną klauzulę collect , count itp., Która zbiera do tej samej wartości wyjściowej. Będą wykonywane sekwencyjnie.

Poniższe konwertuje listę asocjacji (której można użyć z assoc ) na listę właściwości (której można używać z getf ):

(loop for (key . value) in assoc-list
      collect key
      collect value)

Chociaż jest to lepszy styl:

(loop for (key . value) in assoc-list
      append (list key value))

Warunkowe wykonywanie klauzul LOOP

LOOP ma własną instrukcję IF , która może kontrolować sposób wykonywania klauzul:

(loop repeat 1000
      for x = (random 100)
      if (evenp x)
        collect x into evens
      else
        collect x into odds
      finally (return (values evens odds)))

Łączenie wielu klauzul w treści JEŻELI wymaga specjalnej składni:

 (loop repeat 1000
       for x = (random 100)
       if (evenp x)
          collect x into evens
          and do (format t "~a is even!~%" x)
       else
          collect x into odds
          and count t into n-odds
       finally (return (values evens odds n-odds)))

Iteracja równoległa

W LOOP dozwolonych jest wiele klauzul FOR . Pętla kończy się, gdy kończy się pierwsza z tych klauzul:

(loop for a in '(1 2 3 4 5)
      for b in '(a b c)
      collect (list a b))
;; Evaluates to: ((1 a) (2 b) (3 c))

Inne klauzule określające, czy pętla powinna być kontynuowana, można łączyć:

(loop for a in '(1 2 3 4 5 6 7)
      while (< a 4)
      collect a)
;; Evaluates to: (1 2 3)

(loop for a in '(1 2 3 4 5 6 7)
      while (< a 4)
      repeat 1
      collect a)
;; Evaluates to: (1)

Ustal, która lista jest dłuższa, odcinając iterację, gdy tylko odpowiedź będzie znana:

(defun longerp (list-1 list-2)
    (loop for cdr1 on list-1
          for cdr2 on list-2
          if (null cdr1) return nil
          else if (null cdr2) return t
          finally (return nil)))

Numerowanie elementów listy:

(loop for item in '(a b c d e f g)
      for x from 1
      collect (cons x item))
;; Returns ((1 . a) (2 . b) (3 . c) (4 . d) (5 . e) (6 . f) (7 . g))

Upewnij się, że wszystkie liczby na liście są parzyste, ale tylko dla pierwszych 100 pozycji:

(assert
   (loop for number in list
         repeat 100
         always (evenp number)))

Iteracja zagnieżdżona

Specjalna składnia LOOP NAMED foo umożliwia utworzenie pętli, z której można wcześniej wyjść. Wyjście jest wykonywane przy użyciu return-from i może być używane z zagnieżdżonych pętli.

W poniższym przykładzie zastosowano zagnieżdżoną pętlę do wyszukiwania liczby zespolonej w tablicy 2D:

(loop named top
      for x from 0 below (array-dimension *array* 1)
      do (loop for y from 0 below (array-dimension *array* 0))
               for n = (aref *array* y x)
             when (complexp n)
               do (return-from top (values n x y))))

Klauzula RETURN a forma RETURN.

Wewnątrz LOOP możesz użyć formularza Common Lisp (return) w dowolnym wyrażeniu, co spowoduje, że formularz LOOP natychmiast oceni wartość podaną do return .

LOOP ma również klauzulę return , która działa prawie identycznie, jedyną różnicą jest to, że nie otaczasz jej nawiasami. Klauzula jest używana w DSL LOOP , natomiast forma jest używana w wyrażeniach.

(loop for x in list
      do (if (listp x) ;; Non-barewords after DO are expressions
             (return :x-has-a-list)))

;; Here, both the IF and the RETURN are clauses
(loop for x in list
     if (listp x) return :x-has-a-list)

;; Evaluate the RETURN expression and assign it to X...
;; except RETURN jumps out of the loop before the assignment
;; happens.
(loop for x = (return :nothing-else-happens)
      do (print :this-doesnt-print))

Rzecz w finally musi być wyrażeniem, więc należy użyć formularza (return) a nie klauzuli return :

 (loop for n from 1 to 100
       when (evenp n) collect n into evens
       else collect n into odds
      finally return (values evens odds)) ;; ERROR!

 (loop for n from 1 to 100
       when (evenp n) collect n into evens
       else collect n into odds
      finally (return (values evens odds))) ;; Correct usage.

Pętla nad oknem listy

Kilka przykładów okna o rozmiarze 3:

;; Naïve attempt:
(loop for (first second third) on '(1 2 3 4 5)
      do (print (* first second third)))
;; prints 6 24 60 then Errors on (* 4 5 NIL)

;; We will try again and put our attempt into a function
(defun loop-3-window1 (function list)
  (loop for (first second third) on list
        while (and second third)
        do (funcall function first second third)))
(loop-3-window1 (lambda (a b c) (print (* a b c))) '(1 2 3 4 5))
;; prints 6 24 60 and returns NIL
(loop-3-window1 (lambda (a b c) (print (list a b c))) '(a b c d nil nil e f))
;; prints (a b c) (b c d) then returns NIL

;; A second attempt
(defun loop-3-window2 (function list)
  (loop for x on list
        while (nthcdr 2 x) ;checks if there are at least 3 elements
        for (first second third) = x
        do (funcall function first second third)))
(loop-3-window2 (lambda (a b c) (print (list a b c))) '(a b c d nil nil e f))
;; prints (a b c) (b c d) (c d nil) (c nil nil) (nil nil e) (nil e f)

;; A (possibly) more efficient function:
(defun loop-3-window2 (function list)
  (let ((f0 (pop list))
        (s0 (pop list)))
    (loop for first = f0 then second
          and second = s0 then third
          and third in list
          do (funcall function first second third))))

;; A more general function:
(defun loop-n-window (n function list)
  (loop for x on list
        while (nthcdr (1- n) x)
        do (apply function (subseq x 0 n))))
;; With potentially efficient implementation:
(define-compiler-macro loop-n-window (n function list &whole w)
  (if (typep n '(integer 1 #.call-arguments-limit))
     (let ((vars (loop repeat n collect (gensym)))
           (vars0 (loop repeat (1- n) collect (gensym)))
           (lst (gensym)))
       `(let ((,lst ,list))
          (let ,(loop for v in vars0 collect `(,v (pop ,lst)))
            (loop for
                  ,@(loop for v0 in vars0 for (v vn) on vars
                     collect v collect '= collect v0 collect 'then collect vn
                     collect 'and)
                  ,(car (last vars)) in ,lst
                  do ,(if (and (consp function) (eq 'function (car function))
     w


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow