web-dev-qa-db-ja.com

Javaで同等の慣用的なパターンマッチング

STDINからのイベントを解析して「実行」するシミュレーターを構築しています。私の背景は最近のほとんどが関数型プログラミングであるため、次のようなことをするのは自然なことのように思われました。

data Event = Thing1 String Int | Thing2 Int | Thing3 String String Int
Parse :: String -> [Event]
Simulate :: [Event] -> [Result]

シミュレートする場所

case event
  of Thing1 a b => compute for thing one
   | Thing2 a => compute for thing two

などJavaでこの種のことをする慣用的な方法は何ですか?グーグルで私はネストされたクラスとビジターパターンの方向を指摘されましたが、私の試みではそれはかなり重いようです。型消去は、私と一生懸命戦っているようです。正しく行われるように見えるものの概要を教えていただけますか?

9
closeparen

「Scalaでの関数型プログラミング」の著者は、Javaで型安全な方法で実現できる最高のものを示しています。

http://blog.higher-order.com/blog/2009/08/21/structural-pattern-matching-in-Java/

本質的には、ケースのチャーチエンコーディングを使用して、欠けているものがある場合にコンパイラーが必ず文句を言うようにします。

詳細は簡単に要約されておらず、実際に記事で十分にカバーされているため、ここでそれらを再現しても意味がありません(つまり、ハイパーリンクとはfor正しいですか?) 。

10
NietzscheanAI

この種のことをJavaで行う慣用的な方法は何ですか?

Java(言語)が基本的に必須であることを考えると、実際にはそのようなことはありません。

Javalanguageに限定されないがJVMで実行できる場合、Scalaを調査すると、 パターンマッチング を使用して上記のようなものを実現します。

それ以外の場合は、さまざまなケースを手動で照合し、必要に応じてメソッドを呼び出すか、多分「Event」のサブタイプを定義し、ポリモーフィズムを使用して各サブタイプの特定のメソッドを呼び出すことになると思います。

4
Brian Agnew

https://github.com/johnlcox/motif を見てください。これは、Java 8。

ML/Erlang/Haskellほど素敵ではありませんが、それでもほとんどの場合よりもはるかに宣言的に見えます。

1
9000

訪問者パターンまたはそれに相当する教会のエンコーディングは、進むべき道です。 Javaではかなり冗長ですが、 Derive4J (私が管理している注釈プロセッサ)または Adt4J などのツールがボイラープレートを生成できることを期待しています。このようなツールを使用すると、例は次のようになります。

import Java.util.function.Function;
import org.derive4j.Data;

@Data
public abstract class Event {

  interface Cases<X> {
    X Thing1(String s, int i);
    X Thing2(int i);
    X Thing3(String s, String s2, int i);
  }

  abstract <X> X match(Cases<X> cases);

  static Function<Event, Result> Simulate =
      Events.cases().
          Thing1( (s, i    ) -> computeForThingOne(s, i)       ).
          Thing2( (i       ) -> computeForThingTwo(i)          ).
          Thing3( (s, s2, i) -> computeForThingThree(s, s2, i) );

}

Derive4Jは、流暢なパターンマッチング構文を提供するイベントクラスを生成します(すべてのケースが処理されることを徹底的にチェックします)。

0
JbGi

次のように、enumとインターフェイスを使用して、simulateメソッドをオーバーライドできます。

interface Event {
  void simulate()
}

enum MyEvents implements Event {
  THING1 {
    @Override
    void simulate() {
    //...
    }
  },
  THING2 {
    @Override
    void simulate() {
    //...
    }
  },
}

あなたが持っているとしましょうEvent event。次に、次の2つの方法のいずれかで使用できます。

event.simulate();

または

switch(event) {
  case THING1:
    //..
    break;
  case THING2:
    break;
}

ただし、コンストラクターはJVMによって自動的に呼び出されるため、そこにパラメーターを格納するには、アクセサーなどのプロパティを追加する必要があります。

または、イベントを定数文字列としてコード化し、switch構成を使用することもできます。その場合は、

string event = args[0];
switch(event){
  case THING1:
    int a = args[1];
    //...
    break;
  case THING2:
    int a = args[1];
    int b = args[2];
    break;
}

など。しかし、はい、パターンマッチングを直接模倣するネイティブはありません:(

0
jasiek.miko