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


9

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


1
おそらく、ある程度はタイプに依存します。イベントとそのint / stringメンバーの意味を簡単に説明できますか?たとえば、Eventタイプは概念的に1 Intと2 を持つことと同等Maybe Stringsですか?
Ixrec 2016年

1
Javaはあなたが望む言語ですか、それとも作業する必要がありますか?
Thomas Junk

パターンマッチングは、JEP 305で説明されているJava 1xの将来の機能になる可能性があります。
tsh

回答:


10

「Scalaでの関数型プログラミング」の作者は、Javaで型保証された方法で実現できる最高のものを示しています。

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

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

詳細は容易に要約されておらず、実際にとてもよくここではそれらを再現するにはポイントがないことを記事で覆われている(のはどのようなハイパーリンクであることをのために右が?)。


ありがとう、これは多かれ少なかれ私が探していたものです。訪問者はこのケースでかなりうまく機能するようになりましたが、私はおそらく教会の例を将来使用するでしょう。
closeparen 2016年

2
それは賢いことだと思いますが、それが慣用的である
ブライアンアグニュー

慣用的な同等の機能はダブルディスパッチですt.match。3つの関数を渡すのではなく、3つのメソッドを使用して1つのオブジェクトを渡します。(リンクされた記事は、ビジターパターンとダブルディスパッチを混同しています。ビジターは、パターンを選択するためではなく、ネットワーク上の反復を抽象化するためのパターンです)
Pete Kirkham

二重ディスパッチは、Visitorパターンを実装する1つの方法です。「ネットワーク上のイテレーションを抽象化する」とはどういう意味かわかりません。これは、GoFの訪問者の定義で使用されているフレーズではありません。
NietzscheanAI

:彼は彼の「不良」の例では、こので始まるので、私はこのブログ/記事に苦しんだpublic static int depth(Tree t)Javaでこれを行うための「正しい」方法は、メソッドとのインターフェースを定義することであるとき、彼はinstanceofの場合はチェーン使用していますpublic int depth()し、ポリモーフィズムを使用します。わらの男のようです。
JimmyJames

4

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

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

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

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


あなたの最後の段落は頭に釘を打ったと思います。Javaでのこれに対する慣用的なアプローチは、ポリモーフィズムを使用することです。Eventを適切なメソッドを持つインターフェースにし、Thing1、Thing2、およびThing3をそのインターフェースの実装にします。関数ではなくタイプでコードを分割しますが、それが基本的にOOPのポイントですね。
ジュール

Javaが必須であることは重要ではありません。Javaはsum型を統合することができます(そして統合する必要があります)が、そうではありません。
gardenhead 2016


0

次のように、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;
}

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


クラスではなく列挙型を使用している理由がわかりません-列挙型の利点は何ですか?
Jules

これは、switchステートメントでも使用できるため、パターンマッチングに少し似ています
jasiek.miko

それを今追加しました。
jasiek.miko 2016年

0

訪問者のパターンまたはそれに相当する教会のエンコーディングは、進むべき道です。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は、流暢なパターンマッチング構文を提供するイベントクラスを生成します(すべてのケースが処理されることを徹底的にチェックします)。


投稿でプロジェクトとの関係を明確にしていただけますか(stackoverflow.com/help/promotionを参照)。
Benni 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.