Multiples Pattern-Matching

Auf Stackoverflow habe ich schon häufiger die Frage gelesen, wie man es anstellt, dass beim Pattern-Matching nicht nur der erste passende Fall berücksichtigt werden, sondern alle passenden Fälle. Eingebaut gibt es das leider nicht, es ist aber auch nicht schwer zu realisieren. Hier eine recht elegante Version von Philippe:

class MatchAll[S](scrutinee : =>S) {
  def matchAll[R](patterns : PartialFunction[S,R]*) : Seq[R] = {
    val evald : S = scrutinee
    patterns.flatMap(_.lift(evald))
  }
}

implicit def anyToMatchAll[S](scrut : =>S) : MatchAll[S] = new MatchAll[S](scrut)

Und die Anwendung:

(2,4) matchAll (
  { case (2,_) => "starts with two" },
  { case (3,_) => "starts with three" },
  { case (_,4) => "ends with four" },
  { case (a,b) if a + b == 6 => "adds up to six" }
)

//--> ArrayBuffer(starts with two, ends with four, adds up to six)

Ich denke, dass man mit der im Vergleich zu match leicht veränderten Syntax gut leben kann, und fände es gut, wenn dieses Konstrukt auch in die Scala-Bibliotheken aufgenommen würde.

Werbeanzeigen

Block-Trick

Ich bin gerade auf Stack Overflow auf einen netten Trick gestoßen.

Die Frage war, warum so eine seltsame Syntax wie…

(1 to 3) foreach {  
  val line = Console.readLine  
  i: Int => println(line)  
}  

… erlaubt ist. Die Antwort ist einfach: foreach[A] erwartet als Argument eine Funktion A => Unit, und der Wert eines Blocks ist gleich seinem letzten Element, das hier auch eine passende Funktion liefert. Aus praktischer Sicht heißt das, dass in Methoden wie foreach, map u.s.w. vor der Funktionsdefinitions alles mögliche stehen kann. „Dem Faß die Krone“ aufgesetzt hat Dave Griffith in seiner Antwort mit folgendem Beispiel (ich habe es noch etwas vereinfacht):

val v = (1 to 6) map {
    var total = 1
    i:Int => {total *= i; total}
}
println(v)

Das Beispiel gibt die Fakultäten von 1 bis 6 zurück. Dabei „merkt“ sich die zurückgegebene Funktion die Variable total – genau diese „Einfangen“ der aktuellen Umgebung unterscheidet echte Closures von simplen Codeblöcken – und „recycled“ sie. Damit hat man ein interessantes Werkzeug, um die eigentlich voneinander unabhängigen Berechnungen in map oder foreach doch irgendwie miteinander zu verketten.

Und gerade das Beispiel mit der Fakultät scheint es in sich zu haben, denn wie soll man es sonst schreiben? Ich bin nur noch auf folgende Monströsität gekommen, die sicher keine Verbesserung ist:

val v = (1 to 6).foldLeft(List[Int]()){ (list, i) =>
    (list.headOption.getOrElse(1) * i) :: list }.reverse
println(v)

Wem fällt etwas besseres ein?

Nachtrag
Wenn wir schonmal beim Etwas-Besseres-Finden sind: Ich bin gerade über das Blog von James Carr gestolpert. Hier meine Lösung zu seiner Aufgabe, das Pascalsche Dreieck als Liste von Listen zu berechnen:

def pascal(n: Int) = {
  def loop(n:Int):List[List[Long]] = if (n == 1) List(List(1L)) else {
    val p = loop(n-1)
    val last = 0L :: p.head
    last.zip(last.reverse).map(t => t._1 + t._2) :: p
  }
  loop(n).reverse
}

Und wem fällt dazu etwas besseres ein?