2009-07-13 3 views
5

Недавно мне пришлось переписывать javascript-функцию в javascript динамически. Легкость, с которой я это делал, и как это было весело, поразило меня.На каких языках вы можете динамически переписывать функции «на лету»?

За здесь у меня есть некоторые 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> 

И я не мог изменить выводимый HTML, но мне нужно, чтобы добавить дополнительный параметр в этой ссылке. Я начал думать об этом и понял, что могу просто сделать это:

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&", "") + ';'); 

И это сработало как чемпион! excelExport.onclick возвращает объект функции, который я преобразовываю в строку, и выполняю некоторые операции с строкой. Поскольку он теперь находится в форме «function() {...}», я просто возвращаюсь и назначаю его событию onclick объекта dom. Это немного уродливое использование eval, но AFAIK не имеет конструктора функций javascript, который может взять строку кода и превратить ее в объект красиво.

Во всяком случае, моя точка зрения не в том, что я супер умный (я не уверен), я хочу сказать, что это круто. И я знаю, что javascript - это не единственный язык, который может это сделать. Я слышал, что у lisp были макросы в течение многих лет для этой конкретной цели. За исключением действительно макросов макросов, вам нужно действительно заглянуть в lisp, и я не понимаю, я просто «получаю это».

Так что мой вопрос: На каких других языках вы можете (легко) динамически переписать функции, и можете ли вы показать мне простой пример? Я хочу посмотреть, где еще вы можете это сделать, и как это делается!

(также, я понятия не имею, что это как помечать, так что я взял случайные догадки)

+7

Таким образом, это безумие. – skaffman

+0

Возможно, сообщество wiki? – ChristopheD

+0

Я никогда не говорил, что это самая элегантная вещь, но это довольно круто. Это похоже на большинство бит-халатов - опасная причина, по которой они могут полагаться на реализации компилятора и такие, но все же умные и интересные. –

ответ

1

Я имел обыкновение делать это все время в TCL, это был ветер и работал чудесно. Я мог бы исследовать какой-то интерфейс по сети, а затем создать пользовательский интерфейс «на лету» для доступа и управления вещами. Например, вы можете создать собственный SNMP-интерфейс из общей библиотеки SNMP.

Я не использовал его, но C# имеет встроенную поддержку для генерации собственного байтового кода, что довольно впечатляет.

Я тоже делал это на C, но там он не переносится и почти никогда не стоит хлопот. Это метод, используемый иногда для «самооптимизирующегося» кода для создания соответствующей функции C для оптимальной обработки заданного набора данных.

+1

В Tcl вы можете даже переопределить встроенные структуры управления для максимальной гибкости/безумия (удалить по вкусу) :-) –

9

LISP является высшим языком при этом. Функции LISP являются фактическими списками LISP, то есть вы можете управлять исходным кодом LISP, как если бы это была любая другая структура данных.

Вот очень простой пример того, как это работает:

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

Это, однако, возможно на любом языке, где функции являются объектами первого класса. Одна из самых интересных витрин власти этого синтаксиса для LISP - в его макросистеме. Я действительно не чувствую, что я мог бы сделать тему справедливости, так что читать эти ссылки, если вы заинтересованы: достаточно

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

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

+1

Функции Lisp не являются списками. Выражения, определяющие функцию, являются списками, но функции представляют собой различные типы объектов. –

+5

Вызов Лиспа «LISP» является sooooo 20-го века. :-) –

1

Легко в Perl.

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

запрос Re Sam шафрана:

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

хаос, можете ли вы переписать мой образец ruby ​​в Perl? (см. мой ответ) –

+0

Я надеюсь, что короткий? – chaos

+0

Я дал вам +1, но я имел ввиду длинный :) –

2

Self-modifying code также называется вырожденным кодом. Это, как правило, считается плохим, и раньше это была цель высокоуровневых языков, чтобы он не был легко написан.

Это от вступления в википедии:

Самомодифицирующийся код рассматривается некоторыми как плохая практика, что делает код труднее читать и поддерживать. Тем не менее существуют способы, при которых саморедактирование, тем не менее, считается приемлемым, например, когда субстандартные указатели динамически изменяются, хотя эффект почти идентичен прямой модификации.

+0

Я согласен, что вы определенно не должны этого делать при нормальных обстоятельствах. Какой кошмар для ремонтопригодности. Но, как уже упоминалось, он открывает некоторые действительно опрятные двери - например, есть некоторые интересные приложения для кода, которые развиваются для решения проблемы. –

+2

Этот ответ не отвечает на вопрос ОП. –

3

Я думаю, это зависит от того, что именно вы определяете как «легко динамическое переписывание». Например, в .Net у вас есть тип Func и lambdas, который позволяет вам определять функции как переменные или как временные анонимные функции, например.

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()); 

Выше, является очень надуманным примером либо подсчет элементов в массиве целых чисел или суммирования затем с помощью динамически создаваемые функциями, подлежащие некоторое произвольным состоянием.

Примечание - Пожалуйста, не указывают на то, что эти вещи могут быть легко выполнены без лямбды (что они, очевидно, может) я просто пытаюсь написать очень простой пример, чтобы продемонстрировать концепцию в C#

2

I подумайте, что это так на большинстве динамических языков. Вот пример в Python

 
def f(x): 
    print x 

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

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

Выход

 
world 
hello world 

Я думаю, что такой метод следует использовать осторожно

+0

Разве это не расстраивает JIT? Это хорошо для переводчиков, но не подходит для JIT, поскольку, по сути, они являются компиляторами и нуждаются в специальной обработке для этого –

2

Схема позволяет сделать это.

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

Теперь вы переопределить fonction путем присвоения переменной salute к правой функции, либо salute-english или salute-french, как это:

(define salute salute-english) 

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

Более Generaly функциональный язык программирования позволяет делать или как функции являются значением первого класса. Функции можно манипулировать, передавать, иногда назначать переменным и так далее. Затем список включает в себя: Лиспе, Схема, Дилан, OCaml и SML. Некоторые языки, имеющие первые функции класса включает в себя Python, Рубин, Smalltalk и я думаю, что Perl.

Обратите внимание, что когда у вас есть интерактивный язык, на котором вы можете интерактивно вводить вашу программу, должно быть возможно переопределение функций/методов: REPL должен быть в состоянии сделать это, на случай, если вам удастся повторить определение уже определенные функции.

0

В PLSQL:

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

Вы могли бы сделать это в C++, но это не будет легко, безопасно, не рекомендуется.

  1. Сформировать текст исходного кода
  2. вызвать компилятор (развилка & EXEC), чтобы построить динамическую библиотеку. В gcc вы можете передать исходный код, который хотите скомпилировать на стандартном вводе, он не обязательно должен находиться в файле.
  3. Загрузите библиотеку (LoadLibrary() на окнах, dlopen() на Linux)
  4. получить функцию указатель на то, что функция, которую вы хотите (GetProcAddress() на окнах, dlsym() на Linux)
  5. Если вы хотите для замены существующей функции, если это виртуальная функция, вы можете изменить v-таблицу, чтобы указать на новую функцию (эта часть особенно ужасная, чреватая опасностью). Расположение v-таблицы или ее формат не является частью стандарта C++, но все используемые мной инструментальные средства были согласованными внутри себя, поэтому, как только вы выясните, как они это делают, это, вероятно, не будет ломать.
0

Вот что-то еще в Python (в дополнение к ответу luc), который я не рекомендую, но просто чтобы показать его - есть exec, который может выполнить строку, которую вы могли бы построить, чтобы быть любым кодом.

I/O, показанный здесь, из сеанса интерпретатора Python 2.5.2. Просто некоторые простые примеры построения строки для выполнения из подстрок (>>> интерпретатора строка) ...

>>> 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

тривиальные в Рубине:

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

Конечно этот примером является скучным:

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 

код выше:

  1. Определяет пустой класс
  2. Добавляет метод к нему
  3. Хватает Экземпляр
  4. перехватывает, что метод этого экземпляра только
0

изменяющемся, что функция не поддерживается в большом количестве языков, и это не так сложно, как вы думаете. В функциональных языках функции являются значениями, а имена функций - это символы, привязанные к ним, как любая переменная. Если язык позволяет переназначить символ другой функции, это тривиально.

Я думаю, что более интересными функциями являются возможность получить исходный код для функции (toString выше) и создать новую функцию из строки (eval в этом случае).