10

エクストラクタがカスタマイズ引数を取ることを可能にする構文は Scala にありますか? この例は少し不自然です。整数の二分探索木があり、現在のノードの値がカスタム値で割り切れる場合に一致させたいとします。

F# アクティブ パターンを使用すると、次のことができます。

type Tree =
    | Node of int * Tree * Tree
    | Empty  

let (|NodeDivisibleBy|_|) x t =
    match t with
    | Empty -> None
    | Node(y, l, r) -> if y % x = 0 then Some((l, r)) else None

let doit = function
    | NodeDivisibleBy(2)(l, r) -> printfn "Matched two: %A %A" l r
    | NodeDivisibleBy(3)(l, r) -> printfn "Matched three: %A %A" l r
    | _ -> printfn "Nada"

[<EntryPoint>]
let main args =
    let t10 = Node(10, Node(1, Empty, Empty), Empty)
    let t15 = Node(15, Node(1, Empty, Empty), Empty)

    doit t10
    doit t15

    0

Scala では、似たようなことができますが、私が望むものとはまったく異なります。

sealed trait Tree
case object Empty extends Tree
case class Node(v: Int, l: Tree, r: Tree) extends Tree

object NodeDivisibleBy {
  def apply(x: Int) = new {
    def unapply(t: Tree) = t match { 
      case Empty => None
      case Node(y, l, r) => if (y % x == 0) Some((l, r)) else None
    }
  }
}

def doit(t: Tree) {
  // I would prefer to not need these two lines.
  val NodeDivisibleBy2 = NodeDivisibleBy(2)
  val NodeDivisibleBy3 = NodeDivisibleBy(3)
  t match { 
    case NodeDivisibleBy2(l, r) => println("Matched two: " + l + " " + r)
    case NodeDivisibleBy3(l, r) => println("Matched three: " + l + " " + r)
    case _ => println("Nada")
  }
}

val t10 = Node(10, Node(1, Empty, Empty), Empty)
val t15 = Node(15, Node(1, Empty, Empty), Empty)

doit(t10)
doit(t15)

私ができれば素晴らしいことです:

case NodeDivisibleBy(2)(l, r) => println("Matched two: " + l + " " + r)
case NodeDivisibleBy(3)(l, r) => println("Matched three: " + l + " " + r)

しかし、これはコンパイル時エラーです: '=>' が必要ですが、'(' が見つかりました。

考え?

4

5 に答える 5

6

スペックから:

   SimplePattern ::= StableId ‘(’ [Patterns] ‘)’

抽出パターンx(p1、...、pn)ここで、 n≥0は、コンストラクタパターンと同じ構文形式です。ただし、ケースクラスの代わりに、安定した識別子xは、名前付きのメンバーメソッドを持つオブジェクト、unapplyまたはunapplySeqパターンに一致するオブジェクトを示します。

と:

安定した識別子は、識別子で終わるパスです。

つまり、のような式ではありませんNodeDivisibleBy(2)

ですから、これはScalaでは簡単な方法では不可能であり、個人的にはそれで問題ないと思います。次のように記述する必要があります(ちなみに、NodeDivisibleByオブジェクトで定義してインポートします)。

val NodeDivisibleBy2 = NodeDivisibleBy(2)
val NodeDivisibleBy3 = NodeDivisibleBy(3)

は、case句の任意の式を解読する必要がないという可読性の向上に対して支払う小さな代償です。

于 2012-11-06T01:43:16.173 に答える
4

Travis Brown が指摘したように、scala では実際には不可能です。

そのシナリオで私が行うことは、ガードとエイリアスを使用してテストから分解を分離することです。

val DivisibleBy = (n: Node, x: Int) => (n.v % x == 0) 

def doit(t: Tree) = t match { 
    case n @ Node(y, l, r) if DivisibleBy(n,2) => println("Matched two: " + l + " " + r)
    case n @ Node(y, l, r) if DivisibleBy(n,3) => println("Matched three: " + l + " " + r)
    case _ => println("Nada")
}

この単純なケースでは、セパレートを定義するのDivisibleByは明らかにやり過ぎですが、F# のアクティブ パターンと同様に、より複雑なシナリオでは読みやすくなる可能性があります。

また、Node のメソッドとして divisibleBy を定義し、次のようにすることもできます。

case class Node(v: Int, l: Tree, r: Tree) extends Tree {
    def divisibleBy(o:Int) = (v % o)==0
}

def doit(t: Tree) = t match { 
    case n @ Node(y, l, r) if n divisibleBy 2 => println("Matched two: " + l + " " + r)
    case n @ Node(y, l, r) if n divisibleBy 3 => println("Matched three: " + l + " " + r)
    case _ => println("Nada")
}

これは、F# バージョンよりも読みやすい (より詳細な場合) と思います

于 2012-11-06T06:28:41.097 に答える
2

ケース クラス、述語、および引数をバインドし、通常どおり結果を照合します。

case class Foo(i: Int)

class Testable(val f: Foo, val ds: List[Int])

object Testable {
  def apply(f: Foo, ds: List[Int]) = new Testable(f, ds)
  def unapply(t: Testable): Option[(Foo, List[Int])] = {
    val xs = t.ds filter (t.f.i % _ == 0)
    if (xs.nonEmpty) Some((t.f, xs)) else None
  }
}

object Test extends App {
  val f = Foo(100)

  Testable(f, List(3,5,20)) match {
    case Testable(f, 3 :: Nil)  => println(s"$f matched three")
    case Testable(Foo(i), 5 :: Nil) if i < 50
                                => println(s"$f matched five")
    case Testable(f, ds)        => println(s"$f matched ${ds mkString ","}")
    case _                      => println("Nothing")
  }
}
于 2012-11-06T08:08:14.917 に答える
-1

私が知っているように、答えはノーです。

この場合も前の方法を使用します。

于 2012-11-06T01:29:13.370 に答える