Translate

sábado, 4 de mayo de 2013

Parámetros de funciones


Parámetros requeridos


Los parámetros declarados al momento de escribir una función son guardados como una lista. El propósito básico de una lista de parámetros es, por supuesto, declarar las variables que van a recibir los argumentos pasados a la función. Cuando una lista de parámetros es una simple lista de nombres de variables - como en de suma-detallada - los parámetros se llaman parámetros requeridos. Cuando una función se llama, deben ser abastecida con un argumento para todos los parámetros requeridos. Cada parámetro es asignado al argumento correspondiente. Si una función es llamada con pocos o demasiados argumentos, Lisp dará una señal de error.

    (defun media (valor-1 valor-2 valor-3)
          (set 'resultado (/ (+ valor-1 valor-2 valor-3)))
          (format t "La media de los tres valores es: ~s" (float resultado)))


 Parametros Opcionales


En Common Lisp puedes utilizar parámetros opcionales, que para los usuarios que llaman a tu función y no especifican los valores para esos parámetros, obtienen un valor predeterminado razonable, y otros pueden proporcionar un valor específico.

Para definir una función con parámetros opcionales, después de los nombres de los parámetros requeridos, coloque el símbolo &optional seguidos por los nombres de los parámetros opcionales. Un ejemplo simple es el siguiente:

   (defun opt (a b &optional c d) (list a b c d))

Cuando se invoca la función, los primeros argumentos son asignados a los parámetros requeridos. Después de que todos los parámetros requeridos han obtenido sus valores, los valores se asignan a los parámetros opcionales. Si los argumentos pasados a la función se agotan antes que los parámetros opcionales, el resto de parámetros opcionales serán asignados a NIL. Por lo tanto, la función definida anteriormente da el siguiente resultado:

   (opt 1 2) ==> (1 2 NIL NIL)
   (opt 1 2 3) ==> (1 2 3 NIL)
   (opt 1 2 3 4) ==> (1 2 3 4)

Lisp todavía comprueba que la cantidad de argumentos apropiados sean pasados a la función - en este caso entre dos y cuatro, ambos inclusive - y dará una señal de error si la función se llama con muy pocos o demasiados.

Por supuesto, a menudo se desea un valor predeterminado distinto de NIL. Usted puede especificar el valor predeterminado mediante la sustitución del nombre del parámetro con una lista que contiene un nombre y una expresión. La expresión se evalúa solamente si el usuario que llama no pasa argumentos suficientes para proporcionar un valor para el parámetro opcional. El caso común es simplemente para proporcionar un valor en la expresión.

   (defun opt (a &optional (b 10)) (list a b))

Esta función requiere un argumento que será asignado al parámetro a. El segundo parámetro b, tomará el valor del segundo argumento, si es que existe, o 10.

   (opt 1 2) ==> (1 2)
   (opt 1) ==> (1 10)

A veces, sin embargo, es posible que tenga más flexibilidad para elegir el valor por defecto. Es posible que desee calcular un valor predeterminado basado en otros parámetros. Y se puede – el valor predeterminado puede referirse a parámetros que se guardan antes en la lista de parámetros. Si estuviera escribiendo una función que devuelve una especie de representación de un rectángulo y que ha querido dejar muy conveniente para hacer plazas, se puede utilizar una lista de argumentos como este:

   (defun hacer-rectángulo (ancho &optional (alto ancho)) ...)

lo que causaría la altura de los parámetros tengan el mismo valor que el ancho a menos que estén explícitamente especificados.

O hacer operaciones entre argumentos pasados a la función:

   (defun hacer-rectangulo (ancho &optional (alto (* 2 ancho)) ... )

Con lo que el alto sería igual al doble del ancho en caso de omitir el argumento.

De vez en cuando, es útil saber si el valor de un argumento opcional fue suministrado por el usuario o es el valor predeterminado. En lugar de escribir código para comprobar si el valor del parámetro es el valor predeterminado (que no funciona de todos modos, si el usuario le pasa de forma explícita el valor predeterminado), puede agregar otro nombre de variable para especificar la falta de valor del parámetro. A esta variable se le asigna true si la persona que llama en realidad suministra un argumento para este parámetro y NIL lo contrario. Por convención, estas variables tienen usualmente el mismo nombre que el parámetro actual con una "-p" agregada al final. Por ejemplo:

   (defun opt (a b &optional (c 3 c-p)) (list a b c c-p))

Esto da resultados como este:

   (opt 1 2) ==> (1 2 3 NIL)
   (opt 1 2 3) ==> (1 2 3 T)
   (opt 1 2 4) ==> (1 2 4 T)

Parámetros rest


Los parámetros opcionales sirven cuando se tienen parámetros discretos para los cuales el usuario puede o no desea proporcionar los valores. Sin embargo, algunas funciones toman un número variable de argumentos. La función FORMAT, por ejemplo, tiene dos argumentos requeridos, el medio y la cadena de control. Pero después de eso se necesita un número variable de argumentos en función de valores que deben ser interpolados en la cadena de control. La función + también tiene un número variable de argumentos - no hay ninguna razón particular para limitar la suma a sólo dos números, sino que se suma un número de valores. (También funciona con cero argumentos, devolviendo un 0, la identidad con la adición.) Las siguientes son todas las llamadas legales de esas dos funciones:

   (format t "hola, mundo")
   (format t "hola, ~a" nombre)
   (format t "x: ~d y:~d" x y)
   (+)
   (+ 1)
   (+ 1 2)
   (+ 1 2 3)

Obviamente, usted puede escribir funciones que toman un número variable de argumentos, simplemente les da un montón de parámetros opcionales. Pero eso sería increíblemente costoso - sólo escribir la lista de parámetros sería bastante malo, y ni que hablar en tratar con todos los parámetros en el cuerpo de la función. Para hacerlo correctamente, usted tendría que tener parámetros opcionales tantos como el número de argumentos que legalmente se puede pasar en una llamada a la función. Este número depende de la implementación, pero se garantiza que al menos 50. Y en las implementaciones actuales que van desde 4096 a 536.870.911.

En cambio, Lisp permite incluir un parámetro comodín después del símbolo &rest. Si una función incluye un parámetro &rest, cualquier argumento restante después de que los valores han sido distribuidos a todos los parámetros requeridos y opcionales es recogido en una lista que se convierte en el valor del parámetro &rest. Por lo tanto, las listas de parámetros de FORMAT y + probablemente lucen como esto:

   (defun format (medio cadena &rest valores) ...)
   (defun + (&rest números) ...)

En el ejemplo para el cálculo de la media en valores requeridos, vemos que solo podemos obtener una media para tres valores nada mas, si quisiéramos calcular la media para una cantidad indefinida de valores:

   (defun media (&rest valores)
       (set 'suma 0)
       (dolist (num valores)
            (set 'suma (+ num suma)))
       (set 'resultado (/ suma (length valores)))
       (format t "El valor de la media es: ~s" (float resultado)))


Parámetros de palabras clave

Los parámetros opcionales e &rest te dan un poco de flexibilidad, pero tampoco te van a ayudar mucho en la siguiente situación: Supongamos que tenemos una función que toma cuatro parámetros opcionales. Ahora bien, supongamos que la mayoría de los lugares en que se llama a la función, el usuario desea proporcionar un valor para sólo uno de los cuatro parámetros y, además, que los usuarios están divididos en cuanto a qué parámetros van a utilizar.

Los usuarios que quieren proporcionar un valor para el primer parámetro están bien – solo pasan el único argumento opcional y dejan en blanco el resto. Pero los otros usuarios que tienen que pasar un valor de entre uno y tres argumentos cualesquiera de ellos. ¿No es exactamente el problema que los parámetros opcionales fueron diseñados para resolver?

Por supuesto que sí. El problema es que los parámetros opcionales son todavía de posición - si el usuario quiere pasar un valor explícito para el cuarto parámetro opcional, resulta que los tres primeros parámetros opcionales son requeridos para él. Por suerte, existe otro sabor de parámetros, los parámetros de palabra clave, que permiten al usuario especificar que valores van con qué parámetros.

Para dar a una función parámetros de palabras clave, después de cualquier parámetro requerido, &optional, y &rest se incluye el símbolo &key y cualquier número de especificadores de parámetro de palabra clave, que funcionan como especificadores de parámetros opcionales. He aquí una función que tiene sólo parámetros de palabra clave:

  (defun clave (&key a b c) (list a b c))

Cuando esta función se llama, cada uno de los parámetros clave está ligado al valor inmediatamente después de una palabra clave del mismo nombre.
Si una palabra clave determinada no aparece en la lista de parámetros, al parámetro correspondiente se le asigna su valor por defecto, al igual que un parámetro opcional. Debido a que los argumentos de palabras clave están etiquetados, que se pueden pasar en cualquier orden, siempre y cuando sigan los argumentos necesarios. Por ejemplo, clave se puede invocar como sigue:

   (clave) ==> (NIL NIL NIL)
   (clave :a 1) ==> (1 NIL NIL)
   (clave :b 1) ==> (NIL 1 NIL)
   (clave :c 1) ==> (NIL NIL 1)
   (clave :a 1 :c 3) ==> (1 NIL 3)
   (clave :a 1 :b 2 :c 3) ==> (1 2 3)
   (clave :a 1 :c 3 :b 2) ==> (1 2 3)

Al igual que con los parámetros opcionales, los parámetros de palabra clave puede proporcionar una forma de valor por defecto y el nombre de una variable con -p agregada. Asimismo, la forma de valor por defecto puede hacer referencia a los parámetros que aparecen anteriormente en la lista de parámetros.

   (defun clave (&key (a 0) (b 0 b-p) (c (+ a b)))
      (list a b c b-p))

   (clave :a 1) ==> (1 0 1 NIL)
   (clave :b 1) ==> (0 1 1 T)
   (clave :a 3 :b 5) ==> (3 5 8 T)
   (clave :b 1 :c 4) ==> (0 1 4 T)
   (clave :a 2 :b 1 :c 4) ==> (2 1 4 T)

Además, si por alguna razón, desea que la palabra clave que el usuario use un parámetro diferente del nombre del parámetro real, puede reemplazar el nombre del parámetro con otra lista que contiene la palabra clave que se utiliza al llamar a la función y el nombre para ser utilizado por el parámetro. La siguiente definición de clave lo muestra:

   (defun clave (&key ((:arbol a)) ((:bombon b) 0) ((:cereza c) 0 c-p)) (list a b c c-p))

permite que el usuario lo llame así:

   (clave :arbol 10 :bombon 20 :cereza 30) ==> (10 20 30 T)

Este estilo es útil sobre todo si se quiere desvincular por completo a la API pública de la función de los detalles internos, por lo general debido a que desea utilizar los nombres de variable cortas, en lugar de palabras descriptivas en el API. Sin embargo, esto no es utilizado con frecuencia.

Fuente consultada:

Seibel Peter (2005). Practical Common Lisp.  EE.UU.: Apress
Llevar adelante este blogg cuesta tiempo, esfuerzo, dinero, y los palazos que me da mi esposa por estar todo el día frente al ordenador...

3 comentarios:

  1. Estimado:
    Mi pregunta es como puedo evaluar una función en una sentencia if ejemplo

    (defun cuadrado (x)
    (if (= x 0)(T)
    (if (= x 1)(Nill)
    )))

    (if t (cuadrado 0) (format t "Verdadero") (format t "False"))

    quisiera evaluar la función si es falso o verdadero cuando esta me devuelva el resultado (T NILL).
    PERO NO PUEDO HACER ESTO EL CLIPS COMO LO PUEDO HACER

    SE ANTE MANO SE AGRADECE SU RESPUESTA.

    ResponderEliminar
    Respuestas
    1. Hola Pragmático, tu función cuadrado debería ser asi:
      (defun cuadrado (x)
      (if (= x 0) (setf valor T))
      (if (= x 1) (setf valor nil))
      valor)
      //al final devuelve valor. Es decir evalua la variable valor
      (cuadrado 0)
      T
      (cuadrado 1)
      nil

      Eliminar
    2. La función para mostrar el resultado mas abajo se vería así:
      (if (cuadrado 0) (format t "verdadero") (format t "falso"))

      Si deseas agregar varias sentencias dentro del if, usa (progn s1 s2 s3 ...) donde s1,s2,s3,...son funciones. Por ejemplo:
      (if (cuadrado 0)
      (progn
      (format t "primera")
      (format t "segunda")
      ...))

      Tambien podrias usar (princ "verdadero") en vez de (format ...

      Si se trata de una sola condición, considera usar (when (condicion) (funcion))
      Ejemplo: (when (= x 1) (setf valor nil))

      Eliminar