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)
但这是一个编译时错误:'=&gt;'预期,但'''发现。
思想?
答案 0 :(得分:6)
来自the spec:
SimplePattern ::= StableId ‘(’ [Patterns] ‘)’
提取器模式 x(p1,...,pn)其中n≥0是相同的 句法形式作为构造函数模式。但是,而不是一个案例 class,稳定标识符 x 表示具有成员的对象 名为
unapply
或unapplySeq
的方法与模式匹配。
和
稳定标识符是以标识符结尾的路径。
即,不是像NodeDivisibleBy(2)
这样的表达式。
所以不,这在Scala中不可能以任何直接的方式进行,我个人认为这很好:必须编写以下内容(顺便说一下,我可能会在NodeDivisibleBy
对象中定义进口):
val NodeDivisibleBy2 = NodeDivisibleBy(2)
val NodeDivisibleBy3 = NodeDivisibleBy(3)
是为了不必破解case子句中的任意表达式而增加的可读性,这是一个很小的代价。
答案 1 :(得分:4)
正如特拉维斯·布朗所指出的那样,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#活动模式一样在更复杂的场景中提供可读性。
您还可以将divisibleBy定义为Node的方法,并具有:
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#版本更具可读性(如果更详细)
答案 2 :(得分:2)
将case类,谓词和参数绑定在一起,然后像往常一样匹配结果。
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")
}
}
答案 3 :(得分:0)
我知道答案是否定的。
对于这种情况,我也使用了以前的方式。
答案 4 :(得分:0)
迟到的答案,但是有一个提供语法~(extractorWithParam(p), bindings)
的scalac插件,编译的所有安全性:https://github.com/cchantep/acolyte/tree/master/scalac-plugin#match-component