Scala: Does reduceLeft and reduceRight have accumulator at different positions? - scala

I am a littlebit confused between the methods reduceleft and reduceRight in Scala. Here's a Snippet that I am testing:
val intList = List(5, 4, 3, 2, 1);
println(intList.reduceRight((curr, acc) => { //Reduce right me y is accumulator
println(s"First Curr = $curr, Acc = $acc")
curr - acc
}));
println(intList.reduceLeft((curr, acc) => { // List(1,2,3,4,5) // Accumulator is the first Element
println(s"Second Curr = $curr, Acc = $acc")
acc - curr}))
And the Output is as shown below:
First Curr = 2, Acc = 1
First Curr = 3, Acc = 1
First Curr = 4, Acc = 2
First Curr = 5, Acc = 2
3
Second Curr = 5, Acc = 4
Second Curr = -1, Acc = 3
Second Curr = 4, Acc = 2
Second Curr = -2, Acc = 1
In both the iterations what I observe is that in case of reduceRight we have (curr,acc) [Meaning curr is passed as first argument and accumulator as second], whereas in case of reduceLeft we have (acc,curr).
Is there any specific reason behind this inconsistency in the arguments?

Let's use a little visualization:
reduceLeft:
a b c d e
acc1 = f(a, b) \/ / / /
acc2 = f(acc1, c) \/ / /
acc3 = f(acc2, d) \/ /
acc4 = f(acc3, e) \/
reduceRight:
a b c d e
\ \ \ \/ acc1 = f(d, e)
\ \ \/ acc2 = f(c, acc1)
\ \/ acc3 = f(b, acc2)
\/ acc4 = f(a, acc3)
The order of arguments help us remember the where the arguments came from and in which order they were evaluated. Because this is quite important to not confuse left-associative and right-associative operations:
any visual help matters and writing acc on the correct side of function makes reasoning easier
lambdas in coll.reduceLeft(_ operation _) and coll.reduceRight(_ operation _) would have very confusing behavior if acc were on left in both cases
Imagine if you defined a right-associative operator e.g. a ** b (a to the power of b). Exponentiation in math is right-associative, so a ** b ** c should behave like a ** (b ** c). Someone decides to calculate such use case using rightReduce, because it implements exactly this behavior. They do
List(a, b, c).reduceRight(_ ** _)
and then they learn that someone decided that acc should be on left side because they wanted it to be "consistent" with reduceLeft. That would be much more inconsistent with every intuitions that you carried from mathematics.

Related

Dynamic sliding window in Scala

Suppose, I have a log file of events (page visits) with a timestamp. I'd like to group events into sessions where I consider that events belong to the same session when they are not further than X minutes from each other.
Currently, I ended up with this algorithm.
val s = List(1000, 501, 500, 10, 3, 2, 1) // timestamps
val n = 10 // time span
import scala.collection.mutable.ListBuffer
(s.head +: s).sliding(2).foldLeft(ListBuffer.empty[ListBuffer[Int]]) {
case (acc, List(a, b)) if acc.isEmpty =>
acc += ListBuffer(a)
acc
case (acc, List(a, b)) =>
if (n >= a - b) {
acc.last += b
acc
} else {
acc += ListBuffer(b)
acc
}
}
The result
ListBuffer(ListBuffer(1000), ListBuffer(501, 500), ListBuffer(10, 3, 2, 1))
Is there any better/functional/efficient way to do it?
Slightly adapting this answer by altering the condition ...
s.foldRight[List[List[Int]]](Nil)((a, b) => b match {
case (bh # bhh :: _) :: bt if (bhh + n >= a) => (a :: bh) :: bt
case _ => (a :: Nil) :: b
})

How to reduce with a non-associative function via adjacent pairs

How can I reduce a collection via adjacent pairs?
For instance, let + be a non-associative operator:
(1, 2, 3, 4, 5, 6) => ((1+2) + (3+4)) + (5+6)
(a, b, c, d, e, f, g, h) => ((a+b) + (c+d)) + ((e+f) + (g+h))
This question is similar to this, however I don't think parallel collections apply because the semantics require an associative operator for determinism. I'm not concerned so much about parallel execution as I am about the actually associating such that it constructs a balanced expression tree.
Here is a version that does what you want, but treats the "remaining" elements somewhat arbitrarily (if the number of inputs in current iteration is odd, one element is left as-is):
def nonassocPairwiseReduce[A](xs: List[A], op: (A, A) => A): A = {
xs match {
case Nil => throw new IllegalArgumentException
case List(singleElem) => singleElem
case sthElse => {
val grouped = sthElse.grouped(2).toList
val pairwiseOpd = for (g <- grouped) yield {
g match {
case List(a, b) => op(a, b)
case List(x) => x
}
}
nonassocPairwiseReduce(pairwiseOpd, op)
}
}
}
For example, if this is your non-associative operation on Strings:
def putInParentheses(a: String, b: String) = s"(${a} + ${b})"
then your examples
for {
example <- List(
('1' to '6').toList.map(_.toString),
('a' to 'h').toList.map(_.toString)
)
} {
println(nonassocPairwiseReduce(example, putInParentheses))
}
are mapped to
(((1 + 2) + (3 + 4)) + (5 + 6))
(((a + b) + (c + d)) + ((e + f) + (g + h)))
Would be interesting to know why you want to do this.

Confused about merge sort implementation

What is occurring on this line, x is being concatenated to xs1 but x and xs1 are not defined anywhere?
case (x :: xs1, y :: ys1) =>
Also here, what value do have x and y below? Is merge being recursively called as part of the case class?
if( x < y) x :: merge(xs1 , ys)
Here is the complete Scala code :
object mergesort {
def msort(xs: List[Int]): List[Int] = {
val n = xs.length / 2
if(n == 0) xs
else {
def merge(xs: List[Int], ys: List[Int]): List[Int] = (xs , ys) match {
case (Nil, ys) => ys
case (xs, Nil) => xs
case (x :: xs1, y :: ys1) =>
if( x < y) x :: merge(xs1 , ys)
else y :: merge(xs, ys1)
}
val (fst, snd) = xs splitAt n
merge(msort(fst), msort(snd))
}
} //> msort: (xs: List[Int])List[Int]
val nums = List(2, -4, 5, 7, 1) //> nums : List[Int] = List(2, -4, 5, 7, 1)
msort(nums) //> res0: List[Int] = List(-4, 1, 2, 5, 7)
}
In
case (x :: xs1, y :: ys1) =>
:: is a syntactic sugar in pattern matching to de-construct a list in to head and tail
the list xs is de-constructed in to head x and tail xs.
In pattern matching :: de-constructs' a list, exact reverse of what it actually does in normal, construct a list.
Read De-Constructing objects in The Point of Pattern Matching in Scala
This
(xs , ys) match {
...
case (x :: xs1, y :: ys1)
is a pattern match that declares the variables x, xs1 etc. in the same statement as asserting a sequence match.
The code above is checking that xs can be decomposed into a sequence with head x and tail xs1, and if so, making the head/tail available to the successive code block in those two variables.
To answer your second question (since nobody else has!), yes, the merge function (declared within the outer function) is being called recursively.
Here's an example of how scala allows you to do pattern matching on a List:
scala> List(1,2,3)
res0: List[Int] = List(1, 2, 3)
scala> res0 match {
| case h :: t => "more than two elements, " + h + " is the first"
| case _ => "less than two elements"
| }
res1: java.lang.String = more than two elements, 1 is the first
Note that :: on the left side of the case decomposes the list in its head ( 1 ) and its tail (the rest of the list 2, 3) and binds the values to h and t, that are created and scoped only inside the first case.
Here's how you decompose a tuple:
scala> val tp = ("a", 1)
tp: (java.lang.String, Int) = (a,1)
scala> tp match {
| case (a, b) => a + " is a string, " + b + " is a number"
| case _ => "something missing"
| }
res2: java.lang.String = a is a string, 1 is a number
In the code in your question you're mixing both things and pattern matching on a tuple of Lists (xs , ys).
case (x :: xs1, y :: ys1) is both decomposing the tuple in its two lists and decomposing its two lists in their respective heads and tails.
The match-case keywords are used in scala to perform pattern matching, which is a way to match/decompose objects using several mechanisms like case classes and extractors. Google for scala pattern matching and you'll find the answers you need.

Scala Get First and Last elements of List using Pattern Matching

I am doing a pattern matching on a list. Is there anyway I can access the first and last element of the list to compare?
I want to do something like..
case List(x, _*, y) if(x == y) => true
or
case x :: _* :: y =>
or something similar...
where x and y are first and last elements of the list..
How can I do that.. any Ideas?
Use the standard :+ and +: extractors from the scala.collection package
ORIGINAL ANSWER
Define a custom extractor object.
object :+ {
def unapply[A](l: List[A]): Option[(List[A], A)] = {
if(l.isEmpty)
None
else
Some(l.init, l.last)
}
}
Can be used as:
val first :: (l :+ last) = List(3, 89, 11, 29, 90)
println(first + " " + l + " " + last) // prints 3 List(89, 11, 29) 90
(For your case: case x :: (_ :+ y) if(x == y) => true)
In case you missed the obvious:
case list # (head :: tail) if head == list.last => true
The head::tail part is there so you don’t match on the empty list.
simply:
case head +: _ :+ last =>
for example:
scala> val items = Seq("ham", "spam", "eggs")
items: Seq[String] = List(ham, spam, eggs)
scala> items match {
| case head +: _ :+ last => Some((head, last))
| case List(head) => Some((head, head))
| case _ => None
| }
res0: Option[(String, String)] = Some((ham,eggs))
Lets understand the concept related to this question, there is a difference between '::', '+:' and ':+':
1st Operator:
'::' - It is right associative operator which works specially for lists
scala> val a :: b :: c = List(1,2,3,4)
a: Int = 1
b: Int = 2
c: List[Int] = List(3, 4)
2nd Operator:
'+:' - It is also right associative operator but it works on seq which is more general than just list.
scala> val a +: b +: c = List(1,2,3,4)
a: Int = 1
b: Int = 2
c: List[Int] = List(3, 4)
3rd Operator:
':+' - It is also left associative operator but it works on seq which is more general than just list
scala> val a :+ b :+ c = List(1,2,3,4)
a: List[Int] = List(1, 2)
b: Int = 3
c: Int = 4
The associativity of an operator is determined by the operator’s last character. Operators ending in a colon ‘:’ are right-associative. All other operators are left-associative.
A left-associative binary operation e1;op;e2 is interpreted as e1.op(e2)
If op is right-associative, the same operation is interpreted as { val x=e1; e2.op(x) }, where x is a fresh name.
Now comes answer for your question:
So now if you need to get first and last element from the list, please use following code
scala> val firstElement +: b :+ lastElement = List(1,2,3,4)
firstElement: Int = 1
b: List[Int] = List(2, 3)
lastElement: Int = 4

Expand a Set[Set[String]] into Cartesian Product in Scala

I have the following set of sets. I don't know ahead of time how long it will be.
val sets = Set(Set("a","b","c"), Set("1","2"), Set("S","T"))
I would like to expand it into a cartesian product:
Set("a&1&S", "a&1&T", "a&2&S", ..., "c&2&T")
How would you do that?
I think I figured out how to do that.
def combine(acc:Set[String], set:Set[String]) = for (a <- acc; s <- set) yield {
a + "&" + s
}
val expanded = sets.reduceLeft(combine)
expanded: scala.collection.immutable.Set[java.lang.String] = Set(b&2&T, a&1&S,
a&1&T, b&1&S, b&1&T, c&1&T, a&2&T, c&1&S, c&2&T, a&2&S, c&2&S, b&2&S)
Nice question. Here's one way:
scala> val seqs = Seq(Seq("a","b","c"), Seq("1","2"), Seq("S","T"))
seqs: Seq[Seq[java.lang.String]] = List(List(a, b, c), List(1, 2), List(S, T))
scala> val seqs2 = seqs.map(_.map(Seq(_)))
seqs2: Seq[Seq[Seq[java.lang.String]]] = List(List(List(a), List(b), List(c)), List(List(1), List(2)), List(List(S), List(T)))
scala> val combined = seqs2.reduceLeft((xs, ys) => for {x <- xs; y <- ys} yield x ++ y)
combined: Seq[Seq[java.lang.String]] = List(List(a, 1, S), List(a, 1, T), List(a, 2, S), List(a, 2, T), List(b, 1, S), List(b, 1, T), List(b, 2, S), List(b, 2, T), List(c, 1, S), List(c, 1, T), List(c, 2, S), List(c, 2, T))
scala> combined.map(_.mkString("&"))
res11: Seq[String] = List(a&1&S, a&1&T, a&2&S, a&2&T, b&1&S, b&1&T, b&2&S, b&2&T, c&1&S, c&1&T, c&2&S, c&2&T)
Came after the batle ;) but another one:
sets.reduceLeft((s0,s1)=>s0.flatMap(a=>s1.map(a+"&"+_)))
Expanding on dsg's answer, you can write it more clearly (I think) this way, if you don't mind the curried function:
def combine[A](f: A => A => A)(xs:Iterable[Iterable[A]]) =
xs reduceLeft { (x, y) => x.view flatMap { y map f(_) } }
Another alternative (slightly longer, but much more readable):
def combine[A](f: (A, A) => A)(xs:Iterable[Iterable[A]]) =
xs reduceLeft { (x, y) => for (a <- x.view; b <- y) yield f(a, b) }
Usage:
combine[String](a => b => a + "&" + b)(sets) // curried version
combine[String](_ + "&" + _)(sets) // uncurried version
Expanding on #Patrick's answer.
Now it's more general and lazier:
def combine[A](f:(A, A) => A)(xs:Iterable[Iterable[A]]) =
xs.reduceLeft { (x, y) => x.view.flatMap {a => y.map(f(a, _)) } }
Having it be lazy allows you to save space, since you don't store the exponentially many items in the expanded set; instead, you generate them on the fly. But, if you actually want the full set, you can still get it like so:
val expanded = combine{(x:String, y:String) => x + "&" + y}(sets).toSet