2009-08-02 8 views
7

consideran este javascript:¿Puede mostrarme cómo reescribir las funciones en lisp?

function addX(n) 
{ 
    return 3 + n; 
} 
alert(addX(6)); //alerts 9 
eval('var newFunc = ' + addX.toString().replace("3", "5") + ';'); 
alert(newFunc(10)); //alert 15 

por favor, ignora el hecho de que es de dudosa utilidad y la metodología, peligroso, difícil de seguir en una gran base de código, y así sucesivamente. Le permite modificar la función, sobre la marcha, en función de la información del usuario. No tengo eso demostrado, pero igual podría haberlo hecho.

Espero que me muestres cómo hacerlo en lisp. He leído muchos tutoriales, he leído mucho sobre macros, asked a broader question, he intentado muchas cosas pero finalmente me he quedado corto.

Quiero saber cómo, en lisp, puedo modificar esta función en el tiempo de ejecución para añadir en su lugar 5. O lo que sea que el usuario pueda ingresar.

(define (addX n) 
    (+ 3 n)) 

Yo no busco a currying! Sé que puedo hacer esto:

(define addXCurry 
    (lambda (x) 
    (lambda (n) 
     (+ x n)))) 

(define add5 (addXCurry 5)) 
(add5 10) 

Pero esto está creando una fábrica de funciones.
Estoy usando un ejemplo simple porque quiero entender completamente por las malas en algo bastante simple.


Editar Gracias a todos por sus respuestas. Supongo que mi gran problema con las macros (como yo las entiendo) es que no he visto una que separe por completo la modificación de la escritura. El ejemplo de javascript es simple, pero puede realizar reescrituras más complejas según la entrada del usuario.

Las macros que he visto están todas basadas en "tiempo de compilación" (o tiempo escrito por el programador, supongo). Al igual que en C++, no puede tener un parámetro de plantilla dinámica; debe conocerse en tiempo de compilación.

(Parece) En lisp no se puede alterar fundamentalmente un procedimiento en tiempo de ejecución de la forma que se puede en javascript porque se ha perdido la fuente. Puede evaluarlo y redefinirlo, pero no puede recorrer los elementos de la lista (la lista es la definición de la función), inspeccionar cada elemento y decidir si lo cambia o no. La excepción parece ser los ejemplos en la respuesta de Rainer, que son terreno inestable.

+0

http://stackoverflow.com/q/16380479/849891 es sobre cómo se hace en Lisp de una manera adecuada, AFAIK - con cierres, haciendo que las funciones en objetos respondan a los mensajes: '(diversión 'call args ...) 'o' (diversión 'reset-x-to 5) '(o, en C, usando variables estáticas dentro de funciones). Y no, no se trata esencialmente de currying, incluso si podría implementarse con uno, sintácticamente, superficialmente. –

Respuesta

15

La parte difícil es que Common Lisp (y alguna otra Lisps) se deshace del código fuente. Especialmente cuando un compilador está involucrado. Por defecto, el código fuente se ha ido y todo lo que queda es el código de la máquina a continuación. Cómo recuperar la fuente Lisp y en qué forma?

La razón detrás de esto: ¿por qué un programa CL debe mantener la fuente? Podría compilarse completamente para código de máquina o código C y no tener compilador/EVAL en tiempo de ejecución. El programa podría estar ejecutándose sin mucho entorno de desarrollo (sin compilador, etc.). El entorno Common Lisp tampoco debería ser necesario para poder "descomprimir" el código a algún tipo de código fuente reconstruido.

También es generalmente complicado.

(let ((n 0) (step 2)) (defun foo() (incf n step))) 

¿Cuál es la fuente de arriba y cómo podría cambiar STEP? La función depende de la vinculación léxica.

Otra complicación:

(defun foo (n) (+ n #.(random 1.0))) 

cómo recuperar eso? Cada vez que Lisp lee el texto fuente, se leerá un número aleatorio.

Otra complicación:

(setf (symbol-function 'foo) (compute-function)) 

puede establecer el valor de la función, con alguna función arbitraria computarizada o una función predefinida (como SIN). ¿Cómo recuperarlos, si están compilados para código de máquina, cargados como código de máquina, etc.?

Si una implementación de Common Lisp mantiene el código fuente, FUNCTION-LAMBDA-EXPRESSION lo recupera.

Hay dos maneras de evitar esto:

a) Tell Lisp el código fuente o para recordar la fuente.

Proporcione la fuente.

(let* ((fs (copy-list '(lambda (n) (+ n 3)))) 
    (fc (compile nil fs))) 
    (print (funcall fc 6)) 
    (setf (third (third fs)) 5) 
    (setf fc (compile nil fs)) 
    (funcall fc 6)) 

ejemplo Expanded:

escribir una macro DEFINIR que tanto recuerda la fuente y define la función.

(defmacro define (&rest source) 
    `(progn (setf (get ',(first source) :source) (list* 'defun ',source)) 
    (defun ,@source))) 

Above coloca el código fuente en la lista de propiedades del símbolo en: FUENTE.

Ahora podemos escribir una función que modifica la fuente y lo compila:

(defun modify (fname modifier) 
    (let ((source (get fname :source))) 
    (when source 
     (setf (get fname :source) (funcall modifier source)) 
     (eval (get fname :source)) 
     (compile fname)))) 

Ejemplo definición:

(define addx (n) (+ n 3)) 

Ejemplo reescribir:

(modify 'addx (lambda (source) (setf (third (fourth source)) 6) source)) 

b) Algunos Common Lisp implementaciones implementan una función llamada FUNCTION-LAMBDA-EXPRESSION (definida en ANSI Common Lisp))

Esta función devuelve tres valores: la fuente como datos Lisp, closure-p y el nombre. Le permitiría cambiar la fuente, compilarla y establecer el nombre de la nueva función usando COMPILE. Ejemplo de código dejado como ejercicio.

Problema: en Common Lisp, la macro DEFUN define las funciones. Lo que la macro hace detrás de escena (la contabilidad para el IDE, la reescritura de código, ...) depende de la implementación. Por lo tanto, el código que devuelve FUNCTION-LAMBDA-EXPRESSION (si la implementación devuelve el código fuente) puede tener un aspecto diferente para cada implementación.

Este es un ejemplo LispWorks:

CL-USER 12 > (function-lambda-expression #'addx) 

(LAMBDA (N) 
    (DECLARE (SYSTEM::SOURCE-LEVEL #<EQ Hash Table{0} 217874D3>)) 
    (DECLARE (LAMBDA-NAME ADDX)) 
    (+ N 3)) 
NIL 
ADDX 

por lo que podría manipular la expresión de origen y cambiarlo.

1

La forma en que está escribiendo su javascript sugiere que usted está buscando una macro:

(define-syntax addX 
    (syntax-rules() 
    ((addX a) (+ 3 a)) 
    ((addX a b) (+ a b)))) 

lo que este le da:

> (addX 2) 
5 
> (addX 2 5) 
7 

La macro le da lo que quiere en ese no es una fábrica de funciones de currying y no reescribirá el nombre de la macro para que usted acepte los cambios (aunque esto podría hacerse con algo más complejo), pero le da lo que quiere porque le permite para cambiar la función sobre la marcha.

3

Nuestro código de procedimiento:

(define add-x-code 
    '(lambda (n) 
    (+ 3 n))) 

evaluarlo a una función:

(define add-x (eval add-x-code)) 
> (add-x 5) 
8 

cambiarlo:

(define add-x-2 (eval (replace 3 7 add-x-code))) 

> (add-x-2 5) 
12 

que parece similar a lo que ha hecho en el código JavaScript . Si es una buena idea, no es lo que preguntaste, así que lo dejo.

(procedimiento de sustitución simple que nos prepararon rápidamente :)

(define (replace x y list) 
    (map (lambda (x.) 
     (if (equal? x. x) 
      y 
      (if (list? x.) 
       (replace x y x.) 
       x.))) 
     list)) 
+0

¡Gracias! Esto requeriría escribir todas las funciones como listas y evaluarlas todas al principio, ¿no? –

+0

Sí, razón por la cual las macros son la forma más natural de hacerlo. Pero luego, tu querías lo difícil ¿no? – Pinochle

+0

Creo que sí. No conozco una manera simple en la que uno pueda acoplar el código con el procedimiento de modo que el acoplamiento se pueda usar normalmente como un procedimiento, pero también se busca el código, como en JavaScript. –

7

La excepción parece ser los ejemplos en la respuesta de Rainer, que son inestables suelo.

¿Por qué? Es la conclusión lógica. No puede confiar en que el compilador conservará la fuente, por lo que solo debe almacenarla usted mismo.

Después de eso, puede correctamente trabajar con la definición de la función (a diferencia de Javascript, donde simplemente hackear una representación de cadena, que es un buen ejemplo de algo inestable).

Cuestiones relacionadas