Related
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
}
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)
}
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))
The code below is based on Merge sort from "Programming Scala" causes stack overflow
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
}
}
When I try to invoke msort using :
val l = List(5, 2, 4, 6, 1, 3)
msort[Int](l)
I receive error :
Multiple markers at this line - type mismatch; found : List[Int] required: (Int, Int) => Boolean - type mismatch;
found : List[Int] required: (Int, Int) => Boolean - missing arguments for method msort in object mergesort; follow
this method with `_' if you want to treat it as a partially applied function
How do I invoke msort & why is a function required as part of the invocation ?
In Scala it is possible to have Multiple Parameters Lists. Your invocation only passes one argument.
The method is declared as def msort[T](less: (T, T) => Boolean)(xs: List[T]): List[T], so the first argument is of type (T, T) => Boolean, which is a function taking two parameters of type T and returning a Boolean value. You pass there a List[Int], which makes Scala complain.
Why would you like to have such a thing you may ask. Well, consider following example.
val stringSorter = msort[String]((a, b) => a.compareTo(b) < 0) _
// stringSorter: List[String] => List[String] = <function1>
val integerSorter = msort[Int]((a, b) => a < b) _
// integerSorter: List[Int] => List[Int] = <function1>
Those two invocation create two new functions taking only a single parameter - the list you want to sort. You don't have to tell it how to compare the elements, because you already did. Note you can invoke the same function with different lists as an argument.
integerSorter(List(2, 3, 1))
// res0: List[Int] = List(1, 2, 3)
integerSorter(List(2, 4, 1))
// res1: List[Int] = List(1, 2, 4)
stringSorter(List("b", "a", "c"))
res3: List[String] = List(a, b, c)
Note also that the newly created functions are type safe and following code will fail:
integerSorter(List("b", "a", "c"))
<console>:10: error: type mismatch;
found : String("b")
required: Int
integerSorter(List("b", "a", "c"))
Implicit Parameters
As the article in the link mentioned one of the reasons you may want to use Multiple Parameter Lists are implicit parameters.
When using implicit parameters, and you use the implicit keyword, it
applies to the entire parameter list. Thus, if you want only some
parameters to be implicit, you must use multiple parameter lists.
Let's modify the example code you gave us a bit to introduce a new type:
trait Comparator[T] {
def less(a: T, b: T): Boolean
}
and let's swap the parameter lists, and add implicit keyword to the second one, so now it becomes:
def msort[T](xs: List[T])(implicit c: Comparator[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 (c.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(ys)(c), msort(zs)(c), Nil).reverse
}
}
Now you can declare implicit object which will be used in case you don't supply one, e.g.
implicit val intComparator = new Comparator[Int] { def less(a: Int, b: Int) = a < b }
msort(List(5, 3, 1, 3))
// res8: List[Int] = List(1, 3, 3, 5)
While this may not seem to be very appealing it gives you extra flexibility when designing your API. Let's assume that we have a type called CustomType. It can declare an implicit in the companion object and it will be resolved "automatically" by the compiler.
case class CustomType(ordinal: Int, name: String)
object CustomType {
implicit val customTypeComparator = new Comparator[CustomType] {
def less(a: CustomType, b: CustomType) = a.ordinal < b.ordinal
}
}
msort(List(CustomType(2, "Second"), CustomType(1, "First")))
// res11: List[CustomType] = List(CustomType(1,First), CustomType(2,Second))
def msort[T](less: (T, T) => Boolean)(xs: List[T]): List[T]
This function takes two arguments: a function less and a list xs.
How do I invoke msort?
You have to provide values for both arguments: msort(...)(...).
Why is a function required as part of the invocation?
Because the argument less is declared with function type (T, T) => Boolean.
A direct cut and paste of the following algorithm:
def msort[T](less: (T, T) => Boolean)
(xs: List[T]): List[T] = {
def merge(xs: List[T], ys: List[T]): List[T] =
(xs, ys) match {
case (Nil, _) => ys
case (_, Nil) => xs
case (x :: xs1, y :: ys1) =>
if (less(x, y)) x :: merge(xs1, ys)
else y :: merge(xs, ys1)
}
val n = xs.length / 2
if (n == 0) xs
else {
val (ys, zs) = xs splitAt n
merge(msort(less)(ys), msort(less)(zs))
}
}
causes a StackOverflowError on 5000 long lists.
Is there any way to optimize this so that this doesn't occur?
It is doing this because it isn't tail-recursive. You can fix this by either using a non-strict collection, or by making it tail-recursive.
The latter solution goes like this:
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
}
}
Using non-strictness involves either passing parameters by-name, or using non-strict collections such as Stream. The following code uses Stream just to prevent stack overflow, and List elsewhere:
def msort[T](less: (T, T) => Boolean)
(xs: List[T]): List[T] = {
def merge(left: List[T], right: List[T]): Stream[T] = (left, right) match {
case (x :: xs, y :: ys) if less(x, y) => Stream.cons(x, merge(xs, right))
case (x :: xs, y :: ys) => Stream.cons(y, merge(left, ys))
case _ => if (left.isEmpty) right.toStream else left.toStream
}
val n = xs.length / 2
if (n == 0) xs
else {
val (ys, zs) = xs splitAt n
merge(msort(less)(ys), msort(less)(zs)).toList
}
}
Just playing around with scala's TailCalls (trampolining support), which I suspect wasn't around when this question was originally posed. Here's a recursive immutable version of the merge in Rex's answer.
import scala.util.control.TailCalls._
def merge[T <% Ordered[T]](x:List[T],y:List[T]):List[T] = {
def build(s:List[T],a:List[T],b:List[T]):TailRec[List[T]] = {
if (a.isEmpty) {
done(b.reverse ::: s)
} else if (b.isEmpty) {
done(a.reverse ::: s)
} else if (a.head<b.head) {
tailcall(build(a.head::s,a.tail,b))
} else {
tailcall(build(b.head::s,a,b.tail))
}
}
build(List(),x,y).result.reverse
}
Runs just as fast as the mutable version on big List[Long]s on Scala 2.9.1 on 64bit OpenJDK (Debian/Squeeze amd64 on an i7).
Just in case Daniel's solutions didn't make it clear enough, the problem is that merge's recursion is as deep as the length of the list, and it's not tail-recursion so it can't be converted into iteration.
Scala can convert Daniel's tail-recursive merge solution into something approximately equivalent to this:
def merge(xs: List[T], ys: List[T]): List[T] = {
var acc:List[T] = Nil
var decx = xs
var decy = ys
while (!decx.isEmpty || !decy.isEmpty) {
(decx, decy) match {
case (Nil, _) => { acc = decy.reverse ::: acc ; decy = Nil }
case (_, Nil) => { acc = decx.reverse ::: acc ; decx = Nil }
case (x :: xs1, y :: ys1) =>
if (less(x, y)) { acc = x :: acc ; decx = xs1 }
else { acc = y :: acc ; decy = ys1 }
}
}
acc.reverse
}
but it keeps track of all the variables for you.
(A tail-recursive method is one where the method only calls itself to get a complete answer to pass back; it never calls itself and then does something with the result before passing it back. Also, tail-recursion can't be used if the method might be polymorphic, so it generally only works in objects or with classes marked final.)