Translate

sábado, 8 de septiembre de 2012

Funciones de mapeo sobre listas

Si se tiene una lista, y se desea realizar operaciones o modificaciones para cada elemento de dicha lista, obteniendo otra de la misma longitud, se puede utilizar una función de mapeo. mapcar es la mas común de tales funciones. mapcar toma una función y uno o más listas, y aplica esa función a cada elemento de la lista, produciendo una nueva lista resultante.

 > (setq una-lista '(1 2 3 4 5))

UNA-LISTA

> (defun doble (n) (* n 2))

DOBLE

> (setq otra-lista (mapcar #'doble una-lista))

(2 4 6 8)

Observe el signo #' antes de la función doble. DOBLE es el nombre del símbolo, (definido previamente con defun), que contiene a la función. La notación #' permite acceder al espacio de función del símbolo DOBLE y pasarla como argumento a mapcar.





- Maestro, ¿¡se pueden pasar funciones como argumentos...!?

- Si, pequeño saltamontes, pues las funciones son tipos de datos en Lisp, sigue practicando...

Otro ejemplo:


> (setq cadenas '("argentina gano tres a uno" "Messi hizo un gol" "Paraguay concreto de penal"))

("argentina gano tres a uno" "Messi hizo un gol" "Paraguay concreto de penal")

> (defun cambia-letra (una-cadena) (substitute  #\@ #\e una-cadena))

CAMBIA-LETRA

> (cambia-letra "cielo celeste")

“ci@lo c@l@st@”

> (setq nuevas (mapcar #'cambia-letra cadenas)


("arg@ntina gano tr@s a uno" "Lio M@ssi hizo un gol d@ tiro libr@" "Paraguay concr@to d@ p@nal")
Para no tener que definir funciones a cada rato, existe la función "sin nombre", lambda que puede pasarse como argumento a mapcar, ahí mismo donde se define.
Para el primer ejemplo de la función doble, sería asi:

> (setq otra-lista (mapcar 

                                      #'(lambda (n) (* n 2)) 
                                      una-lista))
(2 4 6 7)

Para el segundo:

(setq nuevas (mapcar 
                       #'(lambda (una-cadena) (substitute #\@ #\e una-cadena)) 
                       cadenas))

("arg@ntina gano tr@s a uno" "Lio M@ssi hizo un gol d@ tiro libr@" "Paraguay concr@to d@ p@nal") 
Bien, lambda es la función sin nombre. Permite definir de manera instantánea la función que se va a pasar como argumento. Se usa de la siguiente manera:

(lambda (argumento) (cuerpo de la función))

some

Devuelve una valor booleano (T o NIL) si algún elemento de la lista verifica la función aplicada.

> (some #'(lambda (x) (> x 9)) '(1 2 3 4 4 6 78 13))
T

Solo basta que la función que se le pasa como argumento sea booleana, es decir que devuelva T o NIL. Aquí otro ejemplo, no se desespere:

> (some #'(lambda (x) (equal x 'futbol)) '(futbol basquet voley handball golf))
T

every

Esta es como la anterior, pero todos los elementos de la lista deben cumplir con la función que se le pasa de argumento.
> (every #'(lambda (x) (atom x)) '("agua" "gota" "barco" "cielo"))
T

atom pregunta si algo es un atomo o una lista. Si es un atomo tal como pueden ser un número, una cadena e incluso una variable, es decir es un solo elemento, devuelve T, si se trata de una lista devuelve NIL.

> (setf lista ‘(1 3 6 45 2))
> (every #'(lambda (x) (>= x 3)) lista)
NIL

apply

Su nombre mismo lo indica, “apply”ca la función a todos los elementos, solo que esta devuelve un solo valor, el valor acumulado.

> (apply #’+ lista)
57

> (apply #’append ‘((a b c) (1 5 9) (“Armando” “Esteban” “Quito”)))
(A B C 1 5 9 "Armando" "Esteban" "Quito")

remove-if

No modifica la lista actual, pero devuelve una lista como resultado de haber eliminado los elementos que verifican la función que se le pasó como argumento.

> (remove-if #'(lambda (x) (> x 5)) lista)
(1 3 2)

>  (remove-if #'atom '("Laurita" 5 ("Jose" valor) (4.1 19 #c(2 3)) “renegado”))
(("Jose" VALOR) (4.1 19 #C(2 3)))

Saludos. Comenten.  :-D

9 comentarios:

  1. Buenas, aprovecho para hacer una pregunta, si bien no se relaciona directamente con el post. Supongamos que yo quiero asignar a una cadena de caracteres un nombre para acudir a ella más tarde. Puedo usar setq y asignar dicha cadena como valor de una variable. Ahora bien, yo necesito que el nombre que doy a dicha cadena (la variable a asignar un valor en setq) dependa de otros valores. Es decir, en lugar de escribir el nombre de la variable necesito evocarlo de otro modo, que dependa por ejemplo, del valor de otra variable. ¿Cómo podría hacerse eso?

    Saludos

    ResponderEliminar
    Respuestas
    1. Creo que lo que pides es esto:
      Bien, "numero" es una variable, digamos, que obtiene un valor como resultado de alguna operación o ingresada por (read), así

      > (numero (read))

      Ahora la variable "cadena" tomara un valor según el valor que tome "numero", te muestro algunas formas:
      Usando la funcion nth:

      (setq cadena (nth numero '("cero" "uno" "dos" "tres" "cuatro")))

      Usando cond, una estructura de control:

      (setq cadena (cond ((= numero 0) "el valor es cero")
      ((= numero 1) "el valor es uno")
      ((= numero 2) "el valor es dos")
      ((> numero 2) "el valor es mayor que dos")))

      Usando if:

      (setq cadena (if (< numero 2)
      "el valor es menor que dos" "el valor es mayor que dos"))

      Ojalá haya contestado a tu pregunta, si no envíame el código en C o Pascal y lo traduzco a Lisp. O bien, decime que quieres que haga tu programa. Saludos

      Eliminar
  2. Gracias. Aclaro antes que nada que recién estoy comenzando a estudiar esto y quizá el modo de encarar el problema por mi parte no sea el indicado. La idea es hacer una programa para cálculo lógico y quiero seguir un método de demostración deductivo de la lógica proposicional que sea aplicable a cualquier fórmula bien formada.

    El problema puntual por el que pregunto es no que el valor que se asigna a una variable dependa del valor de otra sino que su nombre dependa de otra.

    Tomando tu primer ejemplo, la idea sería así (lo que sigue es erróneo, lo escribo para que se entienda la idea):

    (setq (nth numero '("cero" "uno" "dos" "tres" "cuatro")) "valor de la variable")

    Es decir, la cadena "valor de la variable" sería el valor asignado a, en caso de que número sea 2 por ejemplo, a dos.

    Saludos

    ResponderEliminar
    Respuestas
    1. Si si, quedo claro. Esto seria así, vamos a usar set en vez de setq.

      Supongamos que guardamos un valor numérico en la variable valor. Por ejemplo:

      (set 'valor 3)
      ;;; la comilla indica que no se evalua la variable 3
      ;;; 'valor es un simbolo aca

      En nombre-variable guardaremos el nombre de la variable que va a depender de la variable valor a la que asignamos 3.

      (set 'nombre-variable (nth valor '(cero uno dos tres)))
      TRES

      ;;;se le asigna a nombre-variable uno de los simbolos
      ;;;de la lista en nuestro caso TRES. Un simbolo puede
      ;;;contener otro simbolo

      Ahora asignamos al contenido de nombre-variable cualquier valor que se nos ocurra:

      (set nombre-variable "valor que se nos ocurra")
      "valor que se nos ocurra"
      ;;;fijate que ahora nombre-variable no tiene
      ;;;comilla simple ('), es decir hacemos referencia al
      ;;;contenido de nombre-variable (TRES), o sea al
      ;;;simbolo que asignamos previamente, que depende de
      ;;;la variable VALOR. Cuando no hay (') se evalua

      Finish, ahora podemos consultar nuestro valores en la terminal para ver que todo salio bien :)

      > nombre-variable
      TRES

      > TRES
      "valor que se nos ocurra"

      Exitos con tu proyecto!!!

      Eliminar
    2. fe de erratas: en el comentario despues de:
      (set 'valor 3)
      ;;; la comilla indica que no se evalua la variable VALOR

      Eliminar
  3. Estupendo sitio, gracias por disponer del material aqui, el link del libro de Cooper esta caido. Un cordial saludo desde Argentina

    ResponderEliminar
  4. Hola René, muchas gracias por compartir esta información. Estoy empezando con Lisp, y encontrar tu blog me está ayudando enormemente. ¡Abrazos desde la patagonia!

    ResponderEliminar