ほとんどの言語でバインディングがネイティブ機能ではないのはなぜですか?


11

変数を別の変数または式にバインドするIMHOは、数学の非常に一般的なシナリオです。実際、最初は、多くの学生が割り当て演算子(=)が何らかの拘束力を持つと考えています。しかし、ほとんどの言語では、バインディングはネイティブ機能としてサポートされていません。C#などの一部の言語では、いくつかの条件が満たされている場合にバインドがサポートされる場合があります。

しかし、これをネイティブ機能として実装するIMHOは、次のコードを変更するのと同じくらい簡単でした。

int a,b,sum;
sum := a + b;
a = 10;
b = 20;
a++;

これに

int a,b,sum;
a = 10;
sum = a + b;
b = 20;
sum = a + b;
a++;
sum = a + b;

右側の式に含まれる変数の値を変更するすべての命令の後に、割り当て命令としてバインディング命令を配置することを意味します。この後、冗長な命令のトリミング(またはコンパイル後のアセンブリの最適化)が行われます。

そのため、ほとんどの言語でネイティブにサポートされていない理由。特にCファミリーの言語ですか?

更新:

さまざまな意見から、この提案された「バインディング」をより正確に定義すべきだと思います。

  • これは一方向のバインディングです。合計のみがa + bにバインドされ、その逆はありません。
  • バインディングのスコープはローカルです。
  • バインドが確立されると、変更できません。つまり、合計がa + bにバインドされると、合計は常にa + bになります。

アイデアが今より明確であることを願っています。

アップデート2:

私はちょうど欲しかったこのPの#機能を。それが将来的に存在することを願っています。


14
おそらく、この機能をCに追加しようとしたコンパイラ開発者が追い詰められて撃たれたためでしょう。
ピートウィルソン

私は双方向ではなく、一方向(右から左)のバインディングについて話しています。バインディングは常に1つの変数のみに影響します。
グルシャン

2
価値のあることとして、この種のプログラムの観点は増加しています:リアクティブプログラミング。あなたが説明していることは、基本的にステロイドのデータバインディング(またはリアクティブプログラミング)であるExcelなどのスプレッドシートプログラムによっても具体化されます。
マイクローゼンブラム

11
これは、「ほとんどの」プログラミング言語の機能ではないかもしれませんが、それはあるの機能で最も人気のあるプログラミング言語:エクセル。
ヨルグW Mittag

2
式ツリーのおかげで、これは既にC#で可能です。私は昨日、それについてブログ:happynomad121.blogspot.com/2013/01/...
HappyNomad

回答:


9

あなたはプログラミングと数学を混同しています。関数型プログラミングでさえ完全な数学ではありません。たとえ多くのアイデアを取り入れて、実行してプログラミングに使用できるものに変えたとしてもです。命令型プログラミング(ほとんどのCに触発された言語、JavaScriptである顕著な例外、およびC#の最近の追加を含む)は数学とほとんど関係がないので、なぜこれらの変数は数学の変数のように振る舞うべきですか?

これは常にあなたが望むものではないことを考慮しなければなりません。ループは、ある時点での値のコピーではなく変数を保持するため、つまりfor (i = 0; i < 10; i++) { var f = function() { return i; }; /* store f */ }を返す10個のクロージャーを作成するため、ループで作成されるクロージャーに多くの人が噛まれます9。したがって、両方の方法をサポートする必要があります。つまり、「複雑さの予算」とさらにもう1つのオペレーターのコストが2倍になります。型システムが十分に洗練されていない限り(これはもっと複雑です!)、これを使用するコードとこれを使用しないコードの間の非互換性もあります。

また、これを効率的に実装することは非常に困難です。素朴な実装は、すべての割り当てに一定のオーバーヘッドを追加します。これは、命令型プログラムではすぐに追加されます。他の実装では、変数が読み取られるまで更新が遅延する場合がありますが、それは非常に複雑であり、変数が再度読み取られない場合でもオーバーヘッドがあります。十分にスマートなコンパイラーは両方を最適化できますが、十分にスマートなコンパイラーはまれであり、作成に多大な労力がかかります(特に、変数の範囲が広く、マルチスレッドが機能する場合は、例のように常に単純ではないことに注意してください!)

リアクティブプログラミングは基本的にこれについてであることに注意してください(私が知る限り)、それは存在します。従来のプログラミング言語ではそれほど一般的ではありません。そして、前の段落で挙げた実装の問題のいくつかは解決されたに違いない。


私はあなたが3つのポイントを持っていると思います-1)それは命令型スタイルのプログラミングではありません。最近では、ほとんどの言語はいくつかのパラダイムに限定されていません。私はこれがこのパラダイムスタイルの外にあるとは思わない、良いロジックです。2)複雑さ。さらに多くの複雑なものが既にサポートされていることを示しました。これはなぜですか?私は、ほとんど役に立たない演算子がサポートされていることを知っています。そして、これは全く新しい機能です。それでは、どのように互換性の問題があるのでしょうか。私は何かを変えたり、一掃したりしていません。3)ハード実装。現在のコンパイラは、これをすでに最適化する能力を持っていると思います。
グルシャン

1
@Gulshan:(1)プログラミングパラダイムは数学ではありません。FPは近いですが、FPは比較的まれであり、可変変数を持つ不純なFP言語は、これらの変数を数学からのものであるかのように扱いません。これが存在する1つのパラダイムは、リアクティブプログラミングですが、リアクティブプログラミングは(従来のプログラミング言語では)知られていないか、広く使用されていません。(2)すべてには複雑なコストと価値があります。これはかなりのコストがかかり、少数のドメインを除いてIMHOの価値が比較的低いため、これらのドメインを特にターゲットにしない限り言語に最初に追加するものではありません。

ボックスにもう1つのツールがありませんか?ツールがあれば、人々はそれを利用します。質問。CやC ++などの言語でこの機能を実装し、意見を求めたい場合は、「それを与えないでください。あなたにとっては難しすぎて、人々がこれを台無しにするからです」と言いますか?
グルシャン

@Gulshan:(3)に関して:あなたの例ほど簡単ではない(言うまでもなく、めったにありません)。グローバルスコープに入れると、突然リンク時の最適化が必要になります。次に、動的リンクを追加すると、コンパイル時にはもできなくなります。うまく機能させるには、非常にスマートなコンパイラー、JITを含む非常に賢いランタイムが必要です。また、すべての重要なプログラムでの割り当ての量を考慮してください。あなたも私もこれらのコンポーネントを書くことはできません。このテーマに興味を持ち、興味を持っている人は多くても数人しかいません。そして、彼らにはもっと良いことがあるかもしれません。

@Gulshan:信じられないほど複雑な獣C ++にすでに機能を追加しないように頼むか、システムプログラミング以外のものにCを使用しないように頼みます(これは非常に便利なドメインの1つではありません) )。それとは別に、エキサイティングな新機能がすべてですが、複雑な予算が十分に残っていて(多くの言語が常に使い果たしている)、機能が言語の目的に役立つ場合にのみ-前に言ったように、これが役立つ少数のドメインのみです。

3

ほとんどのプログラミングモデルにはあま​​り適合しません。それは一種の完全に制御されていない距離でのアクションを表し、単一の割り当てを行うことで数百または数千の変数とオブジェクトフィールドの値を破壊することができます。


次に、バインドされた変数、つまり左側が他の方法で変更されないというルールを作成することをお勧めします。そして、私が話しているのは、双方向ではなく、一方向のバインディングです。あなたが私の質問に従うなら、あなたは見ることができます。したがって、他の変数は影響を受けません。
グルシャン

それは問題ではありません。あなたが書くたびab、あなたはその一つ一つの場所への影響を検討する必要がsum使用され、あなたが読んでいることをすべての場所sumあなたは何を検討する必要があるabやっているし。自明ではない場合、特にsum実行時にバインドされる実際の式が変更される可能性がある場合、それは複雑になる可能性があります。
jprete

バインディングが完了すると、バインディング式を変更できないというルールをお勧めします。割り当てさえ不可能です。式にバインドされると、半定数のようになります。つまり、合計がa + bにバインドされると、プログラムの残りの間、常にa + bになります。
グルシャン

3

そうですね、Web2.0環境ではリアクティブプログラミングがクールかもしれないというこのしつこい直感を持っています。なんで気持ち?さて、この1ページは、ほとんどがテーブルであり、テーブルセルのonClickイベントに応答して常に変化します。また、セルのクリックは、多くの場合、列または行のすべてのセルのクラスを変更することを意味します。そしてそれは、他の関連するセルを見つけるためのgetRefToDiv()などの無限ループを意味します。

IOW、私が書いた〜3000行のJavaScriptの多くは、オブジェクトを見つけること以外何もしません。たぶん、リアクティブプログラミングはそれらすべてを低コストで実行できます。また、コード行が大幅に削減されました。

あなたはそれについてどう思いますか?はい、私のテーブルにはスプレッドシートのような機能がたくさんあることに気付きました。


3

あなたが説明しているものはスプレッドシートと呼ばれていると思います:

A1=5
B1=A1+1
A1=6

...次にB1リターンを評価します7。

編集

C言語は、「ポータブルアセンブリ」と呼ばれることもあります。これは必須の言語ですが、スプレッドシートなどは宣言型の言語です。あなたが変わるときに再評価することを言っB1=A1+1て期待B1することA1は間違いなく宣言的です。(機能言語がサブセットである)宣言型言語は、ハードウェアの動作方法から遠く離れているため、一般に高レベル言語と見なされます。

関連する注意事項として、ラダーロジックのような自動化言語は通常宣言的です。あなたが言うロジックのラング書けばoutput A = input B OR input Cそれが起こって常にその文を再評価し、Aいつでも変更することができますBまたはC変更を。関数ブロック図(Simulinkを使用したことがある場合はおなじみでしょう)などの他の自動化言語も宣言型であり、継続的に実行されます。

一部の(組み込み)自動化機器はCでプログラムされており、リアルタイムシステムの場合、ラダーロジックの実行方法と同様に、ロジックを繰り返し再実行する無限ループが存在する可能性があります。その場合、メインループ内で次のように記述できます。

A = B || C;

...また、常に実行されているため、宣言型になります。 A常に再評価されます。


3

C、C ++、Objective-C:

ブロックは、探しているバインディング機能を提供します。

あなたの例では:

sum:= a + b;

and は既存の変数であるコンテキストでsuma + bに設定しています。C、C ++、またはAppleの拡張機能を備えたObjective-Cの「ブロック」(クロージャー、別名ラムダ式)で正確にそれを行うことができます(pdf):ab

__block int a = 0, b = 0;           // declare a and b
int (^sum)(void);                   // declare sum
sum = ^(void){return a + b;};       // sum := a + b

これはsum、aとbの合計を返すブロックに設定します。__blockストレージクラス指定子は、ことを示しているab変更されることがあります。上記を考えると、次のコードを実行できます。

printf("a=%d\t b=%d\t sum=%d\n", a, b, sum());
a = 10;
printf("a=%d\t b=%d\t sum=%d\n", a, b, sum());
b = 32;
printf("a=%d\t b=%d\t sum=%d\n", a, b, sum());
a++;
printf("a=%d\t b=%d\t sum=%d\n", a, b, sum());

出力を取得します。

a=0      b=0     sum=0
a=10     b=0     sum=10
a=10     b=32    sum=42
a=11     b=32    sum=43

ブロックの使用と、提案する「バインディング」の唯一の違いは、の空の括弧のペアですsum()。差sumとは、sum()発現およびその発現の結果との間の差です。関数と同様に、括弧は空である必要はありません。ブロックは関数と同じようにパラメータを取ることができます。


2

C ++

汎用に更新されました。戻り値および入力タイプでパラメーター化されます。パラメーター化された型を満たす任意の二項演算を提供できます。コードはオンデマンドで結果を計算します。結果を回避できる場合は、結果を再計算しないようにします。これが望ましくない場合は、これを取り除いてください(何らかの理由で、含まれているオブジェクトが大きいため、副作用のため)。

#include <iostream>

template <class R, class A, class B>
class Binding {
public:
    typedef R (*BinOp)(A, B);
    Binding (A &x, B &y, BinOp op)
        : op(op)
        , rx(x)
        , ry(y)
        , useCache(false)
    {}
    R value () const {
        if (useCache && x == rx && y == ry) {
            return cache;
        }
        x = rx;
        y = ry;
        cache = op(x, y);
        useCache = true;
        return cache;
    }
    operator R () const {
        return value();
    }
private:
    BinOp op;
    A &rx;
    B &ry;
    mutable A x;
    mutable B y;
    mutable R cache;
    mutable bool useCache;
};

int add (int x, int y) {
    return x + y;
}

int main () {
    int x = 1;
    int y = 2;
    Binding<int, int, int> z(x, y, add);
    x += 55;
    y *= x;
    std::cout << (int)z;
    return 0;
}

質問には答えませんが、あなたが提示したアイデアは気に入っています。より一般的なバージョンはありますか?
グルシャン

@Gulshan:更新
トーマス・エディング
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.