21

一部の人々は、すべてのプログラミング言語には、その目的を達成するために使用できる「複雑さの予算」があると言います。しかし、複雑さの予算が使い果たされると、すべての小さな変更はますます複雑になり、下位互換性のある方法で実装するのが難しくなります。

2010年8月からLambdaの現在の暫定構文(≙Lambda式、例外の透過性、ディフェンダーメソッド、メソッド参照)を読んだ後、Oracleの人々はそのような変更を検討するときにJavaの複雑さの予算を完全に無視したのではないかと思います。

これらは私が考えている質問です-それらのいくつかは一般的な言語デザインについてです:

  • 提案された追加は、他の言語が選択したアプローチと複雑さが同等ですか?
  • 言語にそのような追加を追加し、実装の複雑さから開発者を保護することは一般的に可能ですか?
  • これらの追加は、Java-as-a-languageの進化の終わりに到達した兆候ですか、それとも、巨大な歴史を持つ言語を変更するときにこれが予想されますか?
  • 言語進化のこの時点で、他の言語はまったく異なるアプローチを取っていますか?

ありがとう!

4

5 に答える 5

3

私はJava7ラムダ提案のプロセスと進化をたどっていません。最新の提案の文言が何であるかさえわかりません。これを真実の陳述ではなく、暴言/意見と考えてください。また、私は何年もの間Javaを使用していないので、構文が錆びていて、場所によっては正しくない可能性があります。

まず、Java言語のラムダとは何ですか?シンタックスシュガー。一般に、ラムダを使用すると、コードで小さな関数オブジェクトを適切に作成できますが、そのサポートは、内部クラスを使用することで、Java言語ですでにある程度事前設定されています。

では、ラムダの構文はどれほど優れているのでしょうか?以前の言語構成をどこで上回っていますか?どこがいいのでしょうか?

手始めに、ラムダ関数に使用できる構文が2つあるという事実は嫌いです(ただし、これはC#の行に含まれるため、私の意見は広まっていないと思います。シュガーコートが必要な #(int x)(x*x)場合は#(int x){ return x*x; }、二重構文は他に何も追加しません。私は2番目の構文を好みました。これは、書き込みの追加コストと短いバージョンでより一般的returnです ;

本当に便利なように、ラムダは変数が定義されているスコープとクロージャから変数を取得できます。内部クラスと一貫性があるため、ラムダは「効果的に最終的な」変数のキャプチャに制限されます。言語の以前の機能との一貫性は素晴らしい機能ですが、甘さのために、再割り当てできる変数をキャプチャできると便利です。その目的のために、彼らは、コンテキストに存在し、注釈が付けられた変数が参照によって@Sharedキャプチャされることを考慮しています、割り当てを許可します。ラムダが変数を使用する方法は、ラムダが定義されている場所ではなく、変数の宣言の場所で決定されるため、これは奇妙に思えます。1つの変数を複数のラムダで使用できます。これにより、すべてのラムダで同じ動作が強制されます。

ラムダは実際の関数オブジェクトをシミュレートしようとしますが、提案は完全には実現しません。これまで、識別子は一貫性が保たれているオブジェクトまたはメソッドのいずれかを示し、ラムダを呼び出すには!後を使用する必要があるため、パーサーを単純に保つためです。ラムダ名:#(int x)(x*x)!(5)を返し25ます。これにより、言語の他の部分とは異なるラムダに使用する新しい構文がもたらされます。これは 、仮想ジェネリックインターフェイスの同義語として何とか!立っていますが、完全にしないのはなぜですか?.executeLambda<Result,Args...>

新しい汎用(仮想)インターフェイスLambdaを作成できます。インターフェイスは実際のインターフェイスではなく、そのようなファミリであるため、仮想である必要があります:、、 Lambda<Return>...単一の実行メソッドを定義できます。これは、C ++のようになりたいのですが、それが負担になる場合は、メソッド実行のショートカットとしてを使用して、他の名前でもかまいません。Lambda<Return,Arg1>Lambda<Return,Arg1,Arg2>operator()!

 interface Lambda<R> {
    R exec();
 }
 interface Lambda<R,A> {
    R exec( A a );
 }

identifier!(args)次に、コンパイラはに変換する だけidentifier.exec( args )で済みます。これは簡単です。ラムダ構文の変換では、コンパイラーが実装されている適切なインターフェースを識別する必要があり、次のように照合できます。

 #( int x )(x *x)
 // translated to
 new Lambda<int,int>{ int exec( int x ) { return x*x; } }

これにより、ユーザーは、より複雑な状況でラムダとして使用できる内部クラスを定義することもできます。たとえば、ラムダ関数が@Shared読み取り専用の方法で注釈が付けられた変数をキャプチャする必要がある場合、またはキャプチャされたオブジェクトの状態をキャプチャの場所で維持する必要がある場合、Lambdaの手動実装が利用可能になります。

 new Lambda<int,int>{ int value = context_value;
     int exec( int x ) { return x * context_value; }
 };

現在の内部クラスの定義と同様の方法で、したがって現在のJavaユーザーにとって自然な方法です。これは、たとえば、乗数ラムダを生成するためのループで使用できます。

 Lambda<int,int> array[10] = new Lambda<int,int>[10]();
 for (int i = 0; i < 10; ++i ) {
    array[i] = new Lambda<int,int>{ final int multiplier = i;
       int exec( int x ) { return x * multiplier; }
    };
 }
 // note this is disallowed in the current proposal, as `i` is
 // not effectively final and as such cannot be 'captured'. Also
 // if `i` was marked @Shared, then all the lambdas would share
 // the same `i` as the loop and thus would produce the same
 // result: multiply by 10 --probably quite unexpectedly.
 //
 // I am aware that this can be rewritten as:
 // for (int ii = 0; ii < 10; ++ii ) { final int i = ii; ...
 //
 // but that is not simplifying the system, just pushing the
 // complexity outside of the lambda.

これにより、ラムダとラムダを受け入れるメソッドの両方を新しい単純な構文で使用できるようになります。#(int x){ return x*x; }または、砂糖コーティングが意図したセマンティクスに干渉する特定の場合のより複雑な手動アプローチを使用できます。

全体として、ラムダの提案はさまざまな方向に改善できると思います。シンタックスシュガーを追加する方法は、リークのある抽象化であり(ラムダに固有の問題を外部で処理しています)、下位レベルのインターフェイスを提供しないことで、単純なユースケースに完全に適合しないユースケースでは、ユーザーコードが読みにくくなります。:

于 2010-08-16T10:17:18.393 に答える
3

いくつかのスコープ明確化構造をモジュロすると、これらのメソッドのほとんどすべてが、ラムダ抽象化の実際の定義から導かれます。

λx.E

質問に順番に答えるには:

Java コミュニティによる提案が他の何よりも優れている、または劣っている特定の事柄はないと思います。私が言ったように、それは数学的な定義に従うので、すべての忠実な実装はほぼ同じ形になります。

命令型言語にボルトで固定された匿名のファーストクラス関数は、一部のプログラマーが気に入って頻繁に使用する機能として終わる傾向があり、他のプログラマーは完全に無視する傾向があります-したがって、ある種の人々を混乱させない構文を与えることはおそらく賢明な選択ですこの特定の言語機能の存在を無視することを選択した人。実装の複雑さと詳細を隠すことは、Java とうまく調和する構文を使用して彼らがやろうとしたことだと思いますが、Java プログラマーにとっては実際の意味合いはありません。

おそらく、既存の定義を複雑にしない構文を使用することが望ましいため、演算子などとして使用するために選択できる記号がわずかに制限されます。確かに Java は下位互換性を維持することを主張しているため、言語の進化がわずかに制限されていますが、これは必ずしも悪いことではないと思います。PHP のアプローチは対極にあります (つまり、「新しいポイント リリースがあるたびに、すべてを壊しましょう!」)。Java の進化が本質的に制限されているとは思いませんが、Java の設計の基本的な信条の一部 (たとえば、OOP 原則への準拠、VM ベースなど) を除いてはそうではありません。

Java の観点から言語の進化について強い意見を述べるのは非常に難しいと思います。かなりユニークな位置にあります。一つには、非常に人気がありますが、比較的古いものです。Microsoft は、「C#」と呼ばれる言語の設計を開始することさえ決定する前に、少なくとも 10 年間に相当する Java レガシーの恩恵を受けていました。C プログラミング言語は、基本的にまったく進化を止めました。C++ には、主流に受け入れられるほどの重要な変更はほとんどありません。Java は、ゆっくりではあるが一貫したプロセスで進化を続けてきました。どちらかといえば、同様に巨大なコード ベースがインストールされている他のどの言語よりも、進化し続けるための準備が整っていると思います。

于 2010-08-13T10:54:50.250 に答える
1

おそらくこれはあなたの質問に対する答えではないかもしれませんが、これは object-c (もちろん Java とは対照的に非常に狭いユーザーベースを持っています) がブロック () によって拡張された方法に匹敵するかもしれません。構文は言語の残りの部分 (IMHO) に適合しませんが、これは便利な追加であり、言語機能の観点から追加された複雑さは、たとえば、並行プログラミングの複雑さを軽減することで報われます (配列に対する並行反復のような単純なものまたはグランドセントラルディスパッチのような複雑なテクニック)。

さらに、ブロックを使用すると、多くの一般的なタスクが簡単になります。たとえば、1 つのオブジェクトを同じクラスの複数のインスタンスのデリゲート (Java 用語では「リスナー」) にすることができます。Java では、その目的のために匿名クラスを既に使用できるため、プログラマーは概念を知っており、ラムダ式を使用してソース コードの数行を節約することができます。

Objective-C (または Cocoa/Cocoa Touch フレームワーク) では、多くの場合、新しい機能はブロックを使用してのみアクセスできるようになり、プログラマーはすぐにそれを採用しているようです (古い OS バージョンとの下位互換性をあきらめなければならないことを考えると)。

于 2010-08-15T17:58:01.143 に答える
1

他の言語のラムダ式よりもそれほど複雑ではありません。

検討...

int square(x) {
    return x*x;
}

ジャワ:

#(x){x*x}

パイソン:

lambda x:x*x

C#:

x => x*x

C# のアプローチはもう少し直感的だと思います。個人的にはもっと欲しい...

x#x*x
于 2010-08-05T12:20:53.553 に答える
1

これは、新世代の C++ (C++0x) で提案された Lambda 関数に非常に近いため、Oracle の担当者は独自の実装を作成する前に他の実装を検討したと思います。

http://en.wikipedia.org/wiki/C%2B%2B0x

[](int x, int y) { return x + y; }
于 2010-08-16T06:16:16.590 に答える