This question already has answers here:
What does param: _* mean in Scala?
(3 answers)
Closed 4 years ago.
def swapElementsOfArray(array: Array[Int]) =
array match {
case Array(x, y, z # _*) => Array(y, x) ++ z
case _ => array
}
I don't get how # _* is used here. can anyone help to explain that? thank you in advance.
The operator _* makes reference to a sequence of elements, in your case, it'd be the "tail" of the array.
The operator # allows you to bind a matched pattern to a variable, in your case "z"
So with z # _* you are assigning to Z the rest of the array you are not using but you need to refer to it later.
val a= Array(2, 1, 3, 5, 3, 2)
def swapElementsOfArray(array: Array[Int]) =
array match {
case Array(x, y, z # _*) => Array(y, x) ++ z
case _ => array
}
swapElementsOfArray(a).toString
res0: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 5, 3, 2
z would be "3,5,3,2" you don't need it for use but you need the reference.
In this specific example _* means "the rest of the array".
The z # part is standard pattern match syntax for binding a variable name to a pattern
Please note that the _* operator is a general operator that applies to any vararg parameter, and it's sometimes referred to as the "splat operator".
You can read about it in the specification: https://www.scala-lang.org/files/archive/spec/2.11/04-basic-declarations-and-definitions.html#repeated-parameters
Here's the relevant sentence:
The only exception to this rule is if the last argument is marked to be a sequence argument via a _* type annotation. If m above is applied to arguments (e1,…,en,e′: _*), then the type of m in that application is taken to be (p1:T1,…,pn:Tn,ps:scala.Seq[S])
This applies to both matching and passing varargs. For example, the splat operator can be used to pass a sequence where a vararg is expected.
Example:
val s = Seq(1, 2, 3)
val a1 = Array(s) // does not compile
val a2 = Array(s: _*) // compiles, s is passed as vararg
a2 match {
case Array(s # _*) => s // s now contains all the elements of a2
}
a2 match {
case Array(head, tail # _*) => head // tail contains all the elements except the first
case _ => ??? // empty list!
}
Related
The following three map function call on a Scala List all produced the same result.
Can someone tell me what is the actual different on them?
val l = List(1,2,3,4,5,6,7,8)
l.map(x => x * 2)
l.map{x => x * 2}
l.map{case x => x * 2}
The case thingy is actually syntax for PartialFunction. You can have more than one clause like that:
l.map {
case 1 => "one"
case 2 => "two"
case _ => "foo"
}
This actually passes a PartialFunction to map, not just a Function, but that works, because PartialFunction is a subclass of Function.
The resulting PartialFunction is defined on those parameters, that are matched by a case clause, and not on others:
l.collect {
case 1 => "one"
case 2 => "two"
}
Returns List("one", "two")
You can also use case to deconstruct tuples or case classes (or anything else that has unapply or unapplySeq), like pointed out in the other answer, same way you would in a match expression.
l.map{case x => x * 2}
Is just syntactic sugar for:
l.map(input => input match { case x => x * 2 })
Which means - we use pattern matching (with a single case) on the input argument of the anonymous function passed to map.
Obviously, in this case, the pattern matching doesn't do much (matches everything, and no "unapplying" takes place), but one can easily see how this could be useful in other occasions, for example to "break up" tuples:
val l: Seq[(Int, Int)] = Seq((1, 2), (3, 4))
l.map { case (a, b) => a + b }
I would like to use the pattern matching to swap the first two elements of an array, my code as shown below:
>scala val arr = Array(1,2,3,4,5)
>arr match { case Array(a,b,rest # _*) => Array(b,a,rest)
// Array(2,1,Vector(3,4,5))
However, the result should be Array(2,1,3,4,5). How to revise it?
Your problem is not passing in rest as a varargs, which is done using rest: _* syntax. This tells the compiler to pass in the collection methods as varargs, it works with Seq.
val arr = Array(1, 2, 3, 4, 5)
arr match { case Array(a, b, rest # _*) => Array(b, a +: rest: _*) }
There's an Array.apply method than takes a first element followed by a varargs, but there's none to pass in two elements and then varargs. Because of that, we need to prepend the second element to the Seq before passing the whole thing as varargs.
That's why we end up with a +: rest: _*. +: are invoked on the right hand side of the expression, so the method +: is defined on Seq, by convention Scala methods that end with : are right associative.
I've played a bit with placeholder and found a strange case :
val integers = Seq(1, 2)
val f = (x:Int) => x + 1
integers.map((_, f(_)))
which returns
Seq[(Int, Int => Int)] = List((1,<function1>), (2,<function1>))
I was expecting
Seq[(Int, Int)] = List((1, 2), (2, 3))
If I make the following changes, everything works as expected :
integers.map(i => (i, f(i)))
Any idea why the function f is not applied during the mapping ?
The underscore stands in for the passed argument only once. So in integers.map((_, f(_))) the 1st _ is a value from integers but the 2nd _ has the stand-alone meaning of "partially applied function".
If your anonymous function takes 2 (or more) arguments then you can use 2 (or more) underscores, but each stands in for its passed argument only once.
The Scala compiler can't read your mind, so the _ placeholder syntax is only useful in very simple expressions.
In your example:
integers.map((_, f(_)))
it evaluates the f(_) as a standalone sub-expression, so you end up with something equivalent to this:
x => (x, y => f(y))
Even if the compiler didn't treat f(_) as its own sub-expression, the result would not be the same as what you say want:
integers.map(i => (i, f(i)))
You want both instances of _ to be treated as the same argument, which is not how _ works. Each occurrence of _ in an expression is always treated as a unique argument.
Let's say we have this list of tuples:
val data = List(('a', List(1, 0)), ('b', List(1, 1)), ('c', List(0)))
The list has this signature:
List[(Char, List[Int])]
My task is to get the "List[Int]" element from a tuple inside "data" whose key is, for instance, letter "b". If I implement a method like "findIntList(data, 'b')", then I expect List(1, 1) as a result. I have tried the following approaches:
data.foreach { elem => if (elem._1 == char) return elem._2 }
data.find(x=> x._1 == ch)
for (elem <- data) yield elem match {case (x, y: List[Bit]) => if (x == char) y}
for (x <- data) yield if (x._1 == char) x._2
With all the approaches (except Approach 1, where I employ an explicit "return"), I get either a List[Option] or List[Any] and I don't know how to extract the "List[Int]" out of it.
One of many ways:
data.toMap.get('b').get
toMap converts a list of 2-tuples into a Map from the first element of the tuples to the second. get gives you the value for the given key and returns an Option, thus you need another get to actually get the list.
Or you can use:
data.find(_._1 == 'b').get._2
Note: Only use get on Option when you can guarantee that you'll have a Some and not a None. See http://www.scala-lang.org/api/current/index.html#scala.Option for how to use Option idiomatic.
Update: Explanation of the result types you see with your different approaches
Approach 2: find returns an Option[List[Int]] because it can not guarantee that a matching element gets found.
Approach 3: here you basically do a map, i.e. you apply a function to each element of your collection. For the element you are looking for the function returns your List[Int] for all other elements it contains the value () which is the Unit value, roughly equivalent to void in Java, but an actual type. Since the only common super type of ´List[Int]´ and ´Unit´ is ´Any´ you get a ´List[Any]´ as the result.
Approach 4 is basically the same as #3
Another way is
data.toMap.apply('b')
Or with one intermediate step this is even nicer:
val m = data.toMap
m('b')
where apply is used implicitly, i.e., the last line is equivalent to
m.apply('b')
There are multiple ways of doing it. One more way:
scala> def listInt(ls:List[(Char, List[Int])],ch:Char) = ls filter (a => a._1 == ch) match {
| case Nil => List[Int]()
| case x ::xs => x._2
| }
listInt: (ls: List[(Char, List[Int])], ch: Char)List[Int]
scala> listInt(data, 'b')
res66: List[Int] = List(1, 1)
You can try something like(when you are sure it exists) simply by adding type information.
val char = 'b'
data.collect{case (x,y:List[Int]) if x == char => y}.head
or use headOption if your not sure the character exists
data.collect{case (x,y:List[Int]) if x == char => y}.headOption
You can also solve this using pattern matching. Keep in mind you need to make it recursive though. The solution should look something like this;
def findTupleValue(tupleList: List[(Char, List[Int])], char: Char): List[Int] = tupleList match {
case (k, list) :: _ if char == k => list
case _ :: theRest => findTupleValue(theRest, char)
}
What this will do is walk your tuple list recursively. Check whether the head element matches your condition (the key you are looking for) and then returns it. Or continues with the remainder of the list.
I had a List of Scala tuples like the following:
val l = List((1,2),(2,3),(3,4))
and I wanted to map it in a list of Int where each item is the sum of the Ints in a the corresponding tuple. I also didn't want to use to use the x._1 notation so I solved the problem with a pattern matching like this
def addTuple(t: (Int, Int)) : Int = t match {
case (first, second) => first + second
}
var r = l map addTuple
Doing that I obtained the list r: List[Int] = List(3, 5, 7) as expected. At this point, almost by accident, I discovered that I can achieve the same result with an abbreviated form like the following:
val r = l map {case(first, second) => first + second}
I cannot find any reference to this syntax in the documentation I have. Is that normal? Am I missing something trivial?
See Section 8.5 of the language reference, "Pattern Matching Anonymous Functions".
An anonymous function can be defined by a sequence of cases
{case p1 =>b1 ... case pn => bn }
which appear as an expression without a prior match. The expected type of such an expression must in part be defined. It must be either scala.Functionk[S1, ..., Sk, R] for some k > 0, or scala.PartialFunction[S1, R], where the argument type(s) S1, ..., Sk must be fully determined, but the result type R may be undetermined.
The expected type deternines whether this is translated to a FunctionN or PartialFunction.
scala> {case x => x}
<console>:6: error: missing parameter type for expanded function ((x0$1) => x0$1 match {
case (x # _) => x
})
{case x => x}
^
scala> {case x => x}: (Int => Int)
res1: (Int) => Int = <function1>
scala> {case x => x}: PartialFunction[Int, Int]
res2: PartialFunction[Int,Int] = <function1>
{case(first, second) => first + second} is treated as a PartialFunction literal. See examples in "Partial Functions" section here: http://programming-scala.labs.oreilly.com/ch08.html or section 15.7 of Programming in Scala.
Method map accepts a function. In your first example you create a function, assign it to a variable, and pass it to the map method. In the second example you pass your created function directly, omitting assigning it to a variable. You are doing just the same thing.