25

evalruby の機能を何度も使用しています。evalしかし、 sは厄介だと人々が言っ​​ているのを聞いたことがあります。その理由と方法を尋ねられたとき、私はそれを使用しないという説得力のある理由を得ることができませんでした. 彼らは本当に厄介ですか?はいの場合、どのような方法で?評価するための可能な「より安全な」オプションは何ですか?

4

7 に答える 7

13

eval安全でないだけでなく(他の場所で指摘されているように)、遅いです。実行されるたびに、evaled コードの AST を新たに解析する必要があります (JRuby などでは、バイトコードに変換する必要があります)。プログラムはそれほどeval多くないため、インタープリターの対応する部分は大きいだけでなく、キャッシュコールドです)。

なぜevalRubyにあるのですか?ほとんどの場合「できるから」 - 実際、eval(LISP プログラミング言語用に) が発明されたとき、それは主に見せるためのものでした。さらに言えば、eval「インタープリターにインタープリターを追加」したい場合、プリプロセッサー、デバッガー、またはテンプレート エンジンの作成などのメタプログラミング タスクに使用するのは正しいことです。このようなアプリケーションの一般的なアイデアは、いくつかの Ruby コードをマッサージして呼び出すevalことです。これは、ドメイン固有のおもちゃの言語を再発明して実装することよりも確実に優れています。注意点は次のとおりです。コストに注意してください。たとえば、テンプレート エンジンの場合は、すべてのevaling を実行時ではなく起動時に行います。そしてしないでくださいevalそれを「飼いならす」方法を知らない限り、信頼できないコード、つまり、機能規律の理論に従って言語の安全なサブセットを選択して強制します。後者は非常に難しい作業です (たとえば、Java でどのように行われたかを参照してください。残念ながら、Ruby でそのような作業が行われたことは知りません)。

于 2012-08-27T10:10:56.837 に答える
11

Ruby には、以下よりも適切なギミックがいくつかありますeval()

  1. #send名前が文字列であるメソッドを呼び出して、それにパラメーターを渡すことができるものがあります。
  2. yield受信側メソッドのコンテキストで実行されるメソッドにコード ブロックを渡すことができます。
  3. 多くの場合、名前が文字列であるクラスを取得するには単純Kernel.const_get("String")で十分です。

詳しく説明できないと思いますので、ヒントだけお伝えしましたので、興味のある方はググってみてください。

于 2009-03-12T17:07:17.833 に答える
7

デバッグが困難になります。最適化が難しくなります。しかし、何よりも、それは通常、あなたがやろうとしていることを行うためのより良い方法があるという兆候です.

で何を達成しようとしているのかを教えていただければeval、特定のシナリオに関連するより適切な回答が得られる場合があります。

于 2009-03-12T05:07:11.803 に答える
6

Eval は非常に強力な機能であり、慎重に使用する必要があります。Matt J によって指摘されたセキュリティの問題に加えて、実行時に評価されたコードのデバッグが非常に難しいこともわかります。実行時に評価されるコード ブロックの問題は、インタープリターが表現するのが難しいため、それを探すのが難しくなります。

そうは言っても、その問題に慣れていて、セキュリティの問題を心配していないのであれば、Ruby を魅力的なものにしている機能の 1 つを使用することを避けるべきではありません。

于 2009-03-12T05:09:07.213 に答える
5

特定の状況では、適切な配置evalは賢く、必要なコードの量を減らします。Matt J が言及したセキュリティ上の懸念に加えて、非常に簡単な質問を 1 つ自問する必要があります。

すべてが完了したら、他の誰かがあなたのコードを読んで、あなたが何をしたかを理解できますか?

答えが「いいえ」の場合、 で得たものevalは保守性のために見捨てられます。この問題は、チームで作業している場合だけでなく、自分自身にも当てはまります。今から数年ではなくても、自分のコード月を振り返って、自分が何をしたかを知りたいのです。

于 2009-03-12T05:15:19.990 に答える
-2

「外部」から取得したものを に渡す場合は、eval何か間違ったことをしていて、非常に厄介です。コードを安全にエスケープするのは非常に難しいため、非常に安全ではないと考えています。ただし、次のコード例のように重複を避けるために eval を使用している場合は、使用しても問題ありません。

class Foo
  def self.define_getters(*symbols)
    symbols.each do |symbol|
      eval "def #{symbol}; @#{symbol}; end"
    end
  end

  define_getters :foo, :bar, :baz
end

ただし、少なくとも Ruby 1.9.1 では、Ruby には非常に強力なメタプログラミング手法があり、代わりに次のことを行うことができます。

class Foo
  def self.define_getters(*symbols)
    symbols.each do |symbol|
      define_method(symbol) { instance_variable_get(symbol) }
    end
  end

  define_getters :foo, :bar, :baz
end

ほとんどの場合、これらのメソッドを使用する必要があり、エスケープは必要ありません。

もう 1 つの悪い点evalは、(少なくとも Ruby では) インタープリターが文字列を解析してから現在のバインディング内のコードを実行する必要があるため、かなり遅いという事実です。他のメソッドは C 関数を直接呼び出すため、速度が大幅に向上するはずです。

于 2009-12-14T17:44:35.960 に答える