This is why I love pattern matching in languages that support it - typing is just one of the things on which they can switch. As the Scala website puts it (paraphrasing), "Pattern matching is switch on steroids!"
Here is how you would return all leaf values in a binary tree in Scala:
case class Node
case class Fork(left: Node, right: Node) extends Node
case class Leaf(value: Int) extends Node
def getValues(node: Node): List[Int] = node match {
case f: Fork => getValues(f.left) ++ getValues(f.right)
case l: Leaf => List(l.value)
}
EDIT: Would actually be more idiomatic in Scala to extract the values from the case classes rather than just matching on type...yet another wonderful feature of pattern matching. Coded as follows:
def getValues(node: Node): List[Int] = node match {
case Fork(left, right) => getValues(left) ++ getValues(right)
case Leaf(value) => List(value)
}
Here's the tail-call optimized version, won't blow the stack. Technically idiomatic, but way too cryptic to be serious code. Clearly I need more outlets for writing Scala. =)
@tailrec
def getValuesFromNode(node: Node, accValues: List.empty[Int], nodeList: List.empty[Node]): List[Int] = node match {
case Fork(left, right) => getValuesFromList(accValues, nodeList ++ List(left, right))
case Leaf(value) => getValuesFromList(accValues ++ List(value), nodeList)
}
@tailrec
def getValuesFromList(accValues: List.empty[Int], nodeList: List.empty[Node]): List[Int] = nodeList match {
case x :: xs => getValuesFromNode(x, accValues, xs)
case _ => accValues
}
Of all the code in there this part confused me. What exactly is being switched on? It looks like v is being reassigned to the type of v, then the type of v is written out (instead of the value).
It's being switched on the type of v (string or other, in this case), though in a more complex case you could easily have several different types. The assignment basically redefines v to be the matched type inside the case statement. You could easily just add a "sv := v.(string)" as the first statement of "case string", then use sv in place of v within that block, but this does read much cleaner.
I think it gets more interesting when using several, often complex (struct) types in the same switch statement.
> It looks like v is being reassigned to the type of v,
v is not being reassigned, it is being declared (note := rather than =). But its a little complicated, because type switches are a special syntax construct that is similar to, but different than, regular expression switches. See, "Type switches" in the language reference [1]
"type" is a magic word in Go, and in that example. It's highly idiomatic -- it's inconsistent with the rest of the language (Using "type" instead of an actual type), but it makes sense once you memorize the idiom. Sort of perlish -- there are two different operations that look basically the same, and the correct one is chosen based on context (in this case, the context is "is there a type name, or "type" literally?)
Perhaps it would have been cleaner to use "*" or some other operator symbol instead of the reserved word "type"
> "type" is a magic word in Go, and in that example.
Specifically, type switches are a specific syntax construct that look very similar to normal switches, but switched on something that looks like a type assertion with "type" in place of an actual type.