web-dev-qa-db-ja.com

なぜ変数型の「演算子」を持つ言語がそれほど存在しないのですか?

私はそれをこのように意味します:

<?php
    $number1 = 5;   // (Type 'Int')
    $operator1 = +; // (Type non-existent 'Operator')
    $number2 = 5;   // (Type 'Int')
    $operator2 = *; // (Type non-existent 'Operator')
    $number3 = 8;   // (Type 'Int')

    $test = $number1 $operator1 $number2 $operator2 $number3; //5 + 5 * 8.

    var_dump($test);
?>

しかし、この方法でも:

<?php
    $number1 = 5;
    $number3 = 9;
    $operator1 = <;

    if ($number1 $operator1 $number3) { //5 < 9 (true)
        echo 'true';
    }
?>

どの言語にもこれがないようです-彼らがそうしない理由はありますか?

45
kgongonowdoe

演算子はおかしな名前の関数であり、特別な構文があります。

C++やPythonのようにさまざまな言語で、クラスの特別なメソッドをオーバーライドすることで演算子を再定義できます。次に、標準演算子(例:+)は、指定したロジック(例:文字列の連結や行列の追加など)に従って動作します。

このような演算子を定義する関数は単なるメソッドなので、関数と同じように渡すことができます。

# python
action = int.__add__
result = action(3, 5)
assert result == 8

他の言語では、新しい演算子を関数として直接定義し、それらを中置形式で使用できます。

-- haskell
plus a b = a + b  -- a normal function
3 `plus` 5 == 8 -- True

(+++) a b = a + b  -- a funny name made of non-letters
3 +++ 5 == 8 -- True

let action = (+)
1 `action` 3 == 4 -- True

残念ながら、PHPがそのようなものをサポートしているかどうか、そしてそれをサポートすることが良いかどうかはわかりません。プレーンな関数を使用すると、$foo $operator $barよりも読みやすくなります。

103
9000

なんらかの メタプログラミング を許可する言語はたくさんあります。特に、 [〜#〜] lisp [〜#〜] 言語ファミリについて話している答えが見つからないことに驚いています。

ウィキペディアから:

メタプログラミングとは、プログラムをデータとして扱う機能を備えたコンピュータープログラムの記述です。

本文の後半:

LISPは、歴史的な優先順位と、メタプログラミングの単純さとパワーの両方の理由から、おそらくメタプログラミング機能を備えた典型的な言語です。

LISP言語

LISPの簡単な紹介を以下に示します。

コードを確認する方法の1つは、一連の手順として実行することです。これを実行し、次にそれを実行してから、他のことを実行してください...これはリストです!プログラムが行うことのリスト。そしてもちろん、リスト内にリストを入れてループなどを表すこともできます。

要素a、b、c、dを含むリストを次のように表すと、(abcd)LISP関数呼び出しのようになります。ここで、aは関数、bです。 、cdは引数です。事実、典型的な「Hello World!」プログラムは次のように書くことができます:_(println "Hello World!")_

もちろん、bcまたはdは、何かに評価されるリストでもかまいません。次に、_(println "I can add :" (+ 1 3) )_は、 "" I can add:4 "と出力します。

したがって、プログラムはネストされたリストの連続であり、最初の要素は関数です。良いニュースは、リストを操作できることです!したがって、プログラミング言語を操作できます。

LISPの利点

Lispは、プログラミング言語を作成するためのツールキットほど多くのプログラミング言語ではありません。プログラム可能なプログラミング言語。

これは、Lispで新しい演算子を作成するのがはるかに簡単なだけでなく、引数が関数に渡されるときに評価されるため、他の言語で一部の演算子を記述することもほぼ不可能です。

たとえばC言語のような言語で、if演算子を自分で記述したいとします。

my-if(condition, if-true, if-false)

my-if(false, print("I should not be printed"), print("I should be printed"))

この場合、引数の評価の順序に依存する順序で、両方の引数が評価および出力されます。

Lispでは、演算子(マクロと呼びます)の記述と関数の記述はほぼ同じであり、同じように使用されます。主な違いは、マクロへのパラメーターがマクロに引数として渡される前に評価されないことです。これは、上記のifなどの一部の演算子を記述できるようにするために不可欠です。

実際の言語

正確に示すことはここでは少し範囲外ですが、1つのLISPでプログラミングを試して詳細を学ぶことをお勧めします。たとえば、次の内容を確認できます。

  • Scheme 、小さなコアを持つ古い、かなり「純粋な」LISP
  • Common LISP、十分に統合されたオブジェクトシステムを備えたより大きなLISP、および多くの実装(ANSI標準化)
  • ラケット 型付きLISP
  • Clojure 私のお気に入り、上記の例はClojureコードです。 JVM上で実行される最新のLISP。 [〜#〜] so [〜#〜] にもClojureマクロの例がいくつかあります(ただし、これは適切な出発点ではありません。 4clojure を見てみます。 =、 braveclojure または clojure koans 最初)))。

ちなみに、LISPはLISt処理を意味します。

例について

以下のClojureを使用した例を示します。

Clojure _(defn add [a b] ...your-implementation-here... )_でadd関数を記述できる場合、_+_のように_(defn + [a b] ...your-implementation-here... )_という名前を付けることができます。これは、実際には 実際の実装 で行われます(関数の本体は少し複雑ですが、定義は基本的に上記で記述したものと同じです)。

インフィックス表記についてはどうですか? Clojureはprefix(またはポーランド語)表記を使用しているため、接頭辞付きのコードをClojureコードに変換する_infix-to-prefix_マクロを作成できます。これは実際には驚くほど簡単です(これは実際には マクロ演習 の1つであり、clojureの公用語です)!それはまた、例えば Incanter _$=_ macro を参照してください。

これは説明されたkoansからの最も単純なバージョンです:

_(defmacro infix [form]
  (list (second form) (first form) (nth form 2)))

;; takes a form (ie. some code) as parameter
;; and returns a list (ie. some other code)
;; where the first element is the second element from the original form
;; and the second element is the first element from the original form
;; and the third element is the third element from the original form (indexes start at 0)
;; example :
;; (infix (9 + 1))
;; will become (+ 9 1) which is valid Clojure code and will be executed to give 10 as a result
_

さらに重要な点として、いくつかの LISP引用

「LISPを際立たせているのは、LISPが進化するように設計されていることです。 LISPを使用して、新しいLISP演算子を定義できます。新しい抽象化が普及するにつれて(オブジェクト指向プログラミングなど)、LISPでの実装が常に容易であることが常にわかります。 DNAのように、そのような言語は時代遅れにならない。」

— Paul Graham、ANSI Common LISP

「LISPでのプログラミングは、宇宙の原始的な力で遊ぶようなものです。指先の間が稲妻のようです。他の言語は親近感さえありません。」

— Glenn Ehrlich、LISPへの道

16
nha

$test = $number1 $operator1 $number2 $operator2 $number3;

ほとんどの言語実装には、パーサーがコードを分析し、そこからツリーを構築するステップがあります。たとえば、式5 + 5 * 8は次のように解析されます

  +
 / \
5   *
   / \
  8   8

優先順位に関するコンパイラの知識に感謝します。演算子の代わりに変数を指定した場合、コードを実行する前に操作の適切な順序がわかりません。ほとんどの実装では深刻な問題になるため、ほとんどの言語ではそれを許可していません。

もちろん、パーサーが上記を一連の式および演算子として解析し、実行時にソートおよび評価される言語を想像することもできます。たぶん、これに対するアプリケーションはあまりありません。

多くのスクリプト言語では、実行時に任意の式(または expr の場合は少なくとも任意の算術式)を評価できます。そこでは、数値と演算子を1つの式に組み合わせて、言語にそれを評価させることができます。 PHP(およびその他多数))では、その関数は eval と呼ばれます。

$test = eval("$number1 $operator1 $number2 $operator2 $number3");

コンパイル時にコードを生成できる言語もあります。 Dでのミックスイン式 が頭に浮かびます。

test = mixin("number1 " + operator1 + " number2 " + operator2 + "number3");

ここで、operator1およびoperator2は、コンパイル時に既知の文字列定数でなければなりません。テンプレートパラメータ。 number1number2およびnumber3は、通常のランタイム変数として残されました。

他の回答では、言語に応じて、演算子と関数が多かれ少なかれ同じものになるさまざまな方法がすでに説明されています。しかし、通常、+のような組み込みのインフィックス演算子記号とoperator1のような名前付き呼び出し可能オブジェクトの間には構文上の違いがあります。詳細は他の回答にお任せします。

9
MvG

ALGOL 68はまさにその特徴を持っていました。 ALGOL 68の例は次のようになります。

intnumber1= 5; ¢(Type 'Int')¢
opoperator1=intintaba+b; ¢(タイプが存在しない「演算子」)¢
priooperator1= 4;
intnumber2= 5; ¢(Type 'Int')¢
opoperator2=intintaba*b; ¢(タイプが存在しない「演算子」)¢
priooperator2= 5;
intnumber3= 8; ¢(Type 'Int')¢

inttest=number1operator1number2operator2number3; ¢5 + 5 * 8.¢

var_dumptest);

2番目の例は次のようになります。

intnumber4= 9;
opoperator3=boolintaba<b;
priooperator3= 3;
ifnumber1$ operator3number4then¢5 <9(true)¢
print(true
fi

演算子記号が定義され、目的の操作を含むメソッド本体が割り当てられていることに注意してください。演算子とそのオペランドはすべて型指定されており、演算子に優先順位を割り当てることができるため、正しい順序で評価が行われます。また、演算子記号と変数記号のフォントには若干の違いがあることに気付くかもしれません。

実際、言語はフォントを使用して書かれていますが、その日のマシンはフォント(紙テープやパンチカード)を処理できず、 stropping が使用されていました。プログラムはおそらく次のように入力されます。

'INT' NUMBER4 = 9;
'OP' 'OPERATOR3' = 'BOOL' ('INT' A,B) A < B;
'PRIO' 'OPERATOR3' = 3;
'IF' NUMBER1 'OPERATOR3' NUMBER4 'THEN' 'C' 5 < 9 'C'
PRINT('TRUE')
'FI'

また、何年も前に私が一度利用したオペレーターのシンボルを独自に定義できる場合は、その言語で面白いゲームをプレイすることもできます... [2]。


参照:

[1] 非公式なALGOL 68の紹介C.H.リンジーとS.G.ファンデルミューレン、北ホラント、1971年

[2] ALGOL 68フレーズ、ALGOL 68でのコンパイラ作成を支援するツール、BCトンプセット、ALGOL 68のアプリケーションに関する国際会議、イーストアングリア大学、イギリス、ノリッジ、1976年