2012-11-05 20 views
9

Czy w Scali istnieje składnia, która pozwala ekstraktorom na przyjęcie argumentu dostosowującego? Ten przykład jest nieco wymyślny. Załóżmy, że mam drzewo wyszukiwania binarnego na liczbach całkowitych i chcę dopasować na bieżącym węźle, jeśli jego wartość jest podzielna przez jakąś wartość niestandardową.Scala Extractor z argumentem

Korzystanie F # aktywnych wzorów, mogę wykonać następujące czynności:

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 

w Scala, mogę zrobić coś podobnego, ale nie całkiem to, czego chcę:

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) 

Byłoby wspaniale, gdyby mogłem zrobić:

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

ale jest to błąd kompilacji czas. '=>' oczekiwano, ale '(' znaleziono

Myśli?

Odpowiedz

0

Jak wiem, odpowiedź brzmi nie.

Używam również poprzedniej metody dla tej sprawy.

6

Z 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.

oraz:

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

to znaczy nie jest wyrażeniem jak NodeDivisibleBy(2).

Więc nie, nie jest to możliwe w żaden prosty sposób w Scali, i osobiście uważam, że jest w porządku: napisać następujące (które przy okazji prawdopodobnie bym zdefiniował w obiekcie i importowaniu obiektu NodeDivisibleBy):

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

to niewielka cena za lepszą czytelność braku konieczności rozróżniania dowolnych wyrażeń w klauzuli case.

4

Jak zauważył Travis Brown, nie jest to możliwe w scala.

To, co robię w tym scenariuszu, to oddzielenie rozkładu od testu od strażnika i aliasu.

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

Definiowanie oddzielny DivisibleBy jest oczywiście kompletna przesada w tym prostym przypadku, ale może pomóc czytelności w bardziej złożonych scenariuszy, w podobny sposób jak zrobić F # aktywnych wzorów.

Można również zdefiniować divisibleBy jako metoda Node oraz:

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

co moim zdaniem jest bardziej czytelny (jeśli bardziej gadatliwy) niż F # wersji

2

spajają klasy przypadek, kwantyfikatorów i argumenty, a następnie dopasuj wynik tak, jak zwykle.

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

To jest Scala 2,10 prawda? –