In what languages can you dynamically rewrite func

2019-06-21 06:00发布

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)

12条回答
放我归山
2楼-- · 2019-06-21 06:44

In PLSQL:

create or replace procedure test
as
begin
 execute immediate '
create or replace procedure test2
as
begin
  null;
end;
 ';
end;
/
查看更多
可以哭但决不认输i
3楼-- · 2019-06-21 06:47

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.

查看更多
Fickle 薄情
4楼-- · 2019-06-21 06:49

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.

查看更多
来,给爷笑一个
5楼-- · 2019-06-21 06:50

You could do it in C++, but it wouldn't be easy, safe, or recommended.

  1. Generate the text of the source code
  2. 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.
  3. Load the library (LoadLibrary() on windows, dlopen() on linux)
  4. get a function pointer to whatever function you want (GetProcAddress() on windows, dlsym() on linux)
  5. 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.
查看更多
等我变得足够好
6楼-- · 2019-06-21 06:52

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#

查看更多
一纸荒年 Trace。
7楼-- · 2019-06-21 06:52

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:

  1. Defines a blank class
  2. Adds a method to it
  3. Grabs an instance
  4. Intercepts that method on that instance only
查看更多
登录 后发表回答