2012-11-05 8 views
9

Gibt es in Scala eine Syntax, die es Extraktoren erlaubt, ein Anpassungsargument zu übernehmen? Dieses Beispiel ist ein bisschen erfunden. Angenommen, ich habe einen binären Suchbaum für Ganzzahlen und möchte den aktuellen Knoten abgleichen, wenn sein Wert durch einen benutzerdefinierten Wert teilbar ist.Scala Extractor mit Argument

Verwendung von F # aktiven Muster, kann ich folgendes tun:

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 

In Scala, kann ich etwas Ähnliches tun, aber nicht ganz das, was ich will:

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) 

Es wäre toll, wenn ich konnte tun:

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

aber dies ist eine Kompilierung Fehler:. '=>' erwartet, aber '(' gefunden

Gedanken?

Antwort

0

Wie ich weiß Antwort ist nein.

Ich verwende auch den vorherigen Weg für diesen Fall.

6

Von the spec:

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

An extractor pattern x(p1, ..., pn) where n ≥ 0 is of the same syntactic form as a constructor pattern. However, instead of a case class, the stable identifier x denotes an object which has a member method named unapply or unapplySeq that matches the pattern.

und:

A stable identifier is a path which ends in an identifier.

das heißt, nicht ein Ausdruck wie NodeDivisibleBy(2).

Also nein, das ist in Scala nicht direkt möglich, und persönlich finde ich das ganz gut: das folgende schreiben (was ich übrigens wohl im Objekt definieren und importieren würde):

ist ein kleiner Preis für die erhöhte Lesbarkeit zu zahlen, keine willkürlichen Ausdrücke in der case-Klausel entziffern zu müssen.

4

Wie Travis Brown festgestellt hat, ist es in scala nicht wirklich möglich.

Was ich in diesem Szenario mache, ist nur die Trennung von der Prüfung mit einem Guard und einem Alias ​​zu trennen.

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") 
} 

ein separates DivisibleBy zu definieren ist offensichtlich komplett übertrieben in diesem einfachen Fall aber kann die Lesbarkeit in komplexeren Szenarien in ähnlicher Art und Weise helfen, wie F # aktive Muster tun.

Sie könnten auch divisibleBy als Methode der Knoten definieren und haben:

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") 
} 

die ich denke, besser lesbar ist (wenn ausführlicher) als die F # Version

2

Bind zusammen Falle Klasse, Prädikat und Argumente, dann auf das Ergebnis wie üblich übereinstimmen.

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") 
    } 
} 
+0

Dies ist Scala 2.10 richtig? –