While reading Programming in Scala, 3rd Edition, it says
Class List does offer an "append" operation—it's written :+ But this
operation is rarely used, because the time it takes to append to a
list grows linearly with the size of the list, whereas prepending with
:: takes constant time.
If you want to build a list efficiently by appending elements, you can
prepend them and when you're done call reverse.
I am trying to understand, what is Scala idiomatic way of doing this? Is calling List.reverse twice acceptable and efficient vs ListBuffer (since ListBuffer is mutable)?
// Scala Recommended way - but reverse twice?
val alist = List("A", "B")
// cons is O(1)
// This will print (A, B, C)
println(("C" :: alist.reverse).reverse)
// Scala also Recommended: Use ListBuffer
val alb = ListBuffer("A", "B")
alb.append("C")
val clist2 = alb.toList
// This will print (A, B, C)
println(clist2)
// DO NOT do this, its O(n)
val clist3 = alist :+ "C"
// This will print (A, B, C)
println(clist3)
P.S: I'm not referring to code optimization here. Which one is generally recommended and will not received WTH expression.
Another implementation could be Difference Lists (also Prolog-based explanation available - Understanding Difference Lists).
That's how I implement DList in Scala:
abstract class DiffList[A](calculate: List[A] => List[A]) {
def prepend(s: List[A]): DiffList[A]
def append(s: List[A]): DiffList[A]
def result: List[A]
}
final class DiffListImpl[A](listFunc: List[A] => List[A])
extends DiffList[A](listFunc) {
def prepend(s: List[A]): DiffListImpl[A] =
new DiffListImpl[A](listFunc andThen (s ++ _))
def append(s: List[A]): DiffListImpl[A] =
new DiffListImpl[A](listFunc andThen (_ ++ s))
def result: List[A] = listFunc(Nil)
}
And use it:
val l1 = List(1, 2)
val l2 = List(6, 7)
val l3 = List(3, 4, 5)
val dl = new DiffListImpl[Int](Nil)
val result = dl.prepend(l1).prepend(l2).append(l3).result
Result: List(6, 7, 1, 2, 3, 4, 5)
Related
I need to concat two generic sequences, I tried to do it like this, but I understand that this is wrong. How should I do it in right way? (I need to get new Seq which will be ordered too)
object Main extends App{
val strings = Seq("f", "d", "a")
val numbers = Seq(1,5,4,2)
val strings2 = Seq("c", "b");
val strings3 = strings2.concat(strings)
println(strings3)
println(numbers)
}
class Seq[T] private(initialElems: T*) {
override def toString: String = initialElems.toString
val elems = initialElems
def concat(a:Seq[T]) = a.elems ++ this.elems
}
object Seq {
def apply[T: Ordering](initialElems: T*): Seq[T] = new Seq(initialElems.sorted:_*)
}
You could create a function which will go through both lists taking heads of both lists and comparing them, then appending appropriate head to the result list. Then it should take the next two heads and repeat until one list is over.
Here's tail-recursive example:
import scala.annotation.tailrec
def merge[A](a: List[A], b: List[A])(implicit ordering: Ordering[A]): List[A] = {
#tailrec
def go(a: List[A], b: List[A], acc: List[A] = Nil): List[A] = {
(a, b) match {
case (ax :: as, bx :: bs) => if(ordering.compare(ax, bx) < 0) go(as, bx :: bs, ax :: acc) else go(ax :: as, bs, bx :: acc)
case (Nil, bs) => acc.reverse ++ bs
case (as, Nil) => acc.reverse ++ as
case _ => acc.reverse
}
}
go(a, b)
}
val strings = List("a", "d", "f")
val strings2 = List("b", "c")
merge(strings, strings2) // List(a,b,c,d,e)
I used List instead of Seq. You should rather not use Seq, which is very general type, but utilize more specific collection types, which suit your task best, like Vector, List, ArraySeq etc.
You can't concat two sorted arrays using ++ keeping order. ++ just stick one sequence to the end of another.
You need to implement something like merge operation from merge sort algorithm and create new Seq from merged elems without sorting.
So, you need to do 3 things:
Implement merge:
def merge(a: Seq[T], b: Seq[T]): YourElemsType[T] = ???
Implement new method for creating Seq instance without sorting in object Seq:
def fromSorted(initialElems: T*): Seq[T] = new Seq(initialElems:_*)
After all, your concat can be implemented as composition merge and fromSorted:
def concat(a:Seq[T]): Seq[T] = Seq.fromSorted(merge(this, a))
Read more about merge sort wiki
I have this doubt with extractors.
If I can do this:
val a :: b = List(1, 2, 3)
why I cannot do this:
val c = for ( a :: b <- List(1, 2, 3) } yield a
The translation of a for-yield expression in Scala (without using guards) is the map function. If you try and use map instead, it'll seem a little clear as to the quirk with the code:
List(1, 2, 3).map((x: Int) => ???)
When you map over a List[+A], you project each value, one at a time. You don't have the entire list at your disposable inside the higher order function.
On the contrary, when using pattern matching on the list itself, the compiler will translate your first example into (after some cleanup):
def main(args: Array[String]): Unit = {
private[this] val x$1: (Int, List[Int]) = List(1, 2, 3) match {
case (head: Int, tl: List[Int])scala.collection.immutable.::[Int]((a # _), (b # _)) => Tuple2[Int, List[Int]](a, b)
};
val a: Int = x$1._1;
val b: List[Int] = x$1._2;
()
Which is a pattern match on the List[Int], matching the case of a head and tail. It's simply syntatic sugar for regular pattern matching, same as for-yield is syntatic sugar for a map. They simply do different things.
After Yuval answer, I've understood why it doesn't work.
Equivalent syntax if interested in 1st value:
val first = for (a <- List(1, 2, 3).headOption) yield a
I've recently come across a problem. I'm trying to flatten "tail-nested" tuples in a compiler-friendly way, and I've come up with the code below:
implicit def FS[T](x: T): List[T] = List(x)
implicit def flatten[T,V](x: (T,V))(implicit ft: T=>List[T], fv: V=>List[T]) =
ft(x._1) ++ fv(x._2)
This above code works well for flattening tuples I am calling "tail-nested" like the ones below.
flatten((1,2)) -> List(1,2)
flatten((1,(2,3))) -> List(1,2,3)
flatten((1,(2,(3,4)))) -> List(1,2,3,4)
However, I seek to make my solution more robust. Consider a case where I have a list of these higher-kinded "tail-nested" tuples.
val l = List( (1,2), (1,(2,3)), (1,(2,(3,4))) )
The inferred type signature of this would be List[(Int, Any)] and this poses a problem for an operation such as map, which would fail with:
error: No implicit view available from Any => List[Int]
This error makes sense to me because of the nature of my recursive implicit chain in the flatten function. However, I was wondering: is there any way I can make my method of flattening the tuples more robust so that higher order functions such as map mesh well with it?
EDIT:
As Bask.ws pointed out, the Product trait offers potential for a nice solution. The below code illustrates this:
def flatten(p: Product): List[_] = p.productIterator.toList.flatMap {x => x match {
case pr: Product => flatten(pr)
case _ => List(x)
}}
The result type of this new flatten call is always List[Any]. My problem would be solved if there was a way to have the compiler tighten this bound a bit. In parallel to my original question, does anyone know if it is possible to accomplish this?
UPD Compile-time fail solution added
I have one solution that may suit you. Types of your first 3 examples are resolved in compile time: Int, Tuple2[Int, Int], Tuple2[Int, Tuple2[Int, Int]]. For you example with the list you have heterogeneous list with actual type List[(Int, Any)] and you have to resolve the second type in runtime or it maybe can be done by macro. So you may want to actually write implicit def flatten[T](x: (T,Any)) as your error advises you
Here is the fast solution. It gives a couple of warnings, but it works nicely:
implicit def FS[T](x: T): List[T] = List(x)
implicit def FP[T](x: Product): List[T] = {
val res = (0 until x.productArity).map(i => x.productElement(i) match {
case p: Product => FP[T](p)
case e: T => FS(e)
case _ => sys.error("incorrect element")
})
res.toList.flatten
}
implicit def flatten[T](x: (T,Any))(implicit ft: T=>List[T], fp: Product =>List[T]) =
ft(x._1) ++ (x._2 match {
case p: Product => fp(p)
case t: T => ft(t)
})
val l = List( (1,2), (1,(2,3)), (1,(2,(3,4))) )
scala> l.map(_.flatten)
res0: List[List[Int]] = List(List(1, 2), List(1, 2, 3), List(1, 2, 3, 4))
UPD
I have researched problem a little bit more, and I have found simple solution to make homogeneus list, which can fail at compile time. It is fully typed without Any and match and looks like compiler now correctly resolves nested implicits
case class InfiniteTuple[T](head: T, tail: Option[InfiniteTuple[T]] = None) {
def flatten: List[T] = head +: tail.map(_.flatten).getOrElse(Nil)
}
implicit def toInfiniteTuple[T](x: T): InfiniteTuple[T] = InfiniteTuple(x)
implicit def toInfiniteTuple2[T, V](x: (T, V))(implicit ft: V => InfiniteTuple[T]): InfiniteTuple[T] =
InfiniteTuple(x._1, Some(ft(x._2)))
def l: List[InfiniteTuple[Int]] = List( (1,2), (1,(2,3)), (1,(2,(3,4)))) //OK
def c: List[InfiniteTuple[Int]] = List( (1,2), (1,(2,3)), (1,(2,(3,"44"))))
//Compile-time error
//<console>:11: error: No implicit view available from (Int, (Int, java.lang.String)) => InfiniteTuple[Int]
Then you can implement any flatten you want. For example, one above:
scala> l.map(_.flatten)
res0: List[List[Int]] = List(List(1, 2), List(1, 2, 3), List(1, 2, 3, 4))
I'm working on a Functional Programming in Scala exercise to implement foldRight on a Stream.
Before getting to that exercise, let me show how I've implemented foldLeft.
def foldLeft[A, B](as: Stream[A])(z: B)(f: (B, A) => B): B = {
def go(bs: Stream[A], acc: B) : B = bs match {
case x #:: xs => go(xs, f(acc, x))
case Stream() => acc
}
}
My understanding is that, via #::, I'm executing the fold in a tail-recursive and stream-y fashion, i.e. the tail isn't fully evaluated.
However, when I thought of how to implement foldRight, I figured I could simply do:
stream.reverse.foldLeft(monoid.zero)(monoid.op)
However, calling reverse will result in full evaluation of the stream:
scala> val x = Stream(1,2,3)
x: scala.collection.immutable.Stream[Int] = Stream(1, ?)
scala> x.reverse
res15: scala.collection.immutable.Stream[Int] = Stream(3, 2, 1)
How can I stream-ily implement foldRight?
In Scala 2.8, I had a need to call List.min and provide my own compare function to get the value based on the second element of a Tuple2. I had to write this kind of code:
val list = ("a", 5) :: ("b", 3) :: ("c", 2) :: Nil
list.min( new Ordering[Tuple2[String,Int]] {
def compare(x:Tuple2[String,Int],y:Tuple2[String,Int]): Int = x._2 compare y._2
} )
Is there a way to make this more readable or to create an Ordering out of an anonymous function like you can do with list.sortBy(_._2)?
In Scala 2.9, you can do list minBy { _._2 }.
C'mon guys, you made the poor questioner find "on" himself. Pretty shabby performance. You could shave a little further writing it like this:
list min Ordering[Int].on[(_,Int)](_._2)
Which is still far too noisy but that's where we are at the moment.
One thing you can do is use the more concise standard tuple type syntax instead of using Tuple2:
val min = list.min(new Ordering[(String, Int)] {
def compare(x: (String, Int), y: (String, Int)): Int = x._2 compare y._2
})
Or use reduceLeft to have a more concise solution altogether:
val min = list.reduceLeft((a, b) => (if (a._2 < b._2) a else b))
Or you could sort the list by your criterion and get the first element (or last for the max):
val min = list.sort( (a, b) => a._2 < b._2 ).first
Which can be further shortened using the placeholder syntax:
val min = list.sort( _._2 < _._2 ).first
Which, as you wrote yourself, can be shortened to:
val min = list.sortBy( _._2 ).first
But as you suggested sortBy yourself, I'm not sure if you are looking for something different here.
The function Ordering#on witnesses the fact that Ordering is a contra-variant functor. Others include Comparator, Function1, Comparable and scalaz.Equal.
Scalaz provides a unified view on these types, so for any of them you can adapt the input with value contramap f, or with symbolic denotation, value ∙ f
scala> import scalaz._
import scalaz._
scala> import Scalaz._
import Scalaz._
scala> val ordering = implicitly[scala.Ordering[Int]] ∙ {x: (_, Int) => x._2}
ordering: scala.math.Ordering[Tuple2[_, Int]] = scala.math.Ordering$$anon$2#34df289d
scala> List(("1", 1), ("2", 2)) min ordering
res2: (java.lang.String, Int) = (1,1)
Here's the conversion from the Ordering[Int] to Ordering[(_, Int)] in more detail:
scala> scalaz.Scalaz.maContravariantImplicit[Ordering, Int](Ordering.Int).contramap { x: (_, Int) => x._2 }
res8: scala.math.Ordering[Tuple2[_, Int]] = scala.math.Ordering$$anon$2#4fa666bf
list.min(Ordering.fromLessThan[(String, Int)](_._2 < _._2))
Which is still too verbose, of course. I'd probably declare it as a val or object.
You could always define your own implicit conversion:
implicit def funToOrdering[T,R <% Ordered[R]](f: T => R) = new Ordering[T] {
def compare(x: T, y: T) = f(x) compare f(y)
}
val list = ("a", 5) :: ("b", 3) :: ("c", 2) :: Nil
list.min { t: (String,Int) => t._2 } // (c, 2)
EDIT: Per #Dario's comments.
Might be more readable if the conversion wasn't implicit, but using an "on" function:
def on[T,R <% Ordered[R]](f: T => R) = new Ordering[T] {
def compare(x: T, y: T) = f(x) compare f(y)
}
val list = ("a", 5) :: ("b", 3) :: ("c", 2) :: Nil
list.min( on { t: (String,Int) => t._2 } ) // (c, 2)