您可以使用哪种语言动态地重写功能?

时间:2009-07-13 13:18:33

标签: programming-languages lisp dynamic-languages

我最近有必要动态地在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(){...}”的形式,我只需返回并将其分配给dom对象的onclick事件。不得不使用eval有点难看,但是AFAIK没有一个javascript函数构造函数可以接受一串代码并很好地将它转换成一个对象。

无论如何,我的观点并不是我非常聪明(我不是),我的观点是这很酷。我知道javascript不是唯一可以做到这一点的语言。我听说过lisp为了这个目的已经有多年的宏了。除了真正的grok宏之外,你需要真正了解lisp,我不会理解它,我只是'得到它'。

所以我的问题是:您可以(轻松地)动态重写功能的其他语言,你能给我一个简单的例子吗?我想看看你能做到的其他地方,以及如何它完成了!

(另外,我也不知道该如何标记,因此我随机猜测)

12 个答案:

答案 0 :(得分: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 :(得分: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());

以上是一个非常人为的例子,要么计算整数数组中的项目,要么使用动态创建的函数进行求和,这些函数受某些任意条件的影响。

注意 - 请不要指出这些事情可以很容易地完成而没有lambda(他们显然可以)我只是想写一个非常简单的例子来演示C#中的概念 < / p>

答案 2 :(得分:2)

Self-modifying code也称为退化代码。这通常被认为是一件坏事,它曾经是高级语言的目标,以防止它被轻易写入。

这来自维基百科条目:

  

有些人认为自我修改代码是一种不好的做法,会使代码难以阅读和维护。然而,有些方法可以认为自修改是可以接受的,例如当子例程指针被动态改变时 - 即使效果几乎与直接修改相同。

答案 3 :(得分:2)

我认为大多数动态语言都是如此。这是Python中的一个例子

def f(x):
    print x

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

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

输出

world
hello world

我认为应该谨慎使用这种技术

答案 4 :(得分:2)

Scheme允许你这样做。

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

现在,您可以通过将salute变量分配给正确的函数salute-englishsalute-french来重新定义函数,如下所示:

(define salute salute-english)

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

更多通用的函数式编程语言允许您这样做或者函数是第一类值。函数可以被操纵,传递,有时分配给变量等等。该列表包括: Lisp 方案 Dylan OCaml SML 。一些具有一流功能的语言包括 Python Ruby Smalltalk ,我认为 Perl

请注意,当您拥有可以交互式键入程序的交互式语言时,必须重新定义函数/方法:REPL必须能够执行此操作,以防您碰巧重新键入已定义的功能。

答案 5 :(得分:1)

我曾经在TCL一直这样做,这是一件轻而易举的事情。我可以通过网络调查某些界面,然后动态创建一个定制的界面来访问和控制事物。例如,您可以从通用SNMP库创建自定义SNMP接口。

我没有使用它,但是C#有一些内置的支持来生成它自己的字节码,这相当令人印象深刻。

我在C中也做过这样的事情,但是它不可移植,几乎不值得麻烦。这种技术有时用于“自我优化”代码,以生成适当的C函数,以优化处理给定的数据集。

答案 6 :(得分:1)

Perl很容易。

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

Re Sam Saffron的要求:

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

答案 7 :(得分:1)

你可以用C ++做,但这不容易,安全或推荐。

  1. 生成源代码的文本
  2. 调用编译器(fork&amp; exec)来构建动态库。在gcc中,您可以在标准输入上传递要编译的源代码,它不必在文件中。
  3. 加载库(Windows上的LoadLibrary(),linux上的dlopen())
  4. 获取所需函数的函数指针(Windows上的GetProcAddress(),linux上的dlsym())
  5. 如果你想要替换现有的函数,如果它是一个虚函数,你可以修改v-table以指向新函数(这部分特别是一个充满危险的可怕想法)。 v表的位置或它的格式不是C ++标准的一部分,但我使用的所有工具链本身都是一致的,所以一旦你弄清楚它们是如何做的,它可能不会断。

答案 8 :(得分:0)

在PLSQL中:

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

答案 9 :(得分:0)

这是Python中的其他内容(除了luc的答案),我不推荐,但只是为了展示它 - 有exec,它可以执行一个字符串,你可以构建成任何代码......

此处显示的I / O来自Python 2.5.2解释器会话。只是一些构造字符串以从子字符串执行的简单示例(&gt;&gt;&gt;是解释器提示符)...

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

答案 10 :(得分:0)

Ruby中的小事:

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. 在该实例上拦截该方法

答案 11 :(得分:0)

许多语言都支持更改函数的功能,并且它并不像您想象的那么复杂。在函数式语言中,函数是值,函数名称是与任何变量一样绑定到它们的符号。如果该语言允许您将符号重新分配给其他功能,则这很简单。

我认为更有趣的功能是能够获取函数的源代码(上面是toString)并从字符串创建新函数(在本例中为eval)。