apohllo.pl
więcej niż strona domowa...
 

Lekcja 14

Spis treści | Lekcja 13 | Lekcja 15

14. Rekursja a iteracja

W LISP istnieje kilka instrukcji (makr i form specjalnych) pozwalających wykonywać wielokrotnie ten sam fragment kodu. W tradycyjnym programowaniu służą do tego instrukcje iteracyjne oraz rekurencyjne wywołania pewnych funkcji. Podobnie jest w LISP.

14.1 Iteracja

Isnieje kilka instrukcji pozwalająych dokonywać iteracji, czy to na kolejnych liczbach naturalnych, czy to na kolejnych elementach listy, a także w sposób zdefiniowany przez programistę. Oto one.

14.1.1 loop

Najprostaszą konstrukcją iteracyną w LISP jest (loop body), ktora kolejno, w nieskonczonosc wykonuje formy zawarte w body. Ponieważ instrukacja ta jest niejawnie otoczna instrukcją (block nil) mozna ja przerwać tylko przy pomocy instrukcji (return) lub (return var). W pierwszym przypadku zostanie zwrocona wartosc nil, w drugim zaś var.

14.1

(setq a 4)                        =>  4
(loop
    (setq a (+ a 1))
    (when (> a 7) (return a))
  )                               =>  8
(loop
    (setq a (- a 1))
    (when (< a 3) (return))
  )                               =>  NIL

14.1.2 dotimes

Konstrukcja dotimes pozwala w łatwy sposób wykonać kilkakrotnie ten sam fragment kodu ze zmiennymi będącymi kolejnymi liczbami naturalnymi. Ma ona postać (dotimes (var n) body). Pętla ta wykonuje się n razy, przy czym var przyjmuje w kolejnych iteracjach wartości o 0 do n-1. Na końcu zwaracne jest nil.

14.2

(dotimes (i 4)
    (format t "~&I is ~S." i)) 
I is 0.
I is 1.
I is 2.
I is 3.
                                  =>  NIL

14.1.3 dolist

Instrukajca ta pozwala w łatwy sposób dokonywać pewnych operacji na wszystkich elementach listy. Ma ona postać (dolist (var list) body). Każdy element list jest kolejno przypisywany do zmiennej var, po czym wykownywane jest body, aż do osiągnięcia końca listy.

14.3

(dolist (x '(a b c)) (print x))
A
B
C
                                  =>  NIL

W najprostszej postaci dolist zwraca nil

14.1.4 do

Najbardziej skomplikowaną instrukcją związanąz iteracją jest do. Ma ona następującą postać

(do ((var1 start1 step1)
     (var2 start2 step2) ...)
    (condition resultform)
    body)

Na początku zmienne var1, var2, ... za wiązane z wartościami start1, start2, ... Jeśli wartość nie jest podana wartość początkowa, to zmienne otrzymują wartość nil. Badany jest condition (warunek). Jeśli jest on spełniony, to iteracje są przerywane i do zwraca resultform. Jeśli warunek nie jest spełniony, to wykonywane jest body, a w kolejnych iteracjach zmiennym var1, var2, ... przypisywane są wartości step1, step2,... Jeśli dla danej zmiennej nie występuje step, to zmienna ta pozostaje bez zmian w kolejnych krokach iteracji (chyba, że modyfikowana jest w body).

14.4

(do ((x 1 (+ x 1))
       (y 1 (* y 2)))
      ((> x 5) y)
      (print y)
      (print 'working)
  )
1
WORKING
2
WORKING
4
WORKING
8
WORKING
16
WORKING
32

Zmienne są wiązane analogicznie jak w let. Istniej też forma do*, w któej wiązanie następuje identycznie jak w let*.

14.1.5 mapcar

Niektóre pętle typu dolist dają się znacznie zgrabniej wyrazić w postaci instrukcji mapcar, która wywołuje pewną funkcję na każdym elemencie listy. Ma ona postać (mapcar f list).

14.5

(defun square (n) (* n n))        =>  square
(mapcar #'squara '(1 2 3 4 5))    =>  (1 3 9 16 25)

Widzimy więc, że jest ona podobna do instrukcji apply, różni się jednak traktowaniem listy argumentów. Applay przekazuje wszystkie argumenty należące do listy argumentów “za jednym zamachem”, podczas gdy mapcar przekazuje argumenty każdy z osobna. Jeżeli funkcja f przyjmuje więcej argumentów niż 1, to wszystki argumenty kolejnych wywołań zgrupowana są w osobnych listach.

14.6

(mapcar #'+ '(1 2 3) '(1 2 3))    => (2 4 6)

14.1.6 mapcan

Uzupełnieniem instrukcji mapcar jest instrukacja mapcan, która usuwa wartości nil z listy wynikowej. Wynik jest identyczny temu, który uzysklibyśmy stosują do listy wynikowej instrukcję nconc.

14.7

(defun inc (n)
  (cond ((numberp n) (+ n 1))
        (t nil)
  )
)                                 =>  inc
(inc 1)                           =>  2
(inc 'a)                          =>  nil
(mapcan #'inc '(1 a 2 b 3 c))     => (2 3 4)

14.2 Rekursja

Podobnie jak w innych językach programowania w LISP rekursję otrzymujemy poprzez powtórne wywołani funkcji w jej własnym ciele. Oczywiścia aby rekursja miała sens potrzeba aby były spełnione następujące 3 warunki:

  • rekursja musi mieć warunek stopu,
  • powinien być wyróżniony podstawowy krok rekursji,
  • kolejne wywołanie rekursyjne powinno odbywać się dla struktury o jeden mniejszej niż otrzymana na wejści.

Oto przykład rekursji w LISP:

14.8

(defun silnia (n)
  (cond ((< n 2) n)
        (t (* n (silnia (- n 1))))
  )
)                                 =>  silnia
(silnia 5)                        =>  120

Ponieważ w LISP nie ma ograniczeń na wartości liczb całkowitych (jedynym ograniczeniem jest wielkość stosu, czyli mówiąc ogólnie wielkość pamięci operacyjnej) powyższe wywołanie daje poprawne wyniki dla całkiem dużych wartości n, w porównaniu z takimi językami jak np. C.

Spis treści | Lekcja 13 | Lekcja 15

lisp | Opublikowano 12:52 29-11-2010. Ostatnia modyfikacja 12:55 29-11-2010 |
comments powered by Disqus