Update multiple values in a sequence - scala

To get a sequence with one value updated, one can use
seq.updated(index, value)
I want to set a new value for a range of elements. Is there a library function for that? I currently use the following function:
def updatedSlice[A](seq: List[A], ind: Iterable[Int], value: A): List[A] =
if (ind.isEmpty) seq
else updatedSlice(seq.updated(ind.head, value), ind.tail, value)
Besides the need of writing function, this seems to be inefficient, and also works only for lists, rather than arbitrary subclasses of Seq and Strings. So,
is there a method that performs it?
how can I parametrize the function to take (and return) some subclass of Seq[A]?

To my knowledge there's no combinator that directly provides this functionality.
For the Seq part, well, it works only for List because you're taking a List as a parameter. Take a Seq, return a Seq and you already have one less problem.
Moreover, your implementation throws an IndexOutOfBounds exception if ind contains an index greater or equal to the seq length.
Here's an alternative implementation (which uses Set for a O(1) contains)
def updatedAtIndexes[A](seq: Seq[A], ind: Set[Int], value: A): Seq[A] = seq.zipWithIndex.map {
case (el, i) if ind.contains(i) => value
case (el, _) => el
}
Example
updatedAtIndexes(List(1, 2, 3, 4, 5), Set(0, 2), 42) // List(42, 2, 42, 4)
You can even make it prettier with a simple implicit class:
implicit class MyPimpedSeq[A](seq: Seq[A]) {
def updatedAtIndexes(ind: Set[Int], value: A): Seq[A] = seq.zipWithIndex.map {
case (el, i) if ind.contains(i) => value
case (el, _) => el
}
}
Examples
List(1, 2, 3, 4).updatedAtIndexes(Set(0, 2), 42) // List(42, 2, 42, 4)
Vector(1, 2, 3).updatedAtIndexes(Set(1, 2, 3), 42) // Vector(1, 42, 42)

No one at a computer has said:
scala> (1 to 10).toSeq patch (3, (1 to 5), 3)
res0: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 1, 2, 3, 4, 5, 7, 8, 9, 10)
Save your green checks for #Marth.
Note they're still working on it.
https://issues.scala-lang.org/browse/SI-8474
Which says something about less-frequently-used API.
Update: I glanced at the question a second time and saw that I misread it, oh well:
scala> implicit class x[A](as: Seq[A]) {
| def updatedAt(is: collection.Traversable[Int], a: A) = {
| (as /: is) { case (xx, i) => xx updated (i, a) } } }
defined class x
scala> (1 to 10) updatedAt (Seq(3,6,9), 0)
res9: Seq[Int] = Vector(1, 2, 3, 0, 5, 6, 0, 8, 9, 0)
Just a relaxing round of golf.
Update: s/relaxing/annoying
Looks like it needs more type params, but I don't have a time slice for it.
scala> implicit class slicer[A, B[_] <: Seq[_]](as: B[A]) {
| def updatedAt[That<:B[_]](is: Traversable[Int], a: A)(implicit cbf: CanBuildFrom[B[A], A, That]) =
| (as /: is) { case (x,i) => x updated[A,That] (i,a) }}
<console>:15: error: type arguments [A,That] conform to the bounds of none of the overloaded alternatives of
value updated: [B >: _$1, That](index: Int, elem: B)(implicit bf: scala.collection.generic.CanBuildFrom[Seq[_$1],B,That])That <and> [B >: A, That](index: Int, elem: B)(implicit bf: scala.collection.generic.CanBuildFrom[Repr,B,That])That
(as /: is) { case (x,i) => x updated[A,That] (i,a) }}
^
Who even knew updated was overloaded?
My new favorite Odersky quote:
I played with it until it got too tedious.

Related

Getting proper type constructor parameters for a "refined" type

I'm having problems comparing the 'compatibility' between two types using reflection (actually I'm writing a macro). For example, I want to permit Vector[Int] === List[Int]. Now I know the general approach. But the problem is I cannot get the type constructor parameters in this case:
import scala.reflect._
import runtime.universe._
typeOf[List[Int]].typeArgs // List(Int) OK
typeOf[List[Int] with java.io.Serializable].typeArgs // List() FAIL
Why is this a problem?
def test[A, B >: A](a: A, b: B)(implicit tt: TypeTag[B]) = {
println(s"tt = $tt")
typeOf[B].typeArgs
}
Now this works:
test(List(1, 2, 3), List(1, 2, 3)) // List(Int)
But this doesn't:
test(Vector(1, 2, 3), List(1, 2, 3)) // List()
One can use an extractor called RefinedType:
def test[A, B >: A](a: A, b: B)(implicit tt: TypeTag[B]): List[List[Type]] = {
val all = typeOf[B] match {
case RefinedType(parents, scope) => parents.map(_.typeArgs)
case x => x.typeArgs :: Nil
}
all.filter(_.nonEmpty)
}
test(List(1, 2, 3), List(1, 2, 3))
test(Vector(1, 2, 3), List(1, 2, 3))
Then one still has to somehow find a strategy to align the parents. (I'm testing all combinations now).

Scala: List to List of Lists, idiomatically?

In Scala, I wrote a Higher Order Function that iterates a list, and calls back with the head and list, for each element, that is:
def headAndSelf[A, B](li: List[A], f: (A, List[A]) => B): List[B] = {
if (li == Nil) Nil
else f(li.head, li) :: headAndSelf(li.tail, f)
}
Given val a = List(1,2,3)
scala> headAndSelf(a, (x: Int, y: List[Int]) => (x,y))
res4: List[(Int, List[Int])] = List((1,List(1, 2, 3)), (2,List(2, 3)), (3,List(3)))
It then occurred to me I could get the head from the list, so it was simpler to write:
def self[A, B](li: List[A], f: (List[A]) => B): List[B] = {
if (li == Nil) Nil
else f(li) :: self(li.tail, f)
}
(even though it makes the lambda a bit less elegant)
scala> self(a, (x: List[Int]) => x)
res7: List[List[Int]] = List(List(1, 2, 3), List(2, 3), List(3))
Then I thought, surely there's an even easier, idiomatic way to turn a List into a List of Lists. So what is it?
Bonus Question: why can't Scala infer the type of the lambda? a is a List[Int], so x should be a List[Int] too, no?:
scala> self(a, x => x)
<console>:13: error: missing parameter type
self(a, x => x)
^
tails will do most of the job.
tails will have an empty list as its last element, and you need to filter that out, which is what collect does below:
def headAndSelf[A,B](l: List[A])(f: (A, List[A]) => B) =
l.tails.collect{case list # head :: _ => f(head, list)}.toList
Having two parameters lists as demonstrated here will allow proper type inference. You need two list at call site too, but it is often convenient :
headAndSelf(yourList){(h,l) => whatever}

why does scala not match implicitly on tuples?

you can do the following in ruby:
l = [[1, 2], [3, 4], [5, 6]]
m = l.map {|(a, b)| a+b}
but you can not do the following in scala:
val a = List((1, 2), (3, 4), (5, 6))
a.map((f, s) => f + s)
<console>:9: error: wrong number of parameters; expected = 1
a.map((f, s) => f + s)
instead you have to do this:
a.map { case (f, s) => f + s }
I find this rather wordy, since scala defines a "tuple" type, I was expecting it to also provide the syntactic sugar on top of it to match implicitly like above. Is there some deep reason why this kind of matching is not supported? Is there a more elegant way of doing this?
the reason
The reason is that the syntax you are trying to use already has a meaning. It is used when the higher order function expects a two-argument function. For example, with reduce or fold:
List(1,2,3).reduce((a,b) => a+b)
a solution
The cleaner way can be achieved by defining your own implicit method:
import scala.collection.generic.CanBuildFrom
import scala.collection.GenTraversableLike
implicit class EnrichedWithMapt2[A, B, Repr](val
self: GenTraversableLike[(A, B), Repr]) extends AnyVal {
def mapt[R, That](f: (A, B) => R)(implicit bf: CanBuildFrom[Repr, R, That]) = {
self.map(x => f(x._1, x._2))
}
}
Then you can do:
val a = List((1, 2), (3, 4), (5, 6))
a.mapt((f, s) => f + s) // List(3, 7, 11)
other alternatives
There are some other tricks you can do, like using tupled, but they doesn't really help you with the situation you described:
val g = (f: Int, s: Int) => f + s
a.map(g.tupled)
Or just
a.map(((f: Int, s: Int) => f + s).tupled)
More alternatives
If you have more time, you can even do the following with implicits:
val _1 = { t: { def _1: Int } => t._1 }
val _2 = { t: { def _2: Int } => t._2 }
implicit class HighLevelPlus[A](t: A => Int) {
def +(other: A => Int) = { a: A => t(a) + other(a) }
def *(other: A => Int) = { a: A => t(a) * other(a) }
}
val a = List((1, 2), (3, 4), (5, 6))
a map _1 + _2
Another possibility with monads and the keyword for and yield:
val a = List((1, 2), (3, 4), (5, 6))
for((i, j) <- a) yield i + j
but this one might not be the solution you prefer.
Is there some deep reason why this kind of matching is not supported?
Yes, because in Scala, the syntax
(f, s) =>
means an anonymous function that takes 2 arguments.
Is there a more elegant way of doing this?
(Somewhat tongue-in-cheek answer.) Use Haskell, where \(f, s) -> actually means a function that takes a tuple as an argument.
you can use _1, _2 properties of Tuple for desired effect
scala>val a = List((1, 2), (3, 4), (5, 6))
a: List[(Int, Int)] = List((1,2), (3,4), (5,6))
scala>a.map(x => x._1 + x._2)
res2: List[Int] = List(3, 7, 11)

Problems with contravariance in Scala

I want to define a type-class like this:
trait CanFold[-T, R] {
def sum(acc: R, elem: T): R
def zero: R
}
implicit object CanFoldInts extends CanFold[Int, Int] {
def sum(x: Int, y: Int) = x + y
def zero = 0
}
implicit object CanFoldSeqs extends CanFold[Traversable[_], Traversable[_]] {
def sum(x: Traversable[_], y: Traversable[_]) = x ++ y
def zero = Traversable()
}
def sum[A, B](list: Traversable[A])(implicit adder: CanFold[A, B]): B =
list.foldLeft(adder.zero)((acc,e) => adder.sum(acc, e))
However, the problem is when I do this I get a Traversable[Any] and it
would be nice to get a Traversable[Int] instead:
scala> sum(List(1,2,3) :: List(4, 5) :: Nil)
res10: Traversable[Any] = List(1, 2, 3, 4, 5)
To make matters worse, I cannot define an implicit for
Traversable[Int] after defining one for Traversable[_], because then
the definitions would cause ambiguity. After pulling my hair out I
gave up.
Is there any way I could make that sum return a
Traversable[T] instead of a Traversable[Any]?
Looking at how sum() is defined on Seq in Scala's library, I can see it works with Numeric, which is invariant, but I want default implementations for supertypes and having the result be different than the input (same as the fold operation) is nice.
The only way I know to add type parameters to such type classes is to use a def instead of an object:
implicit def CanFoldSeqs[A] = new CanFold[Traversable[A], Traversable[A]] {
def sum(x: Traversable[A], y: Traversable[A]) = x ++ y
def zero = Traversable()
}
scala> sum(List(1, 2, 3) :: List(4, 5) :: Nil)
res0: Traversable[Int] = List(1, 2, 3, 4, 5)

Type inference on anonymous functions with enrich-my-library

Say I have a method that turns a (function on two elements) into a (function on two sequences):
def seqed[T](f: (T,T) => T): (Seq[T], Seq[T]) => Seq[T] = (_,_).zipped map f
In words, the resulting function takes two sequences xs and ys, and creates a new sequence consisting of (xs(0) f ys(0), xs(1) f ys(1), ...)
So, for example, if xss is Seq(Seq(1,2),Seq(3,4)) and f is (a: Int, b: Int) => a + b, we can invoke it thus:
xss reduceLeft seqed(f) // Seq(4, 6)
or with an anonymous function:
xss reduceLeft seqed[Int](_+_)
This is pretty good; it would be nice to get rid of the [Int] type argument but I don't see how (any ideas?).
To make it feel a bit more like the tupled method, I also tried the enrich-my-library pattern:
class SeqFunction[T](f: (T,T) => T) {
def seqed: (Seq[T], Seq[T]) => Seq[T] = (_,_).zipped map f
}
implicit def seqFunction[T](f: (T,T) => T) = new SeqFunction(f)
For a pre-defined function this works great, but it's ugly with anonymous ones
xss reduceLeft f.seqed
xss reduceLeft ((_:Int) + (_:Int)).seqed
Is there another way I can reformulate this so that the types are inferred, and I can use syntax something like:
// pseudocode
xss reduceLeft (_+_).seqed // ... or failing that
xss reduceLeft (_+_).seqed[Int]
? Or am I asking too much of type inference?
You can't do it the way you want, but look at Function.tupled, which is a counter-part to .tupled that solves this very same problem.
scala> List(1, 2, 3) zip List(1, 2, 3) map (_ + _).tupled
<console>:8: error: missing parameter type for expanded function ((x$1, x$2) => x$1.$plus(x$2))
List(1, 2, 3) zip List(1, 2, 3) map (_ + _).tupled
^
<console>:8: error: missing parameter type for expanded function ((x$1: <error>, x$2) => x$1.$plus(x$2))
List(1, 2, 3) zip List(1, 2, 3) map (_ + _).tupled
^
scala> List(1, 2, 3) zip List(1, 2, 3) map Function.tupled(_ + _)
res7: List[Int] = List(2, 4, 6)
I am pretty sure you are asking too much. Type inference in Scala goes from left to right, so the type of (_+_) needs to be figured out first before even considering the .sedeq part. And there isn't enough information there.
The reason why a type annotation is required in
xss reduceLeft seqed[Int](_+_)
but not in
xs zip ys map Function.tupled(_+_)
is due to the difference in type requirements between map and reduceLeft.
def reduceLeft [B >: A] (f: (B, A) ⇒ B): B
def map [B] (f: (A) ⇒ B): Seq[B] // simple version!
reduceLeft expects seqed to return type B where B >: Int. It seems that therefore the precise type for seqed cannot be known, so we have to provide the annotation. More info in this question.
One way to overcome this is to re-implement reduceLeft without the lower bound.
implicit def withReduceL[T](xs: Seq[T]) = new {
def reduceL(f: (T, T) => T) = xs reduceLeft f
}
Test:
scala> Seq(Seq(1,2,3), Seq(2,2,2)) reduceL seqed(_+_)
res1: Seq[Int] = List(3, 4, 5)
The problem now is that this now doesn't work on subtypes of Seq (e.g. List), with or without the [Int] parameter:
scala> Seq(List(1,2,3), List(2,2,2)) reduceL seqed(_+_)
<console>:11: error: missing parameter type for expanded function ((x$1, x$2) => x$1.$plus(x$2))
Seq(List(1,2,3), List(2,2,2)) reduceL seqed(_+_)
^
reduceL expects a function of type (List[Int], List[Int]) => List[Int]. Because Function2 is defined as Function2 [-T1, -T2, +R], (Seq[Int], Seq[Int]) => Seq[Int] is not a valid substitution.