zipWith in Scala using map - scala

Define zipWith. It should zip two lists, but instead of zipping elements into a tuple,
it should use a function to combine two elements.
Example: zipWith(List(1, 2, 3),
List(10, 11, 12),
(x: Int, y: Int) => x+y)
Should return: List(11,13,15)
use map and zip.
def zipWith[A,B,C](xs: List[A], ys: List[B], f: (A, B) => C): List[C] = {
val zs = xs.zip(ys)
//I don't know how to do this because if myMap(zs, f)
//myMap takes a functin f:(a)=>b instead of f: (A, B) => C
}
}

It sounds like you looking for something like this:
def zipWith[A,B,C](xs: List[A], ys: List[B], f: (A, B) => C): List[C] = {
(xs, ys) match {
case (Nil, _) => Nil
case (_, Nil) => Nil
case (x :: xs, y :: ys) => f(x, y) :: zipWith(xs, ys, f)
}
}
Hope that helps.
Update
Here is the same function but being tail-recursive:
def zipWith[A, B, C](xs: List[A], ys: List[B], f: (A, B) => C): List[C] = {
#tailrec
def zipAccumulatingResult(xs: List[A], ys: List[B], f: (A, B) => C, acc: List[C]): List[C] = {
(xs, ys) match {
case (Nil, _) => acc
case (_, Nil) => acc
case (x :: xs, y :: ys) => zipAccumulatingResult(xs, ys, f, acc :+ f(x, y))
}
}
zipAccumulatingResult(xs, ys, f, Nil)
}

Related

Scala foldLeft while some conditions are true

How to emulate following behavior in Scala? i.e. keep folding while some certain conditions on the accumulator are met.
def foldLeftWhile[B](z: B, p: B => Boolean)(op: (B, A) => B): B
For example
scala> val seq = Seq(1, 2, 3, 4)
seq: Seq[Int] = List(1, 2, 3, 4)
scala> seq.foldLeftWhile(0, _ < 3) { (acc, e) => acc + e }
res0: Int = 1
scala> seq.foldLeftWhile(0, _ < 7) { (acc, e) => acc + e }
res1: Int = 6
UPDATES:
Based on #Dima answer, I realized that my intention was a little bit side-effectful. So I made it synchronized with takeWhile, i.e. there would be no advancement if the predicate does not match. And add some more examples to make it clearer. (Note: that will not work with Iterators)
First, note that your example seems wrong. If I understand correctly what you describe, the result should be 1 (the last value on which the predicate _ < 3 was satisfied), not 6
The simplest way to do this is using a return statement, which is very frowned upon in scala, but I thought, I'd mention it for the sake of completeness.
def foldLeftWhile[A, B](seq: Seq[A], z: B, p: B => Boolean)(op: (B, A) => B): B = foldLeft(z) { case (b, a) =>
val result = op(b, a)
if(!p(result)) return b
result
}
Since we want to avoid using return, scanLeft might be a possibility:
seq.toStream.scanLeft(z)(op).takeWhile(p).last
This is a little wasteful, because it accumulates all (matching) results.
You could use iterator instead of toStream to avoid that, but Iterator does not have .last for some reason, so, you'd have to scan through it an extra time explicitly:
seq.iterator.scanLeft(z)(op).takeWhile(p).foldLeft(z) { case (_, b) => b }
It is pretty straightforward to define what you want in scala. You can define an implicit class which will add your function to any TraversableOnce (that includes Seq).
implicit class FoldLeftWhile[A](trav: TraversableOnce[A]) {
def foldLeftWhile[B](init: B)(where: B => Boolean)(op: (B, A) => B): B = {
trav.foldLeft(init)((acc, next) => if (where(acc)) op(acc, next) else acc)
}
}
Seq(1,2,3,4).foldLeftWhile(0)(_ < 3)((acc, e) => acc + e)
Update, since the question was modified:
implicit class FoldLeftWhile[A](trav: TraversableOnce[A]) {
def foldLeftWhile[B](init: B)(where: B => Boolean)(op: (B, A) => B): B = {
trav.foldLeft((init, false))((a,b) => if (a._2) a else {
val r = op(a._1, b)
if (where(r)) (op(a._1, b), false) else (a._1, true)
})._1
}
}
Note that I split your (z: B, p: B => Boolean) into two higher-order functions. That's just a personal scala style preference.
What about this:
def foldLeftWhile[A, B](z: B, xs: Seq[A], p: B => Boolean)(op: (B, A) => B): B = {
def go(acc: B, l: Seq[A]): B = l match {
case h +: t =>
val nacc = op(acc, h)
if(p(nacc)) go(op(nacc, h), t) else nacc
case _ => acc
}
go(z, xs)
}
val a = Seq(1,2,3,4,5,6)
val r = foldLeftWhile(0, a, (x: Int) => x <= 3)(_ + _)
println(s"$r")
Iterate recursively on the collection while the predicate is true, and then return the accumulator.
You cand try it on scalafiddle
After a while I received a lot of good looking answers. So, I combined them to this single post
a very concise solution by #Dima
implicit class FoldLeftWhile[A](seq: Seq[A]) {
def foldLeftWhile[B](z: B)(p: B => Boolean)(op: (B, A) => B): B = {
seq.toStream.scanLeft(z)(op).takeWhile(p).lastOption.getOrElse(z)
}
}
by #ElBaulP (I modified a little bit to match comment by #Dima)
implicit class FoldLeftWhile[A](seq: Seq[A]) {
def foldLeftWhile[B](z: B)(p: B => Boolean)(op: (B, A) => B): B = {
#tailrec
def foldLeftInternal(acc: B, seq: Seq[A]): B = seq match {
case x :: _ =>
val newAcc = op(acc, x)
if (p(newAcc))
foldLeftInternal(newAcc, seq.tail)
else
acc
case _ => acc
}
foldLeftInternal(z, seq)
}
}
Answer by me (involving side effects)
implicit class FoldLeftWhile[A](seq: Seq[A]) {
def foldLeftWhile[B](z: B)(p: B => Boolean)(op: (B, A) => B): B = {
var accumulator = z
seq
.map { e =>
accumulator = op(accumulator, e)
accumulator -> e
}
.takeWhile { case (acc, _) =>
p(acc)
}
.lastOption
.map { case (acc, _) =>
acc
}
.getOrElse(z)
}
}
Fist exemple: predicate for each element
First you can use inner tail recursive function
implicit class TravExt[A](seq: TraversableOnce[A]) {
def foldLeftWhile[B](z: B, f: A => Boolean)(op: (A, B) => B): B = {
#tailrec
def rec(trav: TraversableOnce[A], z: B): B = trav match {
case head :: tail if f(head) => rec(tail, op(head, z))
case _ => z
}
rec(seq, z)
}
}
Or short version
implicit class TravExt[A](seq: TraversableOnce[A]) {
#tailrec
final def foldLeftWhile[B](z: B, f: A => Boolean)(op: (A, B) => B): B = seq match {
case head :: tail if f(head) => tail.foldLeftWhile(op(head, z), f)(op)
case _ => z
}
}
Then use it
val a = List(1, 2, 3, 4, 5, 6).foldLeftWhile(0, _ < 3)(_ + _)
//a == 3
Second example: for accumulator value:
implicit class TravExt[A](seq: TraversableOnce[A]) {
def foldLeftWhile[B](z: B, f: A => Boolean)(op: (A, B) => B): B = {
#tailrec
def rec(trav: TraversableOnce[A], z: B): B = trav match {
case _ if !f(z) => z
case head :: tail => rec(tail, op(head, z))
case _ => z
}
rec(seq, z)
}
}
Or short version
implicit class TravExt[A](seq: TraversableOnce[A]) {
#tailrec
final def foldLeftWhile[B](z: B, f: A => Boolean)(op: (A, B) => B): B = seq match {
case _ if !f(z) => z
case head :: tail => tail.foldLeftWhile(op(head, z), f)(op)
case _ => z
}
}
Simply use a branch condition on the accumulator:
seq.foldLeft(0, _ < 3) { (acc, e) => if (acc < 3) acc + e else acc}
However you will run every entry of the sequence.

Inline function ambiguity in Scala

When passing an operator lifted to be a function to one of defined higher order functions, Scala allows for very concise syntax, e.g (please ignore the fact that it can be simplified to .product()):
List(1,2,3).fold(1)(_ * _)
To the above I can just pass _ \* _
However having defined my own toy function zipWith(), I need to be very explicit when passing a function:
implicit class EnrichedList[A](val self: List[A]) extends AnyVal {
def zipWith[B, C](that: List[B])
(implicit zipper: A => B => C): List[C] = {
def zipWithHelper(zipper: A => B => C)
(as: List[A])
(bs: List[B]): List[C] = {
(as, bs) match {
case (_, Nil) => Nil
case (Nil, _) => Nil
case (a :: restOfA, b :: restOfB) =>
zipper(a)(b) :: zipWithHelper(zipper)(restOfA)(restOfB)
}
}
zipWithHelper(zipper)(self)(that)
}
}
This: List(1, 3, 4).zipWith(List(3, 4, 5))(_ * _) will not work, saying
Error:(60, 46) missing parameter type for expanded function ((x$1: , x$2) => x$1.$times(x$2))
List(1, 3, 4).zipWith(List(3, 4, 5))(_ * _)
I need to say what type of arguments function takes:
List(1, 3, 4).zipWith(List(3, 4, 5))((x: Int) => (y: Int) => x * y)
Why won't the compiler allow me just to pass in a shorthand version _ * _?
The expression _ * _ is not shorthand for (x: Int) => (y: Int) => x * y. It's a shorthand for (x: Int, y: Int) => x * y. If you change the type of zipper to (A, B) => C instead of A => B => C, it should work. Currying is a thing, it's not just a fancy name for an identity function.
This here compiles:
implicit class EnrichedList[A](val self: List[A]) {
def zipWith[B, C](that: List[B])
(implicit zipper: (A, B) => C): List[C] = {
def zipWithHelper(zipper: (A, B) => C)
(as: List[A])
(bs: List[B]): List[C] = {
(as, bs) match {
case (_, Nil) => Nil
case (Nil, _) => Nil
case (a :: restOfA, b :: restOfB) =>
zipper(a, b) :: zipWithHelper(zipper)(restOfA)(restOfB)
}
}
zipWithHelper(zipper)(self)(that)
}
}
println( List(1, 3, 4).zipWith(List(3, 4, 5))(_ * _) )
and prints
List(3, 12, 20)

Implement fold with for-comprehension

How can a fold be implemented as a for-comprehension in Scala? I see the only way is to use some recursive call? This is a try that is failing, not sure how to do this? What is the best way to implement fold as a for-comprehension
val nums = List(1,2,3)
nums.fold(0)(_+_)
def recFold(acc: Int = 0): Int = {
(for {
a <- nums
b = recFold(a + acc)
} yield b).head
}
recFold(0) //Stack overflow
If you really want to use for, you don't need recursion, but you would need a mutable variable:
val nums = List(1,2,3)
def recFold(zero: Int)(op: (Int, Int) => Int): Int = {
var result: Int = zero
for { a <- nums } result = op(result, a)
result
}
recFold(0)(_ + _) // 6
Which is pretty similar to how foldLeft is actually implemented in TraversableOnce:
def foldLeft[B](z: B)(op: (B, A) => B): B = {
var result = z
this foreach (x => result = op(result, x))
result
}
Fold can be implemented both ways right to left or left to right. No need to use for plus recursion. Recursion is enough.
def foldRight[A, B](as: List[A], z: B)(f: (A, B) => B): B = {
as match {
case Nil => z
case x :: xs => f(x, foldRight(xs, z)(f))
}
}
#annotation.tailrec
def foldLeft[A, B](as: List[A], z: B)(f: (A, B) => B): B = {
as match {
case Nil => z
case x :: xs => foldLeft(xs, f(x, z))(f)
}
}

split a list of objects into different lists depending on a predicate splittor

I have a list like this:
val data = List("a","b","","c","d","e","","a","b","c")
I want to split it from the elements "":
List(List("a","b"),List("c","d","e"),List("a","b","c"))
What would be the Scala way?
something like:
data.MAGIC(_=="")
Using span:
def magic[T](l: List[T]): List[List[T]] = {
#tailrec
def magicAux[T](l: List[T], r: MutableList[List[T]]): MutableList[List[T]] = {
val (p, s) = l.span(_ != "")
s match {
case Nil => r += p
case _ => magicAux(s.tail, r += p)
}
}
magicAux(l, new MutableList[List[T]]()).toList
}
How about this one:
scala> Stream.iterate(""::data){ _.tail.dropWhile(_.nonEmpty) }
.takeWhile(_.nonEmpty)
.map{ _.tail.takeWhile(_.nonEmpty) }.toList
res1: List[List[String]] = List(List(a, b), List(c, d, e), List(a, b, c))
Or this one:
scala> (-1 +: data.zipWithIndex.collect{ case ("", i) => i } :+ data.size)
.sliding(2).toList
.map{ case List(h, t) => data.slice(h+1,t) }
res2: List[List[String]] = List(List(a, b), List(c, d, e), List(a, b, c))
And this one:
scala> (data:+"").foldLeft(List[List[String]](), List[String]()){
case((xs, x), v) => if(v.isEmpty) (x.reverse::xs, Nil) else (xs,v::x)
}._1.reverse
res3: List[List[String]] = List(List(a, b), List(c, d, e), List(a, b, c))
Using foldRight:
val res = ("" :: data).foldRight(List[List[_]](Nil))((x, s) =>
(x, s) match {
case ("", Nil :: _) => s
case ("", _) => Nil :: s
case (x, h :: t) => (x :: h) :: t
}).tail

scala parameterised merge sort - confusing error message

I am getting a compilation error when calling the (lt: (T,T) => Boolean) function
The error code is "type mismatch; found : x.type (with underlying type T) required: T"
and the x parameter in lt(x,y) is underlined.
object sort {
def msort[T](xs: List[T])(lt: (T, T) => Boolean): List[T] = {
def merge[T](xs: List[T], ys: List[T]): List[T] = (xs, ys) match {
case (Nil, ys) => ys
case (xs, Nil) => xs
case (x :: xs1, y :: ys1) => {
if (lt(x, y)) x :: merge(xs1, ys)
else y :: merge(xs, ys1)
}
}
val n = xs.length / 2
if (n == 0) xs
else {
val (left, right) = xs splitAt n
merge(msort(left)(lt), msort(right)(lt))
}
}
}
msort and merge have different type parameters. Remove the T type parameter from merge:
def merge(xs: List[T], ys: List[T]): List[T] = (xs, ys) match {
The [T] declares a new parameter unrelated to the first. You get the same error if you declare it as:
def merge[U](xs: List[U], ys: List[U]): List[U] = (xs, ys) match {
lt has a type (U, U) => Boolean, and you're calling it with x and y which have type T and don't match.