2009-07-13 11 views
5

Recientemente tuve la necesidad de reescribir una función de JavaScript en javascript de forma dinámica. La facilidad con la que lo hice, y lo divertido que era, me sorprendió.¿En qué idiomas puede reescribir dinámicamente funciones sobre la marcha?

Por aquí tengo algo de HTML:

<div id="excelExport1234" 
    onclick="if(somestuff) location.href='http://server/excelExport.aspx?id=56789&something=else'; else alert('not important');" 
    >Click here to export to excel</div> 

y yo no podía cambiar el código HTML como salida, pero yo tenía que añadir un parámetro adicional para ese enlace. Empecé a pensar en ello, y me di cuenta de que podía hacer esto:

excelExport = $('excelExport1234'); 
if (needParam) 
     eval('excelExport.onclick = ' + excelExport.onclick.toString().replace("excelReport.aspx?id", "excelReport.aspx?extraParam=true&id") + ';'); 
else 
     eval('excelExport.onclick = ' + excelExport.onclick.toString().replace("extraParam=true&", "") + ';'); 

¡Y funcionó como un campeón! excelExport.onclick devuelve un objeto de función que convierto en una cadena, y realiza una manipulación de cadena. Como ahora está en la forma de "función() {...}", simplemente retrocedo y lo asigno al evento onclick del objeto dom. Es un poco feo tener que usar eval, pero AFAIK no es un constructor de funciones de JavaScript que pueda tomar una cadena de código y convertirlo en un objeto muy bien.

De todos modos, mi punto no es que yo soy muy inteligente (no lo soy), mi punto es que esto es genial. Y sé que javascript no es el único lenguaje que puede hacer esto. He oído que lisp ha tenido macros durante años para este propósito exacto. Excepto por las macros realmente grok, necesitas asimilar realmente el ceceo, y no lo entiendo, simplemente lo "entiendo".

Así que mi pregunta es: ¿En qué otros idiomas puede (fácilmente) reescribir dinámicamente las funciones, y puede mostrarme un ejemplo simple? ¡Quiero ver dónde más puedes hacer esto y cómo se hace!

(también, no tengo idea de qué etiquetar esto como, por lo que tomó conjeturas al azar)

+7

De esa manera yace la locura. – skaffman

+0

¿Quizás Wiki de la comunidad? – ChristopheD

+0

Nunca dije que fuera lo más elegante, pero es genial. Es como la mayoría de los hacks de twiddling: peligrosos porque pueden confiar en implementaciones de compiladores y demás, pero aún son inteligentes e interesantes. –

Respuesta

1

que solía hacer esto todo el tiempo en TCL, que era una brisa y funcionado de maravilla. Podría investigar la interfaz de algunos en la red y luego crear una interfaz personalizada sobre la marcha para acceder y controlar cosas. Por ejemplo, puede crear una interfaz SNMP personalizada a partir de una biblioteca SNMP genérica.

No lo he usado, pero C# tiene un soporte incorporado para generar su propio código de bytes, lo cual es bastante impresionante.

He hecho este tipo de cosas en C también, pero allí no es portátil y casi nunca vale la pena la molestia. Es una técnica utilizada a veces para el código de "auto optimización" para generar la función C apropiada para procesar de manera óptima un conjunto de datos dado.

+1

En Tcl puede incluso redefinir las estructuras de control integradas para lo último en flexibilidad/locura (eliminar según el gusto) :-) –

9

LISP es el último lenguaje en esto. Las funciones LISP son listas LISP reales, lo que significa que puede manipular el código fuente LISP como si fuera cualquier otra estructura de datos.

Aquí hay un ejemplo muy trivial de cómo funciona:

(define hi 
    (lambda() (display "Hello World\n"))) 
;; Displays Hello World 
(hi) 
(set! hi 
     (lambda() (display "Hola World\n"))) 
;; Displays Hola World 
(hi) 

Esto, sin embargo, que es posible en cualquier lenguaje donde las funciones son objetos de primera clase. Una de las muestras más interesantes del poder de esta sintaxis para LISP está en su macro sistema. Realmente no siento que podría hacer el tema de justicia, por lo que leo estos enlaces si está interesado:

http://en.wikipedia.org/wiki/Macro_(computer_science)#Lisp_macros

http://cl-cookbook.sourceforge.net/macros.html

+1

Las funciones Lisp no son listas. Las expresiones que definen la función son listas, pero las funciones son un tipo distinto de objetos. –

+5

Llamar a Lisp "LISP" es taaaan del siglo XX. :-) –

1

bastante fácil en Perl.

*some_func = sub($) { 
    my $arg = shift; 
    print $arg, "\n"; 
}; 
some_func('foo'); 

petición de Re Sam Azafrán:

*hello_world = sub() { 
    print "oops"; 
}; 
hello_world(); 
*hello_world = sub() { 
    print "hello world"; 
}; 
hello_world(); 
+0

caos, ¿puedes volver a escribir mi muestra de Ruby en Perl? (ver mi respuesta) –

+0

Uh, el corto espero? – chaos

+0

te di un +1 pero me refería al largo :) –

2

Self-modifying code también se llama código degenerado. Esto generalmente se considera algo malo, y solía ser un objetivo de los lenguajes de alto nivel para evitar que se escriba fácilmente.

Esto es de la entrada de la Wikipedia:

código mutante es visto por algunos como una mala práctica que hace que el código más difícil de leer y mantener. Sin embargo, hay maneras en que la auto-modificación se considera aceptable, como cuando los punteros sub-rutinarios se alteran dinámicamente, incluso aunque el efecto sea casi idéntico a la modificación directa.

+0

Estoy de acuerdo en que definitivamente no deberías hacer esto en circunstancias normales. Qué pesadilla de mantenibilidad. Pero como se mencionó, abre algunas puertas realmente ordenadas, por ejemplo, hay algunas aplicaciones fascinantes de CS para el código que evoluciona para resolver un problema. –

+2

Esta respuesta no responde a la pregunta de OP. –

3

Supongo que depende de lo que usted defina exactamente como "reescritura fácilmente dinámica". Por ejemplo, en .Net tiene el tipo de Func y lambdas, que le permite definir funciones como variables o como funciones anónimas temporales, por ejemplo.

int[] numbers = {1, 2, 3, 4, 5}; 

Func<int[], int> somefunc; 
if (someCondition) 
{ 
    somefunc = (is => is.Sum()); 
} else { 
    somefunc = (is => is.Count()); 
} 

Console.WriteLine(somefunc(numbers).ToString()); 

Lo anterior es un ejemplo muy artificial de cualquiera de contar los artículos en una matriz de enteros o sumando a continuación, utilizando funciones creados dinámicamente dependen de alguna condición arbitraria.

Nota - Por favor, no señalan que estas cosas pueden lograrse fácilmente sin lambdas (que, obviamente, puede) que simplemente estaba tratando de escribir un ejemplo muy sencillo para demostrar el concepto en C#

2

I creo que es el caso en la mayoría de los lenguajes dinámicos. He aquí un ejemplo en Python

 
def f(x): 
    print x 

def new_function(x): print "hello", x 

f("world")  
f = new_function 
f("world") 

La salida es

 
world 
hello world 

creo que estas técnicas deben usarse con cuidado

+0

¿Tal cosa no frustra el JIT? Es algo que está bien para los intérpretes pero no es bueno para los JIT, ya que esencialmente son compiladores y necesitan un manejo especial para esto. –

2

Esquema le permite hacer eso.

(define (salute-english name) (display "Hello ") (display name)) 
(define (salute-french nom) (display "Salut ") (display nom)) 

Ahora se redefine una fonction mediante la asignación de la variable salute a la función correcta, ya sea salute-english o salute-french, así:

(define salute salute-english) 

(define (redefined-the-salute-function language) 
    (if (eq? language 'french) 
     (set! salute salute-french) 
     (set! salute salute-english))) 

Más lenguaje de programación generaly funcional le permite hacer eso o como funciones son de primera clase Las funciones pueden ser manipuladas, pasadas, a veces asignadas a variables, etc. La lista a continuación, incluyen: Lisp, Esquema, Dylan, OCaml y SML. Algunas lenguas tienen funciones de primera clase incluye Python, Rubí, Smalltalk y creo que Perl.

Tenga en cuenta que cuando tiene un lenguaje interactivo donde puede escribir de manera interactiva su programa, la redefinición de funciones/métodos debe ser posible: el REPL debe poder hacerlo, en caso de que vuelva a escribir la definición de una función ya definida.

0

En PLSQL:

create or replace procedure test 
as 
begin 
execute immediate ' 
create or replace procedure test2 
as 
begin 
    null; 
end; 
'; 
end; 
/
1

que podría hacerlo en C++, pero no sería fácil, seguro, o recomendado.

  1. generar el texto del código fuente
  2. invocar el compilador (tenedor & Exec) para construir una biblioteca dinámica. En gcc, puede pasar el código fuente que desea compilar en la entrada estándar, no tiene que estar en un archivo.
  3. cargar la biblioteca (LoadLibrary() en las ventanas, dlopen() en Linux)
  4. obtener un puntero de función a cualquier función que desee (GetProcAddress() en las ventanas, dlsym() en Linux)
  5. Si desea para reemplazar una función existente, si se trata de una función virtual, se puede modificar la tabla v para apuntar a la nueva función (esa parte es especialmente una idea horrible cargada de peligros). La ubicación de la tabla v o el formato de la misma no forma parte del estándar C++, pero todas las cadenas de herramientas que he usado han sido coherentes dentro de ellas mismas, por lo que una vez que descubras cómo lo hacen, probablemente no lo harán. descanso.
0

Aquí hay algo más en Python (además de la respuesta de luc), que no recomiendo, pero solo para mostrarlo - hay un exec, que puede ejecutar una cadena que podría compilar para cualquier código.

I/O que se muestra aquí es de una sesión de intérprete de Python 2.5.2. Sólo algunos ejemplos sencillos de construir cadenas de ejecutar de subcadenas (>>> es el símbolo del intérprete) ...

>>> def_string = 'def my_func' 
>>> param_string_1 = '():' 
>>> param_string_2 = '(x):' 
>>> do_string_1 = ' print "Do whatever."' 
>>> do_string_2 = ' print "Do something with", x' 
>>> do_string_3 = ' print "Do whatever else."' 
>>> do_string_4 = ' print "Do something else with", x' 
>>> def_1 = '\n'.join([def_string+param_string_1, do_string_1, do_string_3]) 
>>> print def_1 
def my_func(): 
    print "Do whatever." 
    print "Do whatever else." 
>>> exec def_1 
>>> my_func() 
Do whatever. 
Do whatever else. 
>>> def_2 = '\n'.join([def_string+param_string_2, do_string_2, do_string_4]) 
>>> print def_2 
def my_func(x): 
    print "Do something with", x 
    print "Do something else with", x 
>>> exec def_2 
>>> my_func('Tom Ritter') 
Do something with Tom Ritter 
Do something else with Tom Ritter 
>>> 
0

trivial en Rubí:

def hello_world; puts "oops"; end 
hello_world 
# oops 
def hello_world; puts "hello world"; end 
hello_world 
# hello world 

Por supuesto que es aburrido ejemplo:

require "benchmark" 
# why oh _why 
class Object 
    def metaclass; class << self; self; end; end 
    def meta_eval &blk; metaclass.instance_eval &blk; end 
end 

class Turtle 
end 

def make_it_move(klass) 
    klass.send(:define_method, :move) { |distance| 
    puts "moving #{distance} meters" 
    sleep(0.1 * distance) 
    } 
end 

make_it_move(Turtle) 

turtle = Turtle.new 
turtle.move(1) 
# moving 1 meters 

def profile(instance, method) 
    instance.meta_eval do 
    m = instance_method(method) 
    define_method method do |*a| 
     puts "Benchmarking #{instance.class} #{method}" 
     puts Benchmark.measure { 
     m.bind(instance).call(*a) 
     } 
    end 
    end 
end 

profile(turtle, :move) 

turtle.move(10) 
# Benchmarking Turtle move 
# moving 10 meters 
# 0.000000 0.000000 0.000000 ( 1.000994) 


Turtle.new.move(3) 
# moving 3 meters 

El código anterior:

  1. define una clase en blanco
  2. Añade un método para que
  3. ase una instancia
  4. intercepta ese método en esa instancia única
0

Cambiar lo que la función no es compatible con una gran cantidad de idiomas, y no es tan complicado como podría pensar. En los lenguajes funcionales, las funciones son valores, y los nombres de funciones son símbolos que están ligados a ellos como cualquier variable. Si el lenguaje le permite reasignar el símbolo a una función diferente, esto es trivial.

Creo que las características más interesantes son la capacidad de obtener el código fuente para una función (toString anterior) y crear una nueva función a partir de una cadena (eval en este caso).

Cuestiones relacionadas