Consider the following standard class:
class Node(val data: NodeData, val children: Seq[Node])
The NodeData class is also simple:
case class NodeData(text: String, foo: List[Bar])
Also, the tree has arbitrary depth, it's not fixed.
Clearly, implementing a breath-first or depth-first search on that structure is trivial with idiomatic Scala. However, consider that I want not only to visit each of these nodes, but I also want to mutate them on each visit. More concretely, I want to mutate an object in that foo list. How would I go about implementing this? One way I thought about this is to somehow update the nodes and build a new tree while traversing it, but my intuition tells me there is a simpler solution than that.
If you really want to stay immutable, I would define a function recMap on Node, like this:
def recMap(f: NodeData => NodeData) : Node = Node(f(data), children.map(_.recMap(f)))
You could then use it like in this example (I made Node a case class too):
type Bar = Int
case class NodeData(text: String, foo: List[Bar])
case class Node(data: NodeData, children: Seq[Node]) {
def recMap(f: NodeData => NodeData) : Node = Node(f(data), children.map(_.recMap(f)))
}
val tree = new Node(NodeData("parent", List(1, 2, 3, 4)), Seq(
Node(NodeData("a child", List(5, 6, 7, 8)), Seq.empty),
Node(NodeData("another child", List(9, 10, 11, 12)), Seq.empty)
))
val modifiedTree = tree.recMap(
data => NodeData(
if(data.text == "parent") "I am the parent!" else "I am a child!",
data.foo.filter(_ % 2 == 0)
)
)
println(modifiedTree)
Try it out!
Maybe that's what you are searching for.
Related
Given the following Scala 2.9.2 code:
Updated with non-working example
import collection.immutable.SortedSet
case class Bar(s: String)
trait Foo {
val stuff: SortedSet[String]
def makeBars(bs: Map[String, String])
= stuff.map(k => Bar(bs.getOrElse(k, "-"))).toList
}
case class Bazz(rawStuff: List[String]) extends Foo {
val stuff = SortedSet(rawStuff: _*)
}
// test it out....
val b = Bazz(List("A","B","C"))
b.makeBars(Map("A"->"1","B"->"2","C"->"3"))
// List[Bar] = List(Bar(1), Bar(2), Bar(3))
// Looks good?
// Make a really big list not in order. This is why we pass it to a SortedSet...
val data = Stream.continually(util.Random.shuffle(List("A","B","C","D","E","F"))).take(100).toList
val b2 = Bazz(data.flatten)
// And how about a sparse map...?
val bs = util.Random.shuffle(Map("A" -> "1", "B" -> "2", "E" -> "5").toList).toMap
b2.makeBars(bs)
// res24: List[Bar] = List(Bar(1), Bar(2), Bar(-), Bar(5))
I've discovered that, in some cases, the makeBars method of classes extending Foo does not return a sorted List. In fact, the list ordering does not reflect the ordering of the SortedSet
What am I missing about the above code where Scala will not always map a SortedSet to a List with elements ordered by the SortedSet ordering?
You're being surprised by implicit resolution.
The map method requires a CanBuildFrom instance that's compatible with the target collection type (in simple cases, identical to the source collection type) and the mapper function's return type.
In the particular case of SortedSet, its implicit CanBuildFrom requires that an Ordering[A] (where A is the return type of the mapper function) be available. When your map function returns something that the compiler already knows how to find an Ordering for, you're good:
scala> val ss = collection.immutable.SortedSet(10,9,8,7,6,5,4,3,2,1)
ss: scala.collection.immutable.SortedSet[Int] = TreeSet(1, 2, 3, 4, 5,
6, 7, 8, 9, 10)
scala> val result1 = ss.map(_ * 2)
result1: scala.collection.immutable.SortedSet[Int] = TreeSet(2, 4, 6, 8, 10,
12, 14, 16, 18, 20)
// still sorted because Ordering[Int] is readily available
scala> val result2 = ss.map(_ + " is a number")
result2: scala.collection.immutable.SortedSet[String] = TreeSet(1 is a number,
10 is a number,
2 is a number,
3 is a number,
4 is a number,
5 is a number,
6 is a number,
7 is a number,
8 is a number,
9 is a number)
// The default Ordering[String] is an "asciibetical" sort,
// so 10 comes between 1 and 2. :)
However, when your mapper function turns out to return a type for which no Ordering is known, the implicit on SortedSet doesn't match (specifically, no value can be found for its implicit parameter), so the compiler looks "upward" for a compatible CanBuildFrom and finds the generic one from Set.
scala> case class Foo(i: Int)
defined class Foo
scala> val result3 = ss.map(Foo(_))
result3: scala.collection.immutable.Set[Foo] = Set(Foo(10), Foo(4), Foo(6), Foo(7), Foo(1), Foo(3), Foo(5), Foo(8), Foo(9), Foo(2))
// The default Set is a hash set, therefore ordering is not preserved
Of course, you can get around this by simply supplying an instance of Ordering[Foo] that does whatever you expect:
scala> implicit val fooIsOrdered: Ordering[Foo] = Ordering.by(_.i)
fooIsOrdered: Ordering[Foo] = scala.math.Ordering$$anon$9#7512dbf2
scala> val result4 = ss.map(Foo(_))
result4: scala.collection.immutable.SortedSet[Foo] = TreeSet(Foo(1), Foo(2),
Foo(3), Foo(4), Foo(5),
Foo(6), Foo(7), Foo(8),
Foo(9), Foo(10))
// And we're back!
Finally, note that toy examples often don't exhibit the problem, because the Scala collection library has special implementations for small (n <= 6) Sets and Maps.
You're probably making assumption about what SortedSet does from Java. You need to specify what order you want the elements to be in. See http://www.scala-lang.org/docu/files/collections-api/collections_8.html
I've got a CSV response from a service and I want to generate a list of case classes. For example:
case class MyCaseClass(e1: String, e2: String, e3: String)
val body = getLargeCsvFromServiceOrSomething()
val elements = body.split(",")
Now I have an Array[String]. I want to take that large array and break it down into 3 element chucks, so I can generate my List[MyCaseClass], where each instance take 3 elements from the array. Is there a method similar to splitAt, but spits every n elements? I'm sure I can do this point-free, but it's just not coming to me.
What you want is grouped:
scala> List(1,2,3,4,5,6,7).grouped(3).toList
res0: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6), List(7))
So your thing might be like:
val elements = Array("a","b","c","d","e","f")
val classes = elements.grouped(3).map{ case Array(a,b,c) => MyCaseClass(a,b,c) }
println(classes.toList) // List(MyCaseClass(a,b,c), MyCaseClass(d,e,f))
I come from Groovy and it has a .with method on every type which accepts a single-argument closure; the argument is the object on which the .with method is being called. This allows a very cool technique of extending the functional chaining capabilities, which releases you from obligation to introduce temporary variables, factors your code, makes it easier to read and does other niceties.
I want to be able to do something like this:
Seq(1, 2, 3, 4, 5)
.filter(_ % 2 == 0)
.with(it => if (!it.isEmpty) println(it))
Instead of
val yetAnotherMeaninglessNameForTemporaryVariable =
Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0)
if (!yetAnotherMeaninglessNameForTemporaryVariable.isEmpty)
println(yetAnotherMeaninglessNameForTemporaryVariable)
In other words in the first example the .with is kinda similar to .foreach but instead of iterating thru the items of the object it is being called once on the object itself. So it is equal to Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0).
Since I was very surprised not to find anything like that in Scala, my questions are:
am I missing something?
are there any alternative techniques native to Scala?
if not, are there any decent reasons why this feature is not implemented in Scala?
Update:
An appropriate feature request has been posted on the Scala issue tracker: https://issues.scala-lang.org/browse/SI-5324. Please vote and promote
There doesn't exist any such method in the standard library, but it's not hard to define your own.
implicit def aW[A](a: A) = new AW(a)
class AW[A](a: A) {
def tap[U](f: A => U): A = {
f(a)
a
}
}
val seq = Seq(2, 3, 11).
map(_ * 3).tap(x => println("After mapping: " + x)).
filter(_ % 2 != 0).tap(x => println("After filtering: " + x))
EDIT: (in response to the comment)
Oh, I misunderstood. What you need is there in the Scalaz library. It comes under name |> (referred to as pipe operator). With that, your example would look like shown below:
Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0) |> { it => if(!it.isEmpty) println(it) }
If you cannot use Scalaz, you can define the operator on your own:
implicit def aW[A](a: A) = new AW(a)
class AW[A](a: A) {
def |>[B](f: A => B): B = f(a)
}
And it's not a bad practice to pimp useful method(s) on existing types. You should use implicit conversions sparingly, but I think these two combinators are common enough for their pimps to be justifiable.
There is some syntax for this pattern included in Scala:
Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0) match { case it => if (!it.isEmpty) println(it) }
However, this is no accepted idiom so you should maybe refrain from (ab)using it.
If you dislike inventing loads and loads of names for dummy variables, remember that you can use scope braces:
val importantResult = {
val it = Seq(1,2,3).filter(_ % 2 == 0)
if (!it.isEmpty) println(it)
it
}
val otherImportantResultWithASpeakingVariableName = {
val it = // ...
/* ... */
it
}
Try sth like this.
println(Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0).ensuring(!_.isEmpty))
Throws an assertion exception if the condition is not met.
Remember call by name? Perhaps it gives you the capablity you want:
object Test {
def main(args: Array[String]) {
delayed(time());
}
def time() = {
println("Getting time in nano seconds")
System.nanoTime
}
def delayed( t: => Long ) = {
println("In delayed method")
println("Param: " + t)
t
}
}
as described in http://www.tutorialspoint.com/scala/functions_call_by_name.htm
Although I like other solutions better (as they are more local and therefore easier to follow), do not forget that you can
{ val x = Seq(1,2,3,4,5).filter(_ % 2 == 0); println(x); x }
to avoid name collisions on your meaningless variables and keep them constrained to the appropriate scope.
This is just function application f(x) flipped on its head: x.with(f)... If you're looking for an idiomatic way of doing with in Scala, un-flip it:
(it => if (!it.isEmpty) println(it)) (Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0))
Similarly, if you want x.with(f).with(g), just use g(f(x))...
In Scala, calling groupBy() on a collection returns a Map where the values are collections, but I want a MultiMap. What's the easiest way to do the conversion? Can I avoid creating a new MultiMap and copying everything over?
I think the answer to "Do I have to create a new object to mix in a Scala trait?" is "Yes". You can minimize the pain some with wrapping objects and implicit conversions.
For your specific problem, I was unable to coerce groupBy(...) to return a mutable map to mutable sets, which you would need to wrap it with "MapProxy with MultiMap". But, it is not too many lines of code to implement your own version of "groupBy":
package blevins.example
object App extends Application {
implicit def multiMapable[B](c: Iterable[B]) = new {
def groupByMM[A](f: B => A) = {
import scala.collection.mutable._
val ret = new HashMap[A,Set[B]] with MultiMap[A,B]
for (e <- c) { ret.addBinding(f(e), e) }
ret
}
}
val c = List(1,2,3,4,5,6,7,8,9)
val mm = c.groupByMM { i => if (i < 5) "alpha" else "beta" }
mm.addBinding("alpha",12)
println(mm) // Map(beta -> Set(5, 7, 6, 9, 8), alpha -> Set(3, 1, 4, 2, 12))
}
Addendum
Here is an example of wrapping an existing Map[String,Set[Int]] into a MultiMap without copying the values:
object App extends Application {
import scala.collection.mutable._
val seed: Map[String,Set[Int]] = Map("even" -> Set(2,4,6), "odd" -> Set(1,3,5))
val multiMap = new MapProxy[String,Set[Int]] with MultiMap[String,Int] {
val self = seed
}
multiMap.addBinding("even", 8)
println(multiMap) // Map(odd -> Set(5, 3, 1), even -> Set(6, 8, 4, 2))
}
Note that this cannot be done on the result of groupBy(...) because the seed map is required to be mutable and groupBy(...) returns an immutable map.
I've recently been working on a beginner's project in Scala, and have a beginner question about Scala's Lists.
Say I have a list of tuples ( List[Tuple2[String, String]], for example). Is there a convenience method to return the first occurence of a specified tuple from the List, or is it necessary to iterate through the list by hand?
scala> val list = List(("A", "B", 1), ("C", "D", 1), ("E", "F", 1), ("C", "D", 2), ("G", "H", 1))
list: List[(java.lang.String, java.lang.String, Int)] = List((A,B,1), (C,D,1), (E,F,1), (C,D,2), (G,H,1))
scala> list find {e => e._1 == "C" && e._2 == "D"}
res0: Option[(java.lang.String, java.lang.String, Int)] = Some((C,D,1))
You could try using find. (Updated scala-doc location of find)
As mentioned in a previous comment, find is probably the easiest way to do this. There are actually three different "linear search" methods in Scala's collections, each returning a slightly different value. Which one you use depends upon what you need the data for. For example, do you need an index, or do you just need a boolean true/false?
If you're learning scala, I'd take a good look at the Seq trait. It provides the basis for much of scala's functional goodness.
You could also do this, which doesn't require knowing the field names in the Tuple2 class--it uses pattern matching instead:
list find { case (x,y,_) => x == "C" && y == "D" }
"find" is good when you know you only need one; if you want to find all matching elements you could either use "filter" or the equivalent sugary for comprehension:
for ( (x,y,z) <- list if x == "C" && y == "D") yield (x,y,z)
Here's code that may help you.
I had a similar case, having a collection of base class entries (here, A) out of which I wanted to find a certain derived class's node, if any (here, B).
class A
case class B(val name: String) extends A
object TestX extends App {
val states: List[A] = List( B("aa"), new A, B("ccc") )
def findByName( name: String ): Option[B] = {
states.find{
case x: B if x.name == name => return Some(x)
case _ => false
}
None
}
println( findByName("ccc") ) // "Some(B(ccc))"
}
The important part here (for my app) is that findByName does not return Option[A] but Option[B].
You can easily modify the behaviour to return B instead, and throw an exception if none was found. Hope this helps.
Consider collectFirst which delivers Some[(String,String)] for the first matching tuple or None otherwise, for instance as follows,
xs collectFirst { case t#(a,_) if a == "existing" => t }
Some((existing,str))
scala> xs collectFirst { case t#(a,_) if a == "nonExisting" => t }
None
Using # we bind the value of the tuple to t so that a whole matching tuple can be collected.