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() { ... }" の形になったので、戻って dom オブジェクトの onclick イベントに割り当てます。eval を使用しなければならないのは少し見苦しいですが、私の知る限り、コードの文字列を取得してオブジェクトにうまく変換できる JavaScript 関数コンストラクターはありません。

とにかく、私の要点は、私が超賢いということではありません (私はそうではありません)。私の要点は、これがかっこいいということです。そして、これを実行できる言語は JavaScript だけではないことを私は知っています。Lisp にはまさにこの目的のために、何年も前からマクロが用意されていると聞いたことがあります。マクロを本当に理解するには、Lisp を本当に理解する必要がありますが、私はそれを理解していません。

だから私の質問は:他のどの言語で関数を(簡単に)動的に書き換えることができますか?簡単な例を見せてもらえますか? 他にどこでこれを行うことができるか、どのように行われるかを知りたいです!

(また、これに何をタグ付けするかわからないので、ランダムな推測をしました)

4

12 に答える 12

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のこの構文の力の最も興味深いショーケースの1つは、そのマクロシステムにあります。私はトピックの正義を行うことができるとは本当に感じていないので、興味があればこれらのリンクを読んでください:

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

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

于 2009-07-13T13:25:32.243 に答える
3

「簡単に動的な書き換え」と正確に定義するものに依存すると思います。たとえば、.Net には Func 型とラムダがあり、関数を変数として、または一時的な無名関数として定義できます。

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# で概念を示すために非常に単純な例を書こうとしていました。

于 2009-07-13T13:31:21.080 に答える
2

自己変更コードは縮退コードとも呼ばれます。これは一般的に悪いことと考えられており、簡単に記述できないようにするために高級言語の目標でした。

これはウィキペディアのエントリからのものです:

自己変更コードは、コードの読み取りと保守を困難にする悪い習慣と見なされる人もいます。ただし、サブルーチンポインターが動的に変更される場合など、自己変更が許容できると見なされる方法がいくつかあります。ただし、効果は直接変更とほとんど同じです。

于 2009-07-13T13:25:55.050 に答える
2

ほとんどの動的言語に当てはまると思います。これはPythonでの例です

デフ f(x):
    プリント×

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

f("ワールド")    
f = new_function
f("ワールド")

出力は

世界
こんにちは世界

そういうテクニックは慎重に使うべきだと思う

于 2009-07-13T13:39:35.110 に答える
2

スキームはそれを可能にします。

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

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

より一般的な関数型プログラミング言語を使用すると、それを行うことができます。または、関数がファーストクラスの値であるためです。関数は、操作したり、受け渡ししたり、場合によっては変数に割り当てたりすることができます。リストには、LispSchemeDylanOCaml、およびSMLが含まれます。ファーストクラスの機能を持つ言語には、PythonRubySmalltalk、そしてPerlが含まれます。

プログラムを対話的に入力できる対話型言語を使用している場合は、関数/メソッドの再定義が可能でなければならないことに注意してください。すでに定義されている関数の定義をたまたま再入力した場合に備えて、REPL はそれを実行できなければなりません。 .

于 2009-07-13T13:50:54.690 に答える
1

私はTCLでいつもこれを行っていましたが、それは簡単で、素晴らしく機能しました。ネットワークを介して何かのインターフェースを調査し、その場でカスタムメイドのインターフェースを作成して、物事にアクセスして制御することができます。たとえば、汎用SNMPライブラリからカスタムSNMPインターフェイスを作成できます。

私はそれを使用していませんが、C#には独自のバイトコードを生成するための組み込みのサポートがあり、これはかなり印象的です。

私もCでこの種のことをしましたが、移植性がなく、面倒なことはほとんどありません。これは、特定のデータセットを最適に処理するための適切なC関数を生成するために、「自己最適化」コードに時々使用される手法です。

于 2009-07-13T13:24:10.327 に答える
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();
于 2009-07-13T13:25:49.073 に答える
1

C++ で実行することもできますが、簡単でも安全でもなく、推奨もされません。

  1. ソースコードのテキストを生成する
  2. コンパイラー (fork & exec) を呼び出して、動的ライブラリーを構築します。gcc では、コンパイルするソース コードを標準入力に渡すことができます。ファイルに含まれている必要はありません。
  3. ライブラリをロードします (Windows では LoadLibrary()、Linux では dlopen())。
  4. 必要な関数への関数ポインタを取得します (Windows では GetProcAddress()、Linux では dlsym())。
  5. 既存の関数を置き換えたい場合、それが仮想関数である場合は、新しい関数を指すように v テーブルを変更できます (特に、その部分は危険を伴う恐ろしい考えです)。v-table の場所やその形式は C++ 標準の一部ではありませんが、私が使用したすべてのツールチェーンはそれ自体で一貫しているため、それらがどのように機能するかを理解すると、おそらくそうではありません。壊す。
于 2009-07-13T14:41:56.097 に答える
0

関数の機能の変更は多くの言語でサポートされており、思ったほど複雑ではありません。関数型言語では、関数は値であり、関数名は他の変数と同様にそれらにバインドされるシンボルです。言語でシンボルを別の関数に再割り当てできる場合、これは簡単です。

より興味深い機能は、関数のソースコード(上記)を取得し、文字列(この場合)toStringから新しい関数を作成する機能だと思います。eval

于 2009-09-05T14:51:30.587 に答える
0

PLSQLの場合:

create or replace procedure test
as
begin
 execute immediate '
create or replace procedure test2
as
begin
  null;
end;
 ';
end;
/
于 2009-07-13T14:30:59.300 に答える
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
>>> 
于 2009-09-04T22:21:21.520 に答える
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. そのインスタンスでのみそのメソッドをインターセプトします
于 2009-09-05T13:17:34.710 に答える