可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I recently had the necessity of rewriting a javascript function in javascript, dynamically. The ease with which I did it, and how fun it was, astounded me.
Over here I've got some 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>
And I couldn't change the outputted HTML, but I needed to add an extra parameter to that link. I started thinking about it, and realized I could just do this:
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&", "") + ';');
And it worked like a champ! excelExport.onclick returns a function object which I convert to a string, and do some string manip on. Since it's now in the form of "function() { ... }", I just go back and assign it to the onclick event of the dom object. It's a little ugly having to use eval, but AFAIK there isn't a javascript function constructor that can take a string of code and turn it into an object nicely.
Anyway, my point isn't that I'm super clever (I'm not), my point is that this is cool. And I know javascript isn't the only language that can do this. I've heard that lisp has had macros for years for this exact purpose. Except to really grok macros you need to really grok lisp, and I don't grok it, I just 'kind of get it'.
So my question is: In what other languages can you (easily) dynamically rewrite functions, and can you show me a simple example? I want to see where else you can do this, and how it's done!
(also, I have no idea what to tag this as, so I took random guesses)
回答1:
LISP is the ultimate language at this. LISP functions are actual LISP lists, meaning you can manipulate LISP source code as if it were any other data structure.
Here's a very trivial example of how it works:
(define hi
(lambda () (display "Hello World\n")))
;; Displays Hello World
(hi)
(set! hi
(lambda () (display "Hola World\n")))
;; Displays Hola World
(hi)
This, however, is possible in any language where functions are first-class objects. One of the most interesting showcases of the power of this syntax for LISP is in its macro system. I really don't feel I could do the topic justice, so read these links if you're interested:
http://en.wikipedia.org/wiki/Macro_(computer_science)#Lisp_macros
http://cl-cookbook.sourceforge.net/macros.html
回答2:
I guess it depends on what exactly you define as "easily dynamic rewriting". For example in .Net you have the Func type and lambdas which allows you to define functions as variables or as temporary anonymous functions eg.
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());
The above is a very contrived example of either counting the items in an array of integers or summing then using dynamically created functions subject to some arbitrary condition.
Note - Please don't point out that these things can be easily accomplished without lambdas (which they obviously can) I was simply trying to write a very simple example to demonstrate the concept in C#
回答3:
Self-modifying code is also called degenerate code. This is generally considered a bad thing, and it used to be a goal of high-level languages to prevent it from being written easily.
This is from the wikipedia entry:
Self-modifying code is seen by some as a bad practice which makes code harder to read and maintain. There are however ways in which self modification is nevertheless deemed acceptable, such as when sub routine pointers are dynamically altered - even though the effect is almost identical to direct modification.
回答4:
I think that it is the case in most of dynamic languages. Here is an example in Python
def f(x):
print x
def new_function(x): print "hello", x
f("world")
f = new_function
f("world")
The output is
world
hello world
I think that such technique should be used carefully
回答5:
Scheme allows you to do that.
(define (salute-english name) (display "Hello ") (display name))
(define (salute-french nom) (display "Salut ") (display nom))
Now you redefine a fonction by assigning the salute
variable to the right function, either salute-english
or salute-french
, like this:
(define salute salute-english)
(define (redefined-the-salute-function language)
(if (eq? language 'french)
(set! salute salute-french)
(set! salute salute-english)))
More generaly functional programming language allows you to do that or as functions are first class value. Functions can be manipulated, passed around, sometimes assigned to variables and so on. The list then include: Lisp, Scheme, Dylan, OCaml and SML. Some languages having first class functions includes Python, Ruby, Smalltalk and i think Perl.
Note that when you have an interactive language where you can interactively type your program, the redefinition of functions/methods must be possible: the REPL has to be able to do that, just in case you happen to retype the definition of an already defined functions.
回答6:
I used to do this all the time in TCL, it was a breeze and worked wonderfully. I could investigate somethings interface over the network and then create a custom-made interface on the fly to access and control things. For example, you could make a custom SNMP interface from a generic SNMP library.
I haven't used it, but C# has some built-in support for generating it's own byte-code, which is fairly impressive.
I've done this sort of thing in C as well, but there it is non-portable and almost never worth the hassle. It is a technique used sometimes for "self-optimizing" code to generate the appropriate C function to optimally process a given data set.
回答7:
You could do it in C++, but it wouldn't be easy, safe, or recommended.
- Generate the text of the source code
- invoke the compiler (fork & exec) to build a dynamic library. In gcc, you can pass the source code you want to compile on standard input, it doesn't have to be in a file.
- Load the library (LoadLibrary() on windows, dlopen() on linux)
- get a function pointer to whatever function you want (GetProcAddress() on windows, dlsym() on linux)
- If you want to replace an existing function, if it's a virtual function you could modify the v-table to point to the new function (that part especially is a horrible idea fraught with peril). The location of the v-table or the format of it isn't part of the C++ standard, but all the toolchains I've used have been consistent within themselves, so once you figure out how they do it, it probably won't break.
回答8:
Easy enough in Perl.
*some_func = sub($) {
my $arg = shift;
print $arg, "\n";
};
some_func('foo');
Re Sam Saffron's request:
*hello_world = sub() {
print "oops";
};
hello_world();
*hello_world = sub() {
print "hello world";
};
hello_world();
回答9:
In PLSQL:
create or replace procedure test
as
begin
execute immediate '
create or replace procedure test2
as
begin
null;
end;
';
end;
/
回答10:
Here's something else in Python (in addition to luc's answer), which I am not recommending, but just to show it - there is exec, which can execute a string which you could build to be whatever code...
I/O shown here is from a Python 2.5.2 interpreter session. Just some simple examples of constructing strings to execute from substrings (>>> is the interpreter prompt)...
>>> 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
>>>
回答11:
Trivial in Ruby:
def hello_world; puts "oops"; end
hello_world
# oops
def hello_world; puts "hello world"; end
hello_world
# hello world
Of course that example is boring:
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
The code above:
- Defines a blank class
- Adds a method to it
- Grabs an instance
- Intercepts that method on that instance only
回答12:
Changing what a function does is supported in a lot of languages, and it's not as complicated as you might think. In functional languages, functions are values, and function names are symbols that are bound to them like any variable. If the language allows you to reassign the symbol to a different function, this is trivial.
I think the more interesting features are the ability to get the source code for a function (toString
above) and to create a new function from a string (eval
in this case).