Scala method definition named parameters vs. unnamed - scala

i'm new to Scala and i'm struggling sometimes with method signatures.
Lets take this code, i'm especially interested in naming the parameters to do further operations on them.
def divide(xs: List[Int]): List[Int] = {
val mid = xs.length/2
(xs take mid, xs drop mid)
}
Here I defined the input list named as "xs", I've seen this convention on many web pages. But in university we had another method signature definition method (I am missing the name, sorry) in which we didn't name the input parameter(s) but pattern matching takes place:
def mylength: List[Any] => Int = {
case Nil => 0
case x::xs => mylength(xs)+1
}
In this case, it is very trivial to identify the input parameter because there is just a single one. How could I use the same style as in the code below with 2 or more input parameters in the coding style shown above?
def myConcat(xs: List[Any], ys: List[Any]) = {
xs ++ ys
}
Sorry for my English. I didn't find anything on google because I didn't relly have a clue what terms to search for...
Edit: I have to stick to an interface. I make another example with which you could help me.
myAppend1 and myAppend2 shall behave the same way, putting a new element in the front of the list.
def myAppend1(xs: List[Any], y: Any): List[Any] = {
y :: xs
}
My problem is now the naming of my inputs in myAppend2...
def myAppend2: List[Any] => Any => List[Any] = {
/* how can i do this here, when no names y/xs are given?*/
}

To use the same style with 2 or more parameters, just treat the parameters as a tuple of two:
def myConcat: (List[Any], List[Any]) => List[Any] = {
case (Nil, Nil) => List()
case (xs,ys) => ...
}
Let's take the myAppend example:
def myAppend2: (List[Any], Any) => List[Any] = {
case (xs, y) => y :: xs
}
This has (more or less) the same signature as:
def myAppend1(xs: List[Any], y: Any): List[Any] = {
y :: xs
}
Usage:
scala> myAppend1(List(1,2,3), 4)
res3: List[Any] = List(4, 1, 2, 3)
scala> myAppend2(List(1,2,3), 4)
res4: List[Any] = List(4, 1, 2, 3)
If you had a higher-order function that wanted a function argument of (List[Any], Any) = List[Any], then both will work and are (for most practical purposes) equivalent.
Note that by defining it like
def myAppend3: List[Any] => Any => List[Any] = {
xs => y => y::xs
}
you will be creating a curried function, which in scala has a different signature
(from what you want):
myAppend3(List(1,2,3), 4) // won't compile
myAppend3(List(1,2,3))(4) // need to do this

def myAppend2: List[Any] => Any => List[Any] = { xs => y =>
y :: xs
}
with full form of function literal syntax:
def myAppend2: List[Any] => Any => List[Any] = {
(xs: List[Any]) => {
(y: Any) => {
y :: xs
}
}
}

Related

Function call not accepting :: operator in argument

I'm supposed to write a recursive functional concatenation function in Scala without using standard list operators.
def myConcat: (List[Any],List[Any]) => List[Any] = {
case (xs,Nil) => xs
case (xs,y::ys) => myConcat(xs::y,ys)
}
which throws the error
case (xs,y::ys) => myConcat(xs::y,ys)
^
Recursion.scala:3: error: value :: is not a member of Any
I am rather confident that this would, but that I am misunderstanding something about the syntax/typing. To the best of my knowledge xs::y should be of type List[Any].
I assume, you cannot use List.reverse either, so, start with implementing that:
#tailrec
def reverse(in: List[Any], out: List[Any]): List[Any] = in match {
case Nil => out
case head::tail => reverse(tail, head::out)
}
Now, for concat:
#tailrec
def concatReversed(x: List[Any], y: List[Any]): List[Any] = x match {
case Nil => y
case head::tail => concatReversed(tail, head::y)
}
def concat(x: List[Any], y: List[Any]) =
concatReversed(reverse(x, List.empty[Any]), y)
If you are not looking for the solution to be tail-recursive, it makes things simpler (albeit less efficient):
def concat(x: List[Any], y: List[Any]): List[Any] = x match {
case Nil => y
case head :: tail => head :: concat(tail, y)
}

Pattern Matching List[Any] with nested lists Scala?

How can i achieve this? Where xs is a List[Any].
def flatten(xs: List[Any]): List[Any] = {
xs match {
case x: List[Any] :: t => flatten(x) ::: flatten(t)
case x :: t => x :: flatten(t)
case Nil => Nil
}
}
The first case does not work properly. For some reason I cannot give a type to the head of the list x.
As #Luis mentioned, this is really bad idea to use List[Any] but you still want to write flatten, then using reflection you can do like this:
val xs: List[Any] = List(List(1, 2), 3, 4)
def flatten(xs: List[Any]): List[Any] = {
xs match {
case x :: t if x.isInstanceOf[List[_]] => flatten(x.asInstanceOf[List[Any]]) ::: flatten(t)
case x :: t => x :: flatten(t)
case Nil => Nil
}
}
println(flatten(xs)) // List(1, 2, 3, 4)

How to bypass for & yield in Scala for a certain function?

I have written the following Scala code:
case class A(x: Int, out: List[Int])
def isIn: Int => List[Int] => Boolean =
x => l => l.filter { _ == x }.nonEmpty
def filterTest4: (Int, List[A]) => List[List[Int]] = (x, a) =>
for (l <- a; if (isIn(x)(l.out))) yield l.out
The functrion filterTest4 works perfectly fine, however uses the for & yield, which I do not really like and thus would like to see another way. I would be very happy, if someone offered a constructive comment / answer. Please be nice and keep in mind I just started writing in Scala maybe 3 days ago.
I just found the map function, which could be used like this:
def filterTest5: (Int, List[A]) => List[List[Int]] = (x, a) =>
a.filter { a2 => isIn(x)(a2.out) }.map { a2 => a2.out }
I think that does the job.
Alternatively, as other suggested, you can use collect, which combines map and filter. Also you can destructure the case class to access out directly.
def filterTest5: (Int, List[A]) => List[List[Int]] = (x, a) => a.collect {
case A(_, out) if isIn(x)(out) => out
}

find the data type in List

I have a requirement where I need to perform some set of transformations based on the datatype of List. Lets say if I get List[String] I need to apply some transformations but if I get a List[Int] some different transformations need to be applied. I had defined a function which will take List[Any] and with match statement I need to check the datatype. I tried to use isInstanceOf but it didn't work out.
How can I check the datatype of List.
Assuming that your list have same type in all of its elements and using plain Scala you could do something like this:
def test(list: List[Any]): List[Any] = {
if(list.isEmpty) return List()
list.head match {
case a: String => list.map(str => str.asInstanceOf[String]+"3")
case a: Int => list.map(int => int.asInstanceOf[Int]+3)
}
}
It's not the best solution, but i don't see anything else which you could use without different libraries
Here is even weirder solution, which allows you to return exactly same type which you've put to this function, of course you would need have exact same type in every element of list:
def test[T](list: List[T]): List[T] = {
if(list.isEmpty) return List()
list.head match {
case a: String => list.map(str => (str.asInstanceOf[String]+"3").asInstanceOf[T])
case a: Int => list.map(int => (int.asInstanceOf[Int]+3).asInstanceOf[T])
}
}
test(List("123","123")) // res0: List[String] = List(1233, 1233)
test(List(1,2,3)) // res1: List[Int] = List(4, 5, 6)
And again edit, last but not least, you can use TypeTag to avoid type erasure and check list type, like this:
def test1[T: TypeTag](list: List[T]): List[T] = typeOf[T] match {
case t if t =:= typeOf[String] => list.map(str => (str.asInstanceOf[String]+"3").asInstanceOf[T])
case t if t =:= typeOf[Int] => list.map(int => (int.asInstanceOf[Int]+3).asInstanceOf[T])
case _ => list
}
test1(List("123", "123", "123")) // Strings so: res0: List[String] = List(1233, 1233, 1233)
test1(List("123","123", 1)) // Oh-oh we have not defined type, so: res1: List[Any] = List(123, 123, 1)
test1(List(1,2,3)) // And here we have res2: List[Int] = List(4, 5, 6)

How to call merge sort

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.