More efficient Solution with tailrecursion? - scala

I have the following ADT for Formulas. (shortened to the important ones)
sealed trait Formula
case class Variable(id: String) extends Formula
case class Negation(f: Formula) extends Formula
abstract class BinaryConnective(val f0: Formula, val f1: Formula) extends Formula
Note that the following methods are defined in an implicit class for formulas.
Let's say i want to get all variables from a formula.
My first approach was:
Solution 1
def variables: Set[Variable] = formula match {
case v: Variable => HashSet(v)
case Negation(f) => f.variables
case BinaryConnective(f0, f1) => f0.variables ++ f1.variables
case _ => HashSet.empty
}
This approach is very simple to understand, but not tailrecursive. So I wanted to try something different. I implemented a foreach on my tree-like formulas.
Solution 2
def foreach(func: Formula => Unit) = {
#tailrec
def foreach(list: List[Formula]): Unit = list match {
case Nil =>
case _ => foreach(list.foldLeft(List.empty[Formula])((next, formula) => {
func(formula)
formula match {
case Negation(f) => f :: next
case BinaryConnective(f0, f1) => f0 :: f1 :: next
case _ => next
}
}))
}
foreach(List(formula))
}
Now I can implement many methods with the help of the foreach.
def variables2 = {
val builder = Set.newBuilder[Variable]
formula.foreach {
case v: Variable => builder += v
case _ =>
}
builder.result
}
Now finally to the question. Which solution is preferable in terms of efficieny? At least I find my simple first solution more aesthetic.

I would expect Solution 2 to be more efficient, because you aren't create many different HashSet instances and combining them together. It is also more general.
You can simplify your Solution 2, removing the foldLeft:
def foreach(func: Formula => Unit) = {
#tailrec
def foreach(list: List[Formula]): Unit = list match {
case Nil =>
case formula :: next => {
func(formula)
foreach {
formula match {
case Negation(f) => f :: next
case BinaryConnective(f0, f1) => f0 :: f1 :: next
case _ => next
}
}
}
}
foreach(List(formula))
}

Related

Improving Pattern-matching Code

Assume the following data-structure.
sealed abstract class Formula {...}
//... some other case classes
sealed abstract class BinaryConnective(f0: Formula, f1: Formula) extends Formula {
def getf0 = f0
def getf1 = f1
}
object BinaryConnective {
def unapply(bc : BinaryConnective) = Some((bc.getf0, bc.getf1))
}
final case class Conjunction(f0: Formula, f1: Formula) extends BinaryConnective(f0,f1)
final case class Disjunction(f0: Formula, f1: Formula) extends BinaryConnective(f0,f1)
final case class Implication(f0: Formula, f1: Formula) extends BinaryConnective(f0,f1)
final case class Equivalence(f0: Formula, f1: Formula) extends BinaryConnective(f0,f1)
I now wrote a function that has a lot of pattern-matching:
The return-type of getCondition is Formula => Option[HashMap[Variable, Formula]]
formula match {
//.. irrelevant cases not shown
case Conjunction(f0, f1) => (g : Formula) => {
g match {
case conj # Conjunction(g0, g1) => {
getCondition(f0)(conj.f0) match {
case Some(map0) => {
getCondition(f1)(conj.f1) match {
case Some(map1) if map0.forall{case (key, value) => map1.get(key).map(_ == value).getOrElse(true)} => {
Some(map0 ++ map1)
}
case _ => None
}
}
case None => None
}
}
case _ => None
}
}
}
Now to my question.
1) Is there a nicer way to express this code? A lot of matches going on.
Edit 1: I could not think of a nice-looking way to use things like map, filter etc.., but it seems very compact with for-comprehensions. I've also noticed that conj # was not necessary at all, which also made it a little simpler.
case Conjunction(f0, f1) => (g: Formula) => g match {
case Conjunction(g0, g1) => for {
map0 <- getCondition(f0)(g0)
map1 <- getCondition(f1)(g1)
if map0.forall {case (key, value) => map1.get(key).map(_ == value).getOrElse(true)}
} yield map0 ++ map1
case _ => None
}
2) This is the match for Conjunction. I would have to repeat it for Disjunction, Implication and Equivalence. g has to be of the same class as formula. The only thing that would change is case conj # Conjunction(g0, g1). I would have to adjust it to case disj # Disjunction(g0, g1) if formula is a Disjunction etc...
Is there a way to do it combined for all cases?
Option should provide a lot of useful functions to simplify your code.
For example, when you write something like:
o match {
case Some(e) => Some(transform(e))
case _ => None
}
You could just call map: o.map(transform)
I also invite you to look at the filter function for the cases including a condition.
EDIT: great suggestion by #om-nom-nom: For comprehensions can also be used (they actually are sugar relying on map, flatMap, filter, etc):
for{
e <- o
} yield transform(e)

Preferred way of collecting variables from a formula

I'm dealing with propositional logic at the moment and I wrote two algorithms for collecting all variables in a formula. I want the output to be immutable. Which one should be preferred in terms of speed/elegance? Is there an even better way? Thanks in advance.
def getVariables(formula: Formula): Set[Variable] = formula match {
case v: Variable => HashSet(v)
case Negation(f) => getVariables(f)
case BinaryConnective(f0, f1) => getVariables(f0) ++ getVariables(f1)
case _ => HashSet.empty[Variable]
}
def getVariables2(formula: Formula): Set[Variable] = {
def getVariables2(formula: Formula, set: mutable.HashSet[Variable]): Unit = formula match {
case v: Variable => set += v
case Negation(f) => getVariables2(f, set)
case BinaryConnective(f0, f1) => getVariables2(f0, set); getVariables2(f1, set)
case _ =>
}
val set = mutable.HashSet.empty[Variable]
getVariables2(formula, set)
set.toSet
}
The fastest way is almost always to use a builder. So, assuming no stack overflows:
def getVars(formula: Formula): Set[Variable] = {
val sb = Set.newBuilder[Variable]
def inner(formula: Formula) { formula match {
case v: Variable => sb += v
case Negation(f) => inner(f)
case BinaryConnective(f0, f1) => inner(f0); inner(f1)
case _ =>
}}
inner(formula)
sb.result
}
Your first version is probably the most elegant, however.
Note that if you may have very large formulas, this recursive solution could be in danger of stack overflows. The fix is relatively straightforward:
def getVars2(formula: Formula): Set[Variable] = {
val sb = Set.newBuilder[Variable]
def inner(formulas: List[Formula]) {
var more: List[Formula] = Nil
formulas.foreach{ _ match {
case v: Variable => sb += v
case Negation(f) => more = f :: more
case BinaryConnective(f0, f1) => more = f1 :: f0 :: more
case _ =>
}}
if (!more.isEmpty) inner(more)
}
inner(formula :: Nil)
sb.result
}
Your names are way too long to allow convenient typing of an interestingly non-tiny expression, but if we abbreviate to the capital letters, then:
BC(N(V('x)), BC(BC(V('a),V('x)),V('y)))
will run about 7x faster with getVars than your first solution; getVars2 is a little slower (only 4x faster).
(Benchmark timings are:
getVariables 1380 ns +- 20 ns
getVars 190 ns +- 10 ns
getVars2 360 ns +- 10 ns
)

Building variations of nested case classes

So I got something like this:
abstract class Term
case class App(f:Term,x:Term) extends Term
case class Var(s:String) extends Term
case class Amb(a:Term, b:Term) extends Term //ambiguity
And a Term may look like this:
App(Var(f),Amb(Var(x),Amb(Var(y),Var(z))))
So what I need is all variations that are indicated by the Amb class.
This is used to represent a ambiguous parse forest and I want to type check each possible variation and select the right one.
In this example I would need:
App(Var(f),Var(x))
App(Var(f),Var(y))
App(Var(f),Var(z))
Whats the best way to create these variations in scala?
Efficiency would be nice, but is not really requirement.
If possible I like to refrain from using reflection.
Scala provides pattern matching solve these kinds of problems. A solution would look like:
def matcher(term: Term): List[Term] = {
term match {
case Amb(a, b) => matcher(a) ++ matcher(b)
case App(a, b) => for { va <- matcher(a); vb <- matcher(b) } yield App(va, vb)
case v: Var => List(v)
}
}
You can do this pretty cleanly with a recursive function that traverses the tree and expands ambiguities:
sealed trait Term
case class App(f: Term, x: Term) extends Term
case class Var(s: String) extends Term
case class Amb(a: Term, b: Term) extends Term
def det(term: Term): Stream[Term] = term match {
case v: Var => Stream(v)
case App(f, x) => det(f).flatMap(detf => det(x).map(App(detf, _)))
case Amb(a, b) => det(a) ++ det(b)
}
Note that I'm using a sealed trait instead of an abstract class in order to take advantage of the compiler's ability to check exhaustivity.
It works as expected:
scala> val app = App(Var("f"), Amb(Var("x"), Amb(Var("y"), Var("z"))))
app: App = App(Var(f),Amb(Var(x),Amb(Var(y),Var(z))))
scala> det(app) foreach println
App(Var(f),Var(x))
App(Var(f),Var(y))
App(Var(f),Var(z))
If you can change the Term API, you could more or less equivalently add a def det: Stream[Term] method there.
Since my abstract syntax is fairly large (and I have multiple) and I tried my luck with Kiama.
So here is the version Travis Brown and Mark posted with Kiama.
Its not pretty, but I hope it works. Comments are welcome.
def disambiguateRule: Strategy = rule {
case Amb(a: Term, b: Term) =>
rewrite(disambiguateRule)(a).asInstanceOf[List[_]] ++
rewrite(disambiguateRule)(b).asInstanceOf[List[_]]
case x =>
val ch = getChildren(x)
if(ch.isEmpty) {
List(x)
}
else {
val chdis = ch.map({ rewrite(disambiguateRule)(_) }) // get all disambiguate children
//create all combinations of the disambiguated children
val p = combinations(chdis.asInstanceOf[List[List[AnyRef]]])
//use dup from Kiama to recreate the term with every combination
val xs = for { newchildren <- p } yield dup(x.asInstanceOf[Product], newchildren.toArray)
xs
}
}
def combinations(ll: List[List[AnyRef]]): List[List[AnyRef]] = ll match {
case Nil => Nil
case x :: Nil => x.map { List(_) }
case x :: xs => combinations(xs).flatMap({ ys => x.map({ xx => xx :: ys }) })
}
def getChildren(x: Any): List[Any] = {
val l = new ListBuffer[Any]()
all(queryf {
case a => l += a
})(x)
l.toList
}

Scala matching, resolving the same variable from two different patterns

Say I have the following
case class IntWrap(value:Int)
I would like to extract the same variable from two cases as follows:
x match {
case value:Int | IntWrap(value) => dosomethingwith(x)
case _ => ???
}
but the only way I have been able to do this is as:
x match {
case value:Int => dosomethingwith(x)
case IntWrap(value) => dosomethingwith(x)
case _ => ???
}
Is there a better way, as in my real life case dosomething is actually a large block of code which is not so easy to encapsulate.
If it is really the case that you want to do something with x, not with the extracted value, then the following would work:
case class IntWrap(value:Int) // extends T
def dosomethingwith(x: Any) = x
val x: Any = IntWrap(101)
x match {
case _: Int | _: IntWrap => dosomethingwith(x)
case _ => ???
}
If you actually want to work with the extracted value, you could factor out the corresponding match block into its own extractor and reuse that wherever necessary:
x match {
case Unwrap(value) => dosomethingwith(value)
case _ => ???
}
object Unwrap {
def unapply(x: Any) = x match {
case x: Int => Some((x))
case IntWrap(value) => Some((value))
case _ => None
}
}
I honestly don't see an issue with the way you are doing things. As long as dosomethingwith is a separate function then I don't see any issues with duplicate code. If your code looked like this then I don't see any need to come up with other solutions:
def foo(x:Any){
x match {
case value:Int => dosomethingwith(value)
case IntWrap(value) => dosomethingwith(value)
case _ => ???
}
}
def dosomethingwith(x:Int){
//do something complicated here...
}
I came up with sth a little bit different, but it may help you avoid duplicates:
case class IntWrap(value: Int)
implicit def intWrapToInt(intWrap: IntWrap) = intWrap.value
def matchInt(x: AnyVal) = x match {
case i: Int => println("int or intWrap")
case _ => println("other")
}
//test
matchInt(IntWrap(12)) //prints int or intWrap
matchInt(12) //prints int or intWrap
matchInt("abc") //prints other
It won't work for every reference, though. So, be careful.

Applying or operation to Option results

I have the following code:
class CSplit(var s1: CanvNode, var s2: CanvNode) extends SplitPane
{
topComponent = s1.merge
bottomComponent = s2.merge
def containsV(orig: MapCanvT): Option[MapCanvT] =
{
def containsIn(cn: CanvNode): Option[MapCanvT] = cn match
{
case Left => None
case Right(mc) => if (mc == orig) Some(mc) else None
}
containsIn(s1) match
{
case Some(mc) => Some(mc)
case None => containsIn(s2)
}
}
}
I want to reduce the code of the containsV method. My first thought was to use a fold method to shorten the containsIn method. But Option doesn't have one, nor does it extend Class Either. Shouldn't Option[T] extend Either[T, None] ? Then at least one could use Either's fold method.
My final thought was to treat s1 and s2 as a List and do find over it but I can't get this to compile:
def containsV(orig: MapCanvT):
Option[MapCanvT] = ::[CanvNode](s1, s2).find(_ == Right(orig))
Scala 2.10 adds fold to Option. In the meantime you can use map(f).getOrElse(g) instead:
// These produce identical results
o.fold(g)(x => f(x))
o.map(x => f(x)).getOrElse(g)
Edit: so, for example, the following three do the same thing:
val os: List[Option[Int]] = List(Some(5),None)
// Explicit match
os.map{ _ match {
case Some(x) => x+3
case None => 0
}}
// map+getOrElse
os.map{ _.map(_+3).getOrElse(0) }
// fold
os.map{ _.fold(0)(_+3) }
In the fold case, you give the default value for the None case first, and then the function that handles the case where there is a value. In each case you should get List(8,0).
It can be implemented with a list by using the collectFirst method
def containsV(orig: MapCanvT): Option[MapCanvT]
= List(s1, s2).collectFirst {case i: MapCanvT if (i == (orig) => i}
Let's start with the easy part:
containsIn(s1) match
{
case Some(mc) => Some(mc)
case None => containsIn(s2)
}
is the same as
containsIn(s1) orElse containsIn(s2)
Now we only have to deal with containsIn:
def containsIn(cn: CanvNode): Option[MapCanvT] = cn match
{
case Left => None
case Right(mc) => if (mc == orig) Some(mc) else None
}
We can use fold on Either, which gets rid of most of the pattern matching:
cn.fold(_ => None, Some(_))
But there's the orig thingy. We can handle it with a filter, though:
cn.fold(_ => None, Some(_)) filter (orig.==)
Thus:
def containsV(orig: MapCanvT): Option[MapCanvT] = {
def containsIn(cn: CanvNode): Option[MapCanvT] =
cn.fold(_ => None, Some(_)) filter (orig.==)
containsIn(s1) orElse containsIn(s2)
}
I think orElse is much overlooked.