2011-07-18 13 views
6

Wiem, że można zrobić dopasowania na listach w sposób jakMatching z kombinacji niestandardowych/operatorów

val list = List(1,2,3) 
list match { 
    case head::tail => head 
    case _ => //whatever 
} 

więc zacząłem się zastanawiać, jak to działa. Jeśli dobrze rozumiem, :: jest tylko operator, to co powstrzyma mnie od robienia czegoś podobnego

4 match { 
    case x + 2 => x //I would expect x=2 here 
} 

jeśli istnieje sposób, aby stworzyć tego rodzaju funkcjonalności, jak to się robi; jeśli nie, to dlaczego?

+0

Dlaczego nie używasz 'y-2' (gdy y = 4)? –

+0

To jest bardziej ciekawostka, więc nie mam konkretnych przykładów, ale wyobrażałem sobie, że jest to miłe dla bardziej złożonych klas. – Dylan

Odpowiedz

8

Dopasowanie do wzorca pobiera dane wejściowe i rozkłada je za pomocą funkcji unapply. Tak więc w twoim przypadku unapply(4) musiałby zwrócić dwie liczby, które sumują się do 4. Istnieje jednak wiele par, które sumują się do 4, więc funkcja nie wie, co zrobić.

W razie potrzeby funkcja 2 jest dostępna dla funkcji unapply. Specjalna klasa sprawa, która przechowuje 2 będzie działać na to:

case class Sum(addto: Int) { 
    def unapply(i: Int) = Some(i - addto) 
} 

val Sum2 = Sum(2) 
val Sum2(x) = 5 // x = 3 

(Byłoby miło być w stanie coś zrobić jak val Sum(2)(y) = 5 dla zwięzłości, ale Scala nie pozwala parametryzowane extractory patrz here.)

[EDIT: to jest trochę głupie, ale może faktycznie zrobić następujące też:

val `2 +` = Sum(2) 
val `2 +`(y) = 5 // y = 3 

]

EDIT: Powodem, dla którego działa head::tail jest to, że istnieje dokładnie jeden sposób na rozdzielenie głowy od końca listy.

Nic z natury specjalnego :: kontra +: można użyć + jeśli miał ustaloną pomysł jak chciał go złamać numer. Na przykład, jeśli chcesz + oznacza „podzielone na pół”, a następnie można zrobić coś takiego:

object + { 
    def unapply(i: Int) = Some(i-i/2, i/2) 
} 

i używać go jak:

scala> val a + b = 4 
a: Int = 2 
b: Int = 2 

scala> val c + d = 5 
c: Int = 3 
d: Int = 2 

EDIT: Wreszcie this wyjaśnia, że ​​kiedy Dopasowywanie wzorców, A op B oznacza to samo co op(A,B), co sprawia, że ​​składnia wygląda ładnie.

+0

Wydaje mi się, że twoja edycja rozwiązuje moje problemy, będę musiał zabawić się tą koncepcją dziś wieczorem. – Dylan

+0

@Dylan: Przepisałem prozę, więc mam nadzieję, że teraz będzie mniej niejasne. – dhg

4

Matching z case head :: tail wykorzystuje Infix pracy wzór formularza p1 op p2 który zostanie przetłumaczony na op(p1, p2) przed wykonaniem rzeczywistego dopasowania. (Patrz API dla ::)

Problem z + jest następujący:

Chociaż łatwo jest dodać

object + { 
    def unapply(value: Int): Option[(Int, Int)] = // ... 
} 

przedmiot, który zrobi dopasowanie, można dostarczyć tylko jeden wynik na wartość. Na przykład.

object + { 
    def unapply(value: Int): Option[(Int, Int)] = value match { 
    case 0 => Some(0, 0) 
    case 4 => Some(3, 1) 
    case _ => None 
} 

Teraz to działa:

0 match { case x + 0 => x } // returns 0 

również to

4 match { case x + 1 => x } // returns 3 

Ale to nie będzie i nie można go zmienić:

4 match { case x + 2 => x } // does not match 

Żaden problem dla ::, choć, ponieważ tak jest zawsze określa się, co to jest head i co to jest tail listy.

+0

+1 dla łącza API - nie zdawałem sobie z tego sprawy. Więc z tego, co z tego wyniknę, lista [_] jest w rzeczywistości podklasą :: a zatem klasą przypadku i dlatego możesz tak pasować? Więc gdybym chciał zaoferować coś w rodzaju dopasowania "" witaj ", dopasuj {case head :: tail // h + ello ...', to potrzebowałbym dokonać niejawnej konwersji z Stringa na moją własną wyspecjalizowaną klasę przypadków? – Dylan

1

Istnieją dwa :: s (wymawiane "przeciw") w Scala. Jeden to operator na List s, a drugi to klasa, która reprezentuje niepustą listę cechującą się głową i ogonem. Tak więc head :: tail jest wzorcem konstruktora, który nie ma nic wspólnego z operatorem.