4

ケースクラスの引数を一般的に置き換えることは可能ですか? より具体的には、「検索」ケース・クラスと「置換」ケース・クラス (文法規則の左側と右側のようなもの)、およびターゲット・ケース・クラスを受け取る代替関数が必要であるとします。検索ケース・クラスの引数を置換ケース・クラスに置き換えた新しいケース・クラス? この関数は、単純にケース クラス (Product?) と、ケース クラスのすべての引数/製品に適用される関数を受け取ることもできます。

明らかに、特定のケース クラスがあれば、unapply と apply を使用できますが、この種の関数を一般的に (任意のケース クラスを指定して) 記述する最善/最も簡単/その他の方法は何ですか?

Scala 2.10 のリフレクション機能または shapeless の Iso.hlist を使用する良い解決策があるかどうか疑問に思っています。

たとえば、私が本当にできるようにしたいのは、次のようなクラスが与えられた場合です...

class Op[T]
case class From(x:Op[Int]) extends Op[Int]
case class To(x:Op[Int]) extends Op[Int]

case class Target(a:Op[Int], b:Op[Int]) extends ...
// and lots of other similar case classes

... 任意のケース クラスを取り、From 型の要素を To 型のインスタンスに置き換えたコピーを返すことができる関数があります。

4

3 に答える 3

5

プラグインをご容赦いただければ、 Kiama 言語処理ライブラリの書き換えコンポーネントがこの種の目的に最適であることがわかると思います。これは、戦略的プログラミングの非常に強力な形式を提供します。

これは、ケース クラス インスタンスから作成されたツリー内Toの を に書き換える完全なソリューションです。From

import org.kiama.rewriting.Rewriter

class Op[T]
case class Leaf (i : Int) extends Op[Int]
case class From (x : Op[Int]) extends Op[Int]
case class To (x : Op[Int]) extends Op[Int]

case class Target1 (a : Op[Int], b : Op[Int]) extends Op[Int]
case class Target2 (c : Op[Int]) extends Op[Int]

object Main extends Rewriter {

    def main (args : Array[String]) {
        val replaceFromsWithTos =
            everywhere {
                rule {
                    case From (x) => To (x)
                }
            }

        val t1 = Target1 (From (Leaf (1)), To (Leaf (2)))
        val t2 = Target2 (Target1 (From (Leaf (3)), Target2 (From (Leaf (4)))))

        println (rewrite (replaceFromsWithTos) (t1))
        println (rewrite (replaceFromsWithTos) (t2))
    }

}

出力は

Target1(To(Leaf(1)),To(Leaf(2)))
Target2(Target1(To(Leaf(3)),Target2(To(Leaf(4)))))

replaceFromsWithTos値の考え方は、rule構造が部分関数を持ち上げて、あらゆる種類の値を操作できるようにすることです。この場合、部分関数はノードでのみ定義されFrom、それらをノードに置き換えますTo。コンビネータは、everywhere「ツリー内のすべてのノードに私の引数を適用し、引数が適用されない場所を変更せずに残します。

この種の単純な書き直しよりもはるかに多くのことができます。詳細については、いくつかの例へのリンクを含む主要な Kiama 書き換えドキュメントを参照してください。

于 2012-11-16T21:56:24.753 に答える
4

私はシェイプレスで少し実験し、あるケースクラスを別のケースクラスに変換する次の比較的一般的な方法を思い付くことができました。

import shapeless._ /* shapeless 1.2.3-SNAPSHOT */

case class From(s: String, i: Int)
case class To(s: String, i: Int)

implicit def fromIso = Iso.hlist(From.apply _, From.unapply _)
implicit def toIso = Iso.hlist(To.apply _, To.unapply _)

implicit def convert[A, B, L <: HList]
                   (a: A)
                   (implicit srcIso: Iso[A, L],
                             dstIso: Iso[B, L])
                   : B =
  dstIso.from(srcIso.to(a))

val f1 = From("Hi", 7)
val t1 = convert(f1)(fromIso, toIso)

println("f1 = " + f1) // From("Hi", 7)
println("t1 = " + t1) // To("Hi", 7)

しかし、私は暗黙を正しく理解することができませんでした。理想的には、

val t1: To = f1

十分だろう、あるいは多分

val t1 = convert(f1)

もう1つの優れた改善点は、各ケースクラスに対してiso-implicits( fromIso、 )を明示的に宣言する必要をなくすことです。toIso

于 2012-11-15T21:06:22.263 に答える
4

パターン マッチングを通じて unapply/apply を使用するよりも良い方法を実際に見つけることはできないと思います。

someValue match {
  case FindCaseClass(a, b, c) => ReplaceCaseClass(a, b, c)
  // . . .
}

FindCaseClass何らかの方法で関連付けるルールを書き出すReplaceCaseClass必要があります。名前を使用するだけでもう少し簡潔にできるかもしれませんが、これにはケース クラス フィールドの数とタイプもチェックできるという追加の利点があります。コンパイル時に、すべてが正しく一致することを確認します。

おそらく、すべてのケース クラスが extendsProductであるという事実を利用して、これを自動的に行う何らかの方法がありproductElement(n)ますAny。開始:

case class From(i: Int, s: String, xs: Seq[Nothing])
case class To(i: Int, s: String, xs: Seq[Nothing])

val iter = From(5,"x",Nil).productIterator
val f = To.curried
iter.foldLeft(f: Any) { _.asInstanceOf[Any => Any](_) }
// res0: Any = To(5,x,List())

しかし、実際には、パターン マッチング バージョンの方が適していると思います。

編集:これは、メソッドにリファクタリングされた関連コードを含むバージョンです:

case class From(i: Int, s: String, xs: Seq[Nothing])
case class To(i: Int, s: String, xs: Seq[Nothing])

type Curryable = { def curried: _ => _ }

def recase(from: Product, to: Curryable) = {
  val iter = from.productIterator
  val f = to.curried
  iter.foldLeft(f: Any) { _.asInstanceOf[Any => Any](_) }
}

recase(From(5,"x",Nil), To)
// res0: Any = To(5,x,List())
于 2012-11-15T17:10:18.207 に答える