I'm teaching myself scala and trying to fatten my FP skills.
One of my references, Essentials of Programming Languages (available here), has a handy list of easy recursive functions. On page page 27/50, we are asked to implement swapper() function.
(swapper s1 s2 slist) returns a list the same as slist, but
with all occurrences of s1 replaced by s2 and all occurrences of s2 replaced by s1.
> (swapper ’a ’d ’(a b c d))
(d b c a)
> (swapper ’a ’d ’(a d () c d))
(d a () c a)
> (swapper ’x ’y ’((x) y (z (x))))
((y) x (z (y)))
In scala, this is:
swapper("a", "d", List("a","b","c","d"))
swapper("a", "d", List("a","d",List(),"c","d"))
swapper("x", "y", List( List("x"), "y", List("z", List("x"))))
My scala version handles all versions save the final x.
def swapper(a: Any, b: Any, lst: List[Any]): List[Any] ={
def r(subList :List[Any], acc : List[Any]): List[Any] ={
def swap (x :Any, xs: List[Any]) =
if(x == a){
r(xs, acc :+ b)
} else if (x == b) {
r(xs, acc :+ a)
} else {
r(xs, acc :+ x)
}
subList match {
case Nil =>
acc
case List(x) :: xs =>
r(xs, r(List(x), List()) +: acc)
case x :: xs =>
swap(x,xs)
//case List(x) :: xs =>
}
}
r(lst, List())
}
Instinctively, I think this is because I have no swap on the section "case List(x) :: xs" but I'm still struggling to fix it.
More difficult, still, this case breaks the tail-call optimization. How can I do this and where can I go to learn more about the general solution?
You can use this foldRight with pattern match approach:
def swapper(a:Any, b:Any, list:List[Any]):List[Any] =
list.foldRight(List.empty[Any]) {
case (item, acc) if item==a => b::acc
case (item, acc) if item==b => a::acc
case (item:List[Any], acc) => swapper(a, b, item)::acc
case (item, acc) => item::acc
}
or even simplier (thanks to #marcospereira):
def swapper(a:Any, b:Any, list:List[Any]):List[Any] =
list.map {
case item if item==a => b
case item if item==b => a
case item:List[Any] => swapper(a, b, item)
case item => item
}
A simpler way to solve this is just use map:
def swapper[T](a: T, b: T, list: List[T]): List[T] = list.map { item =>
if (item == a) b
else if (item == b) a
else item
}
This seems to work.
def swapper[T](a: T, b: T, lst: List[_]): List[_] = {
val m = Map[T, T](a -> b, b -> a).withDefault(identity)
def swap(arg: List[_]): List[_] = arg.map{
case l: List[_] => swap(l)
case x: T => m(x)
}
swap(lst)
}
The List elements are inconsistent because it might be an element or it might be another List, so the type is List[Any], which is a sure sigh that someone needs to rethink this data representation.
Related
I have an exercise in Scala in which I have to transform that kind of list (a,a,a,b,c,d,d,e,a,a) into
((a,3),(b,1),(c,1),(d,2),(e,1),(a,2)).
I obviously know that my algorithm is not correct yet, but I wanted to start with anything.
The problem is that I don't know how to turn on the function (last line), because the error is that whatever I take as previous' argument, it says that required: A, found: Int/String etc.
The previous was meant to be as a head of the previous iteration.
def compress[A](l: List[A]): List[(A, Int)] = {
def compressHelper(l: List[A], acc: List[(A, Int)], previous: A, counter: Int): List[(A, Int)] = {
l match {
case head::tail => {
if (head == previous) {
compressHelper(tail, acc :+ (head, counter+1), head, counter+1)
}
else {
compressHelper(tail, acc :+ (head, counter), head, 1)
}
}
case Nil => acc
}
}
compressHelper(l, List(), , 1)
}
You don't need to pass previous explicitly, just look at the accumulator:
def compress[A](l: List[A], acc: List[(A, Int)]=Nil): List[(A, Int)] =
(l, acc) match {
case (Nil, _) => acc.reverse
case (head :: tail, (a, n) :: rest) if a == head =>
compress(tail, (a, n+1) :: rest)
case (head :: tail, _) => compress (tail, (head, 1) :: acc)
}
I want to implement method in Scala which filters from Seq elements which are for example greater than provided value and additionally returns up to one equal element. For example:
greaterOrEqual(Seq(1,2,3,3,4), 3) shouldBe Seq(3,4)
I ended up with such method:
def greaterOrEqual(
seq: ArrayBuffer[Long],
value: Long
): ArrayBuffer[Long] = {
val greater = seq.filter(_ > value)
val equal = seq.filter(_ == value)
if (equal.isEmpty) {
greater
} else {
equal.tail ++ greater
}
}
but somehow it doesn't look nice to me :) Moreover, I'd like to have generic version of this method where I'd able to use not only Long type but custom case classes.
Do you have any suggestions?
Thanks in advance.
def foo[A : Ordering[A]](seq: Seq[A], value: A) = seq.find(_ == value).toList ++ seq.filter(implicitly[Ordering[A]].gt(_,value))
Or (different style)
def foo[A](seq: Seq[A], value: A)(implicit ord: Ordering[A]) = {
import ord._
seq.find(_ == value).toList ++ seq.filter(_ > value)
}
The code below is deprecated
scala> def foo[A <% Ordered[A]](seq: Seq[A], value: A) = seq.find(_ == value).toList ++ seq.filter(_ > value)
foo: [A](seq: Seq[A], value: A)(implicit evidence$1: A => Ordered[A])List[A]
scala> foo(Seq(1,2,3,3,4,4,5),3)
res8: List[Int] = List(3, 4, 4, 5)
Here's my take on it (preserving original order).
import scala.collection.mutable.ArrayBuffer
def greaterOrEqual[A]( seq :ArrayBuffer[A], value :A
)(implicit ord :Ordering[A]
) : ArrayBuffer[A] =
seq.foldLeft((ArrayBuffer.empty[A],true)){
case (acc, x) if ord.lt(x,value) => acc
case ((acc,bool), x) if ord.gt(x,value) => (acc :+ x, bool)
case ((acc,true), x) => (acc :+ x, false)
case (acc, _) => acc
}._1
testing:
greaterOrEqual(ArrayBuffer.from("xawbaxbt"), 'b')
//res0: ArrayBuffer[Char] = ArrayBuffer(x, w, b, x, t)
This is an excellent problem for a simple tail-recursive algorithm over lists.
def greaterOrEqual[T : Ordering](elements: List[T])(value: T): List[T] = {
import Ordering.Implicits._
#annotation.tailrec
def loop(remaining: List[T], alreadyIncludedEqual: Boolean, acc: List[T]): List[T] =
remaining match {
case x :: xs =>
if (!alreadyIncludedEqual && x == value)
loop(remaining = xs, alreadyIncludedEqual = true, x :: acc)
else if (x > value)
loop(remaining = xs, alreadyIncludedEqual, x :: acc)
else
loop(remaining = xs, alreadyIncludedEqual, acc)
case Nil =>
acc.reverse
}
loop(remaining = elements, alreadyIncludedEqual = false, acc = List.empty)
}
Which you can use like this:
greaterOrEqual(List(1, 3, 2, 3, 4, 0))(3)
// val res: List[Int] = List(3, 4)
You can use the below snippet:
val list = Seq(1,2,3,3,4)
val value = 3
list.partition(_>=3)._1.toSet.toSeq
Here partition method divide the list into two list. First list which satisfy the given condition, and second list contains the remaining elements.
For generic method you can using implicit Ordering. Any type who can compare elements can be handled by greaterOrEqual method.
import scala.math.Ordering._
def greaterOrEqual[T](seq: Seq[T], value: T)(implicit ordering: Ordering[T]): Seq[T] = {
#scala.annotation.tailrec
def go(xs: List[T], value: T, acc: List[T]): List[T] = {
xs match {
case Nil => acc
case head :: rest if ordering.compare(head, value) == 0 => rest.foldLeft(head :: acc){
case (result, x) if ordering.compare(x, value) > 0 => x :: result
case (result, _) => result
}
case head :: rest if ordering.compare(head, value) > 0 => go(rest, value, head :: acc)
case _ :: rest => go(rest, value, acc)
}
}
go(seq.toList, value, List.empty[T]).reverse
}
How to write the zip function, which tuples corresponding elements in a list. I understand that zip is a built in function but I am trying to write it from scratch.
def zip[A,B](lst1: List[A], lst2: List[B]): List[(A, B)]
Test example would be:
test("zip test 1") {
assert(zip(List(1, 2, 3), List(4, 5, 6)) == List((1,4), (2, 5), (3, 6)))
}
or strings as parameters:
test("zip test 2") {
assert(zip(List("hey", "code"), List("world", "scala")) ==
List(("hey", "world"), ("code", "scala")))
}
I tried this so far:
def zip[A,B](lst1:List[A], lst2:List[B]):List[(A,B)] = (lst1,lst2) match{
case Nil=> Nil
case (h1::t1 :: h2 :: t2) =>zip(t1, t2)
}
But I get a type mismatch error, and a immutability error.
If either list is empty, return the empty list. Otherwise, prepend the pair of heads to the zip of tails:
def zip[A, B](xs: List[A], ys: List[B]): List[(A, B)] =
(xs, ys) match {
case (Nil, _) => Nil
case (_, Nil) => Nil
case (x :: xs, y :: ys) => (x, y) :: zip(xs, ys)
}
Augmenting to exploit tail recursion optimisation is left as an exercise for the reader.
merge sort type signature :
def msort[T](less: (T, T) => Boolean)(xs: List[T]): List[T] = {
The function is called using :
msort[Int]((a, b) => a < b) _
Does the type msort[Int] type the parameters a & b to Int ?
To better understand this type signature I've tried to extract the less function :
def lessFunc[Int]((a , b) : (Int , Int)) : Boolean = {
true
}
But this is not correct ?
Entire code :
def msort[T](less: (T, T) => Boolean)(xs: List[T]): List[T] = {
def merge(xs: List[T], ys: List[T], acc: List[T]): List[T] =
(xs, ys) match {
case (Nil, _) => ys.reverse ::: acc
case (_, Nil) => xs.reverse ::: acc
case (x :: xs1, y :: ys1) =>
if (less(x, y)) merge(xs1, ys, x :: acc)
else merge(xs, ys1, y :: acc)
}
val n = xs.length / 2
if (n == 0) xs
else {
val (ys, zs) = xs splitAt n
merge(msort(less)(ys), msort(less)(zs), Nil).reverse
}
}
This is a function which takes two lists of parameters. The first list contains a less function, which as you've guessed correctly when invoked with [Int] is typing the parameters to Int.
You have just expanded it wrong. What you should have done is
def less(a: Int, b: Int) = true
or to match your anonymous function
def less(a: Int, b: Int) = a < b
Now when you call your msort like msort[Int](less) _ (see currying) you'll get a new function which is able to sort Lits[Int].
val listSorter = msort[Int](less) _
listSorter(List(1, 2, 3))
def msort[T](less: (T, T) => Boolean)(xs: List[T]): List[T]
is a function with two parameter lists that returns List of type T. First parentheses let you pass a function that will be used for sorting the list.
(T,T) => Boolean - means that the function will take two parameters and yield boolean.
The second parentheses take a List of type T . This T after name of the function is like generics in Java. You use it to pass a type. It can be called like:
def msort[String]((a,b) => a.length < b.length)(some list) if you want to sort List of String's by their length. Or you can call it like in the example to sort List of Ints
def msort[Int]((a,b) => a < b)(some list)
Because function is defined with two sets of parameters we can take advantage of it by applying only part of them and build specialised functions based on that one. Like for example:
val stringSort = msort[String]((a,b) => a.length < b.length) _
val ascendingIntSort = msort[Int]((a,b) => a < b) _
These are curried functions because stringSort's signature is List[Strint] => List[String]. Now you can reuse these methods by passing only instances of Lists to them:
stringSort(List("cat", "elephant", "butterfly"))
ascendingIntSort(List(4,1,3,2))
A message class:
case class Message(username:String, content:String)
A message list:
val list = List(
Message("aaa", "111"),
Message("aaa","222"),
Message("bbb","333"),
Message("aaa", "444"),
Message("aaa", "555"))
How to group the messages by name and get the following result:
List( "aaa"-> List(Message("aaa","111"), Message("aaa","222")),
"bbb" -> List(Message("bbb","333")),
"aaa" -> List(Message("aaa","444"), Message("aaa", "555")) )
That means, if a user post several messages, then group them together, until another user posted. The order should be kept.
I can't think of an easy way to do this with the provided Seq methods, but you can write your own pretty concisely with a fold:
def contGroupBy[A, B](s: List[A])(p: A => B) = (List.empty[(B, List[A])] /: s) {
case (((k, xs) :: rest), y) if k == p(y) => (k, y :: xs) :: rest
case (acc, y) => (p(y), y :: Nil) :: acc
}.reverse.map { case (k, xs) => (k, xs.reverse) }
Now contGroupBy(list)(_.username) gives you what you want.
I tried to create such a code which works not only with Lists and can be written in operator notation. I came up with this:
object Grouper {
import collection.generic.CanBuildFrom
class GroupingCollection[A, C, CC[C]](ca: C)(implicit c2i: C => Iterable[A]) {
def groupBySep[B](f: A => B)(implicit
cbf: CanBuildFrom[C,(B, C),CC[(B,C)]],
cbfi: CanBuildFrom[C,A,C]
): CC[(B, C)] =
if (ca.isEmpty) cbf().result
else {
val iter = c2i(ca).iterator
val outer = cbf()
val inner = cbfi()
val head = iter.next()
var olda = f(head)
inner += head
for (a <- iter) {
val fa = f(a)
if (olda != fa) {
outer += olda -> inner.result
inner.clear()
}
inner += a
olda = fa
}
outer += olda -> inner.result
outer.result
}
}
implicit def GroupingCollection[A, C[A]](ca: C[A])(
implicit c2i: C[A] => Iterable[A]
): GroupingCollection[A, C[A], C] =
new GroupingCollection[A, C[A],C](ca)(c2i)
}
Can be used (with Lists, Seqs, Arrays, ...) as:
list groupBySep (_.username)
def group(lst: List[Message], out: List[(String, List[Message])] = Nil)
: List[(String, List[Message])] = lst match {
case Nil => out.reverse
case Message(u, c) :: xs =>
val (same, rest) = lst span (_.username == u)
group(rest, (u -> same) :: out)
}
Tail recursive version. Usage is simply group(list).
(List[Tuple2[String,List[Message]]]() /: list) {
case (head :: tail, msg) if msg.username == head._1 =>
(msg.username -> (msg :: head._2)) :: tail
case (xs, msg) =>
(msg.username -> List(msg)) :: xs
} map { t => t._1 -> t._2.reverse } reverse
Here's another method using pattern matching and tail recursion. Probably not as efficient as those above though due to the use of both takeWhile and dropWhile.
def groupBy(msgs: List[Message]): List[(String,List[Message])] = msgs match {
case Nil => List()
case head :: tail => (head.username ->
(head :: tail.takeWhile(m => m.username == head.username))) +:
groupBy(tail.dropWhile(m => m.username == head.username))
}