「Eval」の版間の差分
→{{lang|en|LISP}}: 解消済み仮リンク REPL の内部リンク化 |
|||
14行目: | 14行目: | ||
例えば、evalは簡易[[テンプレートエンジン]]として使われることがある。[[PHP (プログラミング言語)|PHP]]での例を示す。 |
例えば、evalは簡易[[テンプレートエンジン]]として使われることがある。[[PHP (プログラミング言語)|PHP]]での例を示す。 |
||
< |
<syntaxhighlight lang="php"> |
||
$name = 'John Doe'; |
$name = 'John Doe'; |
||
$greeting = 'Hello'; |
$greeting = 'Hello'; |
||
$template = '"$greeting, $name! How can I help you today?"'; |
$template = '"$greeting, $name! How can I help you today?"'; |
||
print eval("return $template;") |
print eval("return $template;") |
||
</syntaxhighlight> |
|||
</source> |
|||
この例は期待した動作をするが、前述のようにセキュリティ上の問題を抱えており、他の方法よりもはるかに遅い。より高速で安全な方法は単に <code>"$name"</code> という文字列を <code>$name</code> の値で置換することである。 |
この例は期待した動作をするが、前述のようにセキュリティ上の問題を抱えており、他の方法よりもはるかに遅い。より高速で安全な方法は単に <code>"$name"</code> という文字列を <code>$name</code> の値で置換することである。 |
||
36行目: | 36行目: | ||
式評価器としての例: |
式評価器としての例: |
||
< |
<syntaxhighlight lang="javascript"> |
||
foo = 2; |
foo = 2; |
||
alert(eval('foo + 2')); |
alert(eval('foo + 2')); |
||
</syntaxhighlight> |
|||
</source> |
|||
文実行器としての例: |
文実行器としての例: |
||
< |
<syntaxhighlight lang="javascript"> |
||
foo = 2; |
foo = 2; |
||
eval('foo = foo + 2;alert(foo);'); |
eval('foo = foo + 2;alert(foo);'); |
||
</syntaxhighlight> |
|||
</source> |
|||
JavaScriptでのevalの使用の一例は[[Ajax]]などにおける[[JavaScript Object Notation|JSON]]のパースである。しかし、現在は多くのブラウザでより特化したJSON.parseが定義されている。 |
JavaScriptでのevalの使用の一例は[[Ajax]]などにおける[[JavaScript Object Notation|JSON]]のパースである。しかし、現在は多くのブラウザでより特化したJSON.parseが定義されている。 |
||
57行目: | 57行目: | ||
[[Perl]]のevalは、文字列をプログラムとして解釈するほか、例外処理機構としても機能する。コードの塊をテストし、必要ならそれに関して警告することができる。例えば、除算において実行時エラーが発生すると<code>$@</code>で警告を出力することができる。 |
[[Perl]]のevalは、文字列をプログラムとして解釈するほか、例外処理機構としても機能する。コードの塊をテストし、必要ならそれに関して警告することができる。例えば、除算において実行時エラーが発生すると<code>$@</code>で警告を出力することができる。 |
||
< |
<syntaxhighlight lang="perl"> |
||
# 0による除算の例外を回避する |
# 0による除算の例外を回避する |
||
eval { $answer = $x / $y; }; |
eval { $answer = $x / $y; }; |
||
warn $@ if $@; |
warn $@ if $@; |
||
</syntaxhighlight> |
|||
</source> |
|||
=== PHP === |
=== PHP === |
||
67行目: | 67行目: | ||
echoを使った例: |
echoを使った例: |
||
< |
<syntaxhighlight lang="php"> |
||
<?php |
<?php |
||
$foo = "Hello, world!\n"; |
$foo = "Hello, world!\n"; |
||
eval('echo $foo;'); |
eval('echo $foo;'); |
||
?> |
?> |
||
</syntaxhighlight> |
|||
</source> |
|||
値を返す例: |
値を返す例: |
||
< |
<syntaxhighlight lang="php"> |
||
<?php |
<?php |
||
$foo = "Goodbye, world!\n"; |
$foo = "Goodbye, world!\n"; |
||
echo eval('return $foo;'); |
echo eval('return $foo;'); |
||
?> |
?> |
||
</syntaxhighlight> |
|||
</source> |
|||
=== PostScript === |
=== PostScript === |
||
93行目: | 93行目: | ||
<code>eval</code> の例(対話モード): |
<code>eval</code> の例(対話モード): |
||
< |
<syntaxhighlight lang="python"> |
||
>>> x = 1 |
>>> x = 1 |
||
>>> eval('x + 1') |
>>> eval('x + 1') |
||
99行目: | 99行目: | ||
>>> eval('x') |
>>> eval('x') |
||
1 |
1 |
||
</syntaxhighlight> |
|||
</source> |
|||
<code>exec</code> の例(対話モード): |
<code>exec</code> の例(対話モード): |
||
< |
<syntaxhighlight lang="python"> |
||
>>> x = 1 |
>>> x = 1 |
||
>>> y = 1 |
>>> y = 1 |
||
110行目: | 110行目: | ||
>>> y |
>>> y |
||
0 |
0 |
||
</syntaxhighlight> |
|||
</source> |
|||
=== ColdFusion === |
=== ColdFusion === |
||
[[ColdFusion]]の <code>evaluate</code> 関数は文字列で与えられた式を実行時に評価することができる。 |
[[ColdFusion]]の <code>evaluate</code> 関数は文字列で与えられた式を実行時に評価することができる。 |
||
< |
<syntaxhighlight lang="cfm"> |
||
<cfset x = "int(1+1)"> |
<cfset x = "int(1+1)"> |
||
<cfset y = Evaluate(x)> |
<cfset y = Evaluate(x)> |
||
</syntaxhighlight> |
|||
</source> |
|||
これは読み込む変数をプログラム的に選択するときなどに、特に便利である。 |
これは読み込む変数をプログラム的に選択するときなどに、特に便利である。 |
||
< |
<syntaxhighlight lang="cfm"> |
||
<cfset x = Evaluate("queryname.#columnname#[rownumber]")> |
<cfset x = Evaluate("queryname.#columnname#[rownumber]")> |
||
</syntaxhighlight> |
|||
</source> |
|||
=== Ruby === |
=== Ruby === |
||
128行目: | 128行目: | ||
<code>eval</code>の例(メソッドfooのコンテキストで評価している): |
<code>eval</code>の例(メソッドfooのコンテキストで評価している): |
||
< |
<syntaxhighlight lang="ruby"> |
||
def foo |
def foo |
||
x = 1 |
x = 1 |
||
135行目: | 135行目: | ||
eval('x + 1', foo) #=> 2 |
eval('x + 1', foo) #=> 2 |
||
</syntaxhighlight> |
|||
</source> |
|||
<code>instance_eval</code> と <code>module_eval</code> の例: |
<code>instance_eval</code> と <code>module_eval</code> の例: |
||
< |
<syntaxhighlight lang="ruby"> |
||
class Bar |
class Bar |
||
@@cvar = 10 #クラス変数 |
@@cvar = 10 #クラス変数 |
||
149行目: | 149行目: | ||
bar.instance_eval('@ivar') #=> 5 |
bar.instance_eval('@ivar') #=> 5 |
||
Bar.class_eval('@@cvar') #=> 10 |
Bar.class_eval('@@cvar') #=> 10 |
||
</syntaxhighlight> |
|||
</source> |
|||
== 脚注 == |
== 脚注 == |
2020年7月5日 (日) 22:49時点における版
eval(イーバル)はいくつかのプログラミング言語が持つ、文字列を式として評価する関数、または複数の文をプログラム中のあるコンテキストで実行するサブルーチンである。
evalの類の機能はコンパイラ言語よりもインタプリタ言語でより一般的である。なぜならコンパイラ言語でこのような機能を実現するには、プログラム自体に言語処理系や(変数名などの)実行時情報を埋め込む必要があるからである。evalに近い機能を実現しているコンパイラ言語も存在する。
セキュリティ上のリスク
信頼されないソースからのデータをevalするときには特に注意が必要である。例としてインターネット上からデータを得る get_data()
関数を考える。次の擬似コードのようなプログラムは潜在的に危険である。
data = get_data() foo = eval(data)
攻撃者がこのプログラムに例えば "delete_system_files()"
という文字列を与えることができると、delete_system_files()
関数が実行されてしまい、重要なファイルが消されてしまうかもしれない。これを防ぐためには、evalされる文字列はすべてエスケープしたり、潜在的に危険な機能を利用できないようにして実行するなどの対策が必要となる。プログラミング言語によっては、外部から入力されたデータを「汚染されている」として印をつけるものもある。
適切な使用
evalは非常に強力なため、経験の浅いプログラマは何でもevalを使って済ませてしまうことがある。たいてい、そのような場合には専用のより良い選択肢が存在し、コードのパースにかかる時間が節約できる。
例えば、evalは簡易テンプレートエンジンとして使われることがある。PHPでの例を示す。
$name = 'John Doe';
$greeting = 'Hello';
$template = '"$greeting, $name! How can I help you today?"';
print eval("return $template;")
この例は期待した動作をするが、前述のようにセキュリティ上の問題を抱えており、他の方法よりもはるかに遅い。より高速で安全な方法は単に "$name"
という文字列を $name
の値で置換することである。
eval は表計算ソフトなどの数式を評価する必要のあるアプリケーションで使われることがある。これは数式のパーサを自作するよりも手軽だが、自作や既存の専用のパーサを利用するほうがより良い。前述の問題点に加え、言語組み込みのevalはアプリケーション用にカスタマイズできないからである。
おそらく、evalの最も優れた使い道は(LISPなどでの)処理系のブートストラップや、言語の対話的な実行環境でユーザが書いたプログラムを実行することであろう。
実装
インタプリタ言語ではevalは通常のコードと同じインタプリタで実装されるのがほとんどである。
コンパイラ言語ではevalを実装するために通常のコンパイラをプログラムに埋め込むこともある。また特別のインタプリタを使うこともあり、その場合はコードの重複が問題となる。
実例
JavaScript, ActionScript
JavaScriptやActionScriptにおいては、evalは式の評価器と文の実行器のハイブリッドのような存在である。evalは最後に評価された式の値を返し(JavaScriptとActionScriptではすべての文は式である)、最後のセミコロンは省くことができる。
式評価器としての例:
foo = 2;
alert(eval('foo + 2'));
文実行器としての例:
foo = 2;
eval('foo = foo + 2;alert(foo);');
JavaScriptでのevalの使用の一例はAjaxなどにおけるJSONのパースである。しかし、現在は多くのブラウザでより特化したJSON.parseが定義されている。
ActionScriptではevalを任意の式を評価するために使うことはできない。Flash 8のドキュメントによれば、その使用は「変数、プロパティ、オブジェクト、ムービークリップの名前」を表す式に限られ、「パラメータはStringまたはオブジェクトのインスタンスへの直接の参照のどちらでもよい」[1]。
LISP
LISPはevalが最初に登場した言語である。evalの実装によって、最初のLISPインタプリタが現れたのである。それ以前は、LISPの式はコンパイルされていた。しかし一度evalが実装されると、それは単純な入力・評価・出力のループ (REPL) の一部として使われるようになり、最初のLISPインタプリタの基礎を形作った。LISPの後のバージョンのevalはコンパイラとしても実装されている。
Perl
Perlのevalは、文字列をプログラムとして解釈するほか、例外処理機構としても機能する。コードの塊をテストし、必要ならそれに関して警告することができる。例えば、除算において実行時エラーが発生すると$@
で警告を出力することができる。
# 0による除算の例外を回避する
eval { $answer = $x / $y; };
warn $@ if $@;
PHP
PHPでのevalは、ほぼ、文字列中のコードをまるで eval()
呼び出しの代わりにファイル中に記述されているかのように実行する。例外は、エラーは eval()
から復帰した時点で報告され、またreturn文で eval()
の戻り値を返すことができる点である。
echoを使った例:
<?php
$foo = "Hello, world!\n";
eval('echo $foo;');
?>
値を返す例:
<?php
$foo = "Goodbye, world!\n";
echo eval('return $foo;');
?>
PostScript
PostScriptのexec
演算子はオペランドを1つ取り、それが単純なリテラルならスタックにプッシュし返す。PostScriptの式を含む文字列が来た場合は、その文字列をインタプリタによって実行可能な形式に変換する。例えば、
((Hello World) =) cvx exec
は PostScript の式である (Hello World) =
(文字列 Hello World をスタックからポップして画面に表示する)を実行可能な型を持つように変換し、そして実行する。
PostScriptでは run
演算子も似た機能を持つが、代わりにインタプリタそのものがファイル中のPostScriptの式を評価する。
Python
Pythonでの eval
関数は単一の式を評価する。文を実行することはできないが、文を実行する関数を実行することはできる。exec
文は文を実行することができる。
eval
の例(対話モード):
>>> x = 1
>>> eval('x + 1')
2
>>> eval('x')
1
exec
の例(対話モード):
>>> x = 1
>>> y = 1
>>> exec "x += 1; y -= 1"
>>> x
2
>>> y
0
ColdFusion
ColdFusionの evaluate
関数は文字列で与えられた式を実行時に評価することができる。
<cfset x = "int(1+1)">
<cfset y = Evaluate(x)>
これは読み込む変数をプログラム的に選択するときなどに、特に便利である。
<cfset x = Evaluate("queryname.#columnname#[rownumber]")>
Ruby
Rubyには式を評価するコンテキストごとに3種類のevalが存在する。eval
はその場またはProcやBindingオブジェクト、instance_eval
はインスタンス、module_eval
(別名はclass_eval
)はモジュールかクラス、それぞれのコンテキストで評価を行う。なお、評価コンテキスト変更のためにinstance_eval
やmodule_eval
を使う場合、文字列でなくブロックを引数とすることができる[2]。
eval
の例(メソッドfooのコンテキストで評価している):
def foo
x = 1
binding
end
eval('x + 1', foo) #=> 2
instance_eval
と module_eval
の例:
class Bar
@@cvar = 10 #クラス変数
def initialize
@ivar = 5 #インスタンス変数
end
end
bar = Bar.new
bar.instance_eval('@ivar') #=> 5
Bar.class_eval('@@cvar') #=> 10
脚注
- ^ Adobe - Flash 8 LiveDocs
- ^ instance method BasicObject#instance_eval - Ruby1.9.3リファレンスマニュアル