net - Referencias variables en lisp
lisp descargar (6)
Como principiante vine aquí para averiguar cómo hacer lo que debería ser un procedimiento trivial en cualquier idioma. La mayoría de las soluciones publicadas anteriormente no funcionaron correctamente, pueden haber sido más complicadas de lo que se necesitaba o diferentes implementaciones. Aquí hay una solución simple para SBCL :
(defmacro inc-by-num (var num)
(set var (+ (eval var) num)))
Aparentemente no puedes usar setf
b / c, restringe el alcance mientras que set
no lo hace. También es posible que deba usar eval
antes de var
si aparece el error "el argumento X no es un número".
Otro novato (común) pregunta de LISP:
Básicamente, en la mayoría de los lenguajes de programación hay un medio para que las funciones reciban referencias a variables en lugar de solo valores, es decir, pasando por referencia en lugar de pasar por valor. Digamos, para simplificar, quiero escribir una función LISP que reciba una variable y aumente el valor de la variable en uno:
(defun increase-by-one (var)
(setf var (+ var 1)))
Ahora, obviamente, el problema es que esta función solo aumenta el valor de la copia de la variable en la pila, no la variable original real. También he intentado lograr el efecto utilizando macros sin mucho éxito, aunque tengo la sensación de que usar macros es el camino correcto.
Golpeo este muro todo el tiempo en LISP y estoy seguro de que debe haber una forma de evitarlo o tal vez haya un enfoque completamente diferente a este problema en LISP en el que no haya pensado. ¿Cómo se hacen las cosas como esta en LISP?
EDIT : incf
gente ha sugerido usar incf
. Solo usé este ejemplo para demostrar el problema de una manera simple, en realidad no estaba buscando reimplementar incf. Pero gracias por las sugerencias de todos modos.
Con alcance léxico, uno no tiene acceso a variables que no están en el alcance actual. Tampoco puedes pasar variables léxicas a otras funciones directamente. Lisp evalúa las variables y pasa los valores vinculados a estas variables. No hay nada como referencias de primera clase a las variables.
¡Piense funcional!
(let ((a 1))
(values (lambda (new-value)
(setf a new-value))
(lambda () a)))
Lo anterior devuelve dos funciones. Uno puede leer la variable, otro puede escribir la variable.
Llamemos al primer writer
funciones y al segundo reader
.
(defun increase-by-one (writer reader)
(funcall writer (1+ (funcall reader))))
Entonces, para hacer lo que quiera, el código necesita a) estar en el alcance o b) tener acceso a las funciones que están en el alcance.
También la variable podría ser global .
(defvar *counter* 1)
(defun increase-by-one (symbol)
(set symbol (1+ (symbol-value symbol))))
; note the use of SET to set a symbol value
(increase-by-one ''*counter*)
Esto funciona para variables globales que están representadas por un símbolo. No funciona para variables léxicas, estas no están representadas por un símbolo.
También hay una macro INCF
que aumenta un ''lugar'' (por ejemplo, una variable).
(incf a)
Pero a
es la variable en el ámbito actual.
(defun foo (a)
(incf a)) ; increases the local variable a
El límite se ve aquí :
(defun foo (var)
(add-one-some-how var))
(let ((a 1))
(foo something-referencing-a))
No hay manera de pasar una referencia directa de a
a FOO
.
La única forma es proporcionar una función. También tenemos que volver a escribir FOO
, para que llame a la función proporcionada.
(defun foo (f)
(funcall f 1)) ; calls the function with 1
(let ((a 1))
(foo (lambda (n)
(setf a (+ a n)))))
;; passes a function to foo that can set a
Creo que te estás perdiendo uno de los conceptos clave de la programación funcional: se supone que no debes cambiar el estado de los objetos una vez que se hayan creado. Cambiar algo a través de una referencia viola eso.
Las macros son probablemente lo que quieres, porque no evalúan sus argumentos, así que si pasas un nombre de variable, obtienes un nombre de variable, no su valor.
INCF hace exactamente lo que quiere, por lo que si busca en Google "defmacro incf" encontrará muchas definiciones para esto, algunas de las cuales están incluso cerca de ser correctas. :-)
Edición: estaba sugiriendo INCF no como una alternativa a escribir la tuya, sino porque hace lo que quieres, y es una macro para que puedas encontrar fácilmente el código fuente, por ejemplo, ABCL o CMUCL .
Por supuesto, en Lisp puede hacer su propio camino haciendo referencias variables, si lo desea. El enfoque más simple es así:
(defstruct reference getter setter)
(defmacro ref (place)
(let ((new-value (gensym)))
`(make-reference :getter (lambda () ,place)
:setter (lambda (,new-value)
(setf ,place ,new-value)))))
(defun dereference (reference)
(funcall (reference-getter reference)))
(defun (setf dereference) (new-value reference)
(funcall (reference-setter reference) new-value))
Y luego puedes usarlo:
(defun increase-by-one (var-ref)
(incf (dereference var-ref)))
(defun test-inc-by-one (n)
(let ((m n))
(increase-by-one (ref m))
(values m n)))
(test-inc-by-one 10) => 11, 10
Si bien Common Lisp admite un estilo de programación funcional, ese no es su enfoque general (Scheme, aunque no es puramente funcional, está mucho más cerca). Common Lisp soporta muy bien un estilo de programación completamente imperativo.
Si encuentra que necesita escribir código así, la solución habitual es una macro:
(defmacro increase-by-one (var)
`(setf ,var (+ ,var 1)))
Esto le permite escribir código como:
(increase-by-one foo)
que se ampliará a:
(setf foo (+ foo 1))
antes de que sea compilado