Iteraciones


dolist

(dolist
  (var lis res)
  s1
  ...
  sn)

Asigna secuencialmente a la variable var los valores de la lista lis, ejecutando en cada caso la secuencia de sentencias s1, ..., sn. res es un valor o sentencia cuyo resultado es el que se devuelve al final de la iteración; su inclusión es opcional, y en caso de omitirlo, dolist devuelve nil.

;; Escribe los signos de una lista de números.
;; Terminado el bucle, devuelve el símbolo HECHO.
(dolist (e '(-5 6 8/9 -5/8 0 3.5 -8.7) 'hecho)
    ; e es la variable que recorre la lista
    (cond
      ((> e 0)
         (print "positivo"))
      ((< e 0)
         (print "negativo"))
      (t ; se ejecuta siempre que fallen las dos condiciones anteriores
         (print "nulo"))))
"negativo" 
"positivo" 
"positivo" 
"negativo" 
"nulo" 
"positivo" 
"negativo" 
HECHO

dotimes

(dotimes
  (var ent res)
  s1
  ...
  sn)

Asigna secuencialmente a la variable var los valores 0, 1, ... hasta alcanzar el entero ent, ejecutando en cada caso la secuencia de sentencias s1, ..., sn. res es un valor o sentencia cuyo resultado es el que se devuelve al final de la iteración; su inclusión es opcional, y en caso de omitirlo, dolist devuelve nil.

;; Escribe los cuadrados de los números 0 a 10.
(dotimes (k 10)
  (print (* k k)) )
0 
1 
4 
9 
16 
25 
36 
49 
64 
81 
NIL

do

(do
  ((var1 val1 cri1)
   ...
   (varm valm crim))
  (con res)
  s1
  ...
  sn)

En primer lugar, asigna en paralelo a cada variable vari el valor vali. A continuación comprueba si se verifica la condición con; en caso afirmativo, termina el bucle devolviendo res, en caso negativo, evalúa las sentencias s1, ..., sn. Terminada la primera iteración, actualiza las variables vari según los criterios establecidos por los criterios crii, vuelve a comprobar la condición y, según el caso, devuelve el resultado res o ejecuta nuevamente las sentencias del bucle. EL ciclo se repite hasta que la condición con se cumple y el bucle termina.

(do
  ((a 1 (+ a 2))    ; a recorre los sucesivos impares
   (b 2 (+ b 2)))   ; b recorre los sucesivos pares
  ((> a 10) 'hecho) ; itera hasta que a > 10
  (print (+ a b)) ) ; va generando sucesivas sumas de impar+par
3 
7 
11 
15 
19 
HECHO

do*

(do*
  ((var1 val1 cri1)
   ...
   (varm valm crim))
  (con res)
  s1
  ...
  sn)

Sigue la misma lógica de la instrucción do, con la salvedad de que las asignaciones de las variable NO se hacen en paralelo, sino secuencialmente, lo que permite definir la variable vari en función de cualquiera de las anteriores.

(do*
  ((a 1 (+ a 2))          ; a recorre los sucesivos impares
   (b (+ a 1) (+ a 1)))   ; tanto el valor inicial de b como el
                          ; criterio para actualizarlo se calculan
                          ; añadiendo 1 al valor de a
  ((> a 10) 'hecho) ; itera hasta que a > 10
  (print (+ a b)) ) ; va generando sucesivas sumas de impar+par
3 
7 
11 
15 
19 
HECHO

loop

La instrucción loop es muy flexible. Aquí nos ocupamos de las formas de uso más común.

(loop
  s1
  ...
  sn)

Repite la evaluación de las sentencias s1, ..., sn hasta que encuentra un return.

;; Cálculo del factorial de 10
(let ((n 10)   ; n es el número cuyo factorial queremos calcular
      (f 1 )   ; f almacenará los sucesivos productos
      (c 1 ))  ; c es el contador
    (loop
      (setf f (* f c))  ; actualiza f al siguiente número
      (setf c (+ c 1))  ; incrementa contador en una unidad
      (if (> c n)       ; ¿supera c a n?
        (return f)) ))  ; en caso afirmativo devuelve f y termina bucle
3628800
(loop 
  { ; elegir las instrucciones que se necesiten
    ; de las que se listan a continuación
    do s1 ... sn  |
    count s       |
    while s       |
    until s       |
    when s1 do s2 |
    collect s     |
    append s      |
    sum s         |
    minimize s    |
    maximize s    |
    } )

La instrucción loop admite una serie de directivas que permiten hacer una programación similar a la de otros lenguajes de uso más frecuentes:

do
Permite ejecutar una secuencia de sentencias Lisp.
count
Cuenta el número de veces que la sentencia s devuelve un valor distinto de nil. El total se devuelve al final del bucle.
while
La iteración continúa mientras la sentencia s devuelva valores distintos de nil.
until
La iteración continúa hasta que la sentencia s devuelva un valor distinto de nil.
when-do
Cuando la sentencia s1 devuelva un valor distinto de nil, se ejecuta la sentencia s2.
collect
Almacena los resultados de la sentencia s en cada iteración y los devuelve en una lista al final del bucle.
append
Anexiona las listas devueltas por la sentencia s a una lista única que devuelve al final del bucle.
sum
Suma los resultados de la sentencia s en cada iteración y lo devuelve al final del bucle.
minimize
Almacena los resultados numéricos de la sentencia s en cada iteración y devuelve su valor mínimo al final del bucle.
maximize
Almacena los resultados numéricos de la sentencia s en cada iteración y devuelve su valor máximo al final del bucle.
;; Genera puntos aleatorios en el cuadrado unidad 
;; mientras la abscisa sea mayor que 0.3
(let (x y) ; declara variables x e y
  (loop
    do (setf x (random 1.0)   ; genera dos números aleatorios entre 0 y 1
             y (random 1.0) )
    collect (list x y) ; recolecta pares generados
    while (> x 0.3) ))  ; sigue bucle mientras x > 0.3
((0.5234624 0.29858792) (0.46031213 0.51501644) (0.61946905 0.6814344)
 (0.97704315 0.50856435) (0.6648965 0.6285696) (0.5996634 0.48704994)
 (0.027078152 0.8585272))
;; Genera puntos aleatorios en el cuadrado unidad 
;; hasta que la abscisa sea menor que 0.3
(let (x y)
  (loop
    do (setf x (random 1.0)   ; genera dos números aleatorios entre 0 y 1
             y (random 1.0) )
    collect (list x y) ; recolecta pares generados
    until (< x 0.3) ))  ; sigue bucle hasta que x < 0.3
((0.44762385 0.035547733) (0.50253403 0.64249194) (0.5101901 0.64168155)
 (0.011914015 0.88014805))
;; Genera puntos aleatorios en el cuadrado unidad
;; y simula el lanzamiento de una moneda para decidir
;; si termina el bucle.
(let (x y)
  (loop
    do (setf x (random 1.0)
             y (random 1.0) )
    collect (list x y)
    ; Si utilizamos (return) tras el when no devuelve la lista que se ha generado
    when (< (random 1.0) 0.5) do (loop-finish) ))
((0.39815342 0.825333) (0.84633017 0.9185828))
(loop for var {in|on|across} lis
  { ; elegir las instrucciones que se necesiten
    ; de las que se listan a continuación
    do s1 ... sn  |
    count s       |
    while s       |
    until s       |
    when s1 do s2 |
    collect s     |
    append s      |
    sum s         |
    minimize s    |
    minimize s    |
  } )

Tomando la directiva in, la variable var toma sucesivamente los valores de la lista lis y para cada uno de ellos realiza una iteración del bucle. Con on, la variable toma los sucesivos resultados de aplicar la instrucción cdr a la lista lis. La directiva across se utiliza cuando lis es un array.

;; Imprime sucesivamente las raíces cuadradas de los
;; números de una lista
(loop for j in '(1 2 3 4 5)
  do (print (sqrt j)))
1.0 
1.4142135 
1.7320508 
2.0 
2.236068 
NIL
(loop for j on '(1 2 3 4 5)
  do (print j))
(1 2 3 4 5) 
(2 3 4 5) 
(3 4 5) 
(4 5) 
(5) 
NIL
;; Calcula los cuadrados de los números de un vector
(loop for j across #(1 2 3 4 5)
  collect (* j j))
(1 4 9 16 25)
;; Cuenta el número de vocales en un texto.
(loop for p across "Por la boca muere el pez."
  count (find p "aeiou") )
9
;; Multiplica pares de números.
;; Como se ve en el ejemplo, se pueden utilizar n-uplas de variables
(loop for (x y) in '((2 3) (4 5) (6 7) (8 9))
  collect (* x y) )
(6 20 42 72)
(loop for var from ini {to|downto|below|above} fin {by inc}
  { ; elegir las instrucciones que se necesiten
    ; de las que se listan a continuación
    do s1 ... sn  |
    count s       |
    while s       |
    until s       |
    when s1 do s2 |
    collect s     |
    append s      |
    sum s         |
    minimize s    |
    maximize s    |
  } )

Asigna inicialmente la variable var el valor ini para luego en saltos de igual amplitud llegar hasta el valor fin. Para cada valor de var se ejecuta una iteración del bucle. La parte by inc es opcional y si se omite los saltos son de una unidad.

;; La variable aumenta mientras es menor o igual que 15.
;; Saltos de 5 en 5.
(loop for k from 0 to 15 by 5
  do (print k))
0 
5 
10 
15 
NIL
;; La variable aumenta mientras es menor que 15.
;; Saltos de 5 en 5.
(loop for k from 0 below 15 by 5
  do (print k))
0 
5 
10 
NIL
;; La variable disminuye mientras es mayor o igual que -10.
;; Saltos de 2 en 2.
(loop for k from 10 downto -10 by 2
  do (print k))
10 
8 
6 
4 
2 
0 
-2 
-4 
-6 
-8 
-10 
NIL
;; La variable disminuye mientras es mayor que -10.
;; Saltos de 2 en 2.
(loop for k from 10 above -10 by 2
  do (print k))
10 
8 
6 
4 
2 
0 
-2 
-4 
-6 
-8 
NIL
;; El ciclo se mantiene mientras se cumple la condición,
;; aunque no llegue a 10. Saltos de 1 en 1.
(loop for k from 1 to 10
  while (< k 5) do (print k))
1 
2 
3 
4 
NIL
;; El ciclo se mantiene hasta que se cumple la condición,
;; aunque no llegue a 10. Saltos de 1 en 1.
(loop for k from 1 to 10
  until (> k 5) do
    (print k))
1 
2 
3 
4 
5 
NIL
;; Almacena resultados que devuelve en uns lista
(loop for k from 1 to 10
  collect k
  collect (* k k))
(1 1 2 4 3 9 4 16 5 25 6 36 7 49 8 64 9 81 10 100)
;; Fusiona listas en una sola que devuelve al final.
(loop for k from 1 to 5
  append (list (random 1.0) (random 1.0)) )
(0.19508076 0.094441175 0.55699635 0.37676394 0.093762994 0.9857626 0.91501355
 0.99292254 0.929777 0.93538976)
;; Aproxima el valor máximo de y=cos(x) en dominio [-1,1]
(loop for x from -1 to 1 by 0.01
  maximize (cos x))
1.0
(loop
  { ; elegir cuantas directivas for hagan falta para
    ; iterar en paralelo
    for var from ini {to|downto|below|above} fin {by inc}|
    for var {in|on|across} lis
    for var = s
  }
  { ; elegir las instrucciones que se necesiten
    ; de las que se listan a continuación
    do s1 ... sn  |
    count s       |
    while s       |
    until s       |
    when s1 do s2 |
    collect s     |
    append s      |
    sum s         |
    minimize s    |
    maximize s    |
  } )

Incluyendo varias directivas for es posible hacer iteraciones en paralelo (que no adinamiento de bucles). En la forma for var = s, a la variable se le asigna el resultado devuelto por la sentencia s.

;; El resultado muestra que ambos contadores 
;; van en paralelo
(loop
  for x in '(a b c d e)
  for y from 1 to 5
  collect (list x y) )
((A 1) (B 2) (C 3) (D 4) (E 5))
;; Genera pares de la función seno
(loop
  for x from 0 to 1 by 0.1
  for y = (sin x)
  collect (list x y))

© 2016, TecnoStats