I'm trying to sort a list by how close the entries of a list are to num.
I decided to try use sortWith, but the following snippet:
list.sortWith(math.abs(_ - num) < math.abs(_ - num))
failed with missing parameter type for _ in scala.
list is of type List[Int].
Following the other threads, I know that _ is somehow type ambiguous, but I'm not sure why, (and why the following snippet is not type ambiguous):
scala> val sortedDudes = dudes.sortWith(_.name < _.name)
sortedDudes: Array[Person] = Array(Al, Paul, Tyler)
(Source)
def foo = {
val num = 2
val list: List[Int] = List(1, 2)
list.sortWith((a, b) => math.abs(a - num) < math.abs(b - num))
}
work perfectly. It's because scala trying get _ from math.abs, not sortWith
In Scala, _ can be used in a variety of different situations to mean different things. The answers on this question should help clarify a few of them.
Going back to the question, it seems the OP is trying to use _ for parameter replacement. Consider the following example
List(1,2,5,7).filter(_ > 4)
Here filter takes a function of type A => Unit, so the above is shorthand for
List(1,2,5,7).filter(x => x > 4)
The underscore can stand for more than one parameter, but it must be used to refer to each parameter exactly once. This is why the sortedDudes snippet in the OP works. Therefore the following is legal.
List(1,2,5,7).reduce(_ + _)
which is shorthand for
List(1,2,5,7).reduce((a,b) => a + b)
I think the problem with the original snippet is that the compiler cannot unambiguously parse it into something of type (A, A) => Boolean as required by the sortWith method. We can give the compiler a little help as follows.
scala> def op(int: Int, num: Int) = math.abs(int - num)
op: (int: Int, num: Int)Int
scala> List(1,7,5,10).sortWith(op(_, 5) < op(_, 5))
res0: List[Int] = List(5, 7, 1, 10)
Related
This question already has answers here:
How to convert an Array to a Tuple?
(4 answers)
Closed 7 years ago.
Given:
scala> val ss = "hello_world".split("_")
ss: Array[String] = Array(hello, world)
How to turn ss into a tuple (hello, world) with a function of Array (on ss)? I'm thinking about a function so the above snippet would end up as "hello_world".split("_").to[Tuple2] or alike.
Is it possible using Scala 2.11.6 API only?
The shortest I could come up with:
scala> "hello_world" split("_") match { case Array(f, s) => (f, s) }
res0: (String, String) = (hello,world)
Of course it will throw for other non Tuple2 cases. You can have this to avoid exceptons:
scala> "hello_world" split("_") match { case Array(f, s) => Some(f, s); case _ => None }
res1: Option[(String, String)] = Some((hello,world))
Or with implicits:
scala> implicit class ArrayTupple[T](val a: Array[T]) { def pair = a match { case Array(f, s) => (f, s) } }
defined class ArrayTupple
scala> val ss = "hello_world".split("_")
ss: Array[String] = Array(hello, world)
scala> ss.pair
res0: (String, String) = (hello,world)
I'm still not very happy with the solution. Seems like you have to either write out all possible cases up to Tuple22 to make more generic method like to[Tuple22] or maybe use macros.
Unfortunatelly there's no function, that can convert Array of N elements (of course N <= 22) to TupleN. There is a proposed change to Scala API[1] to add :+ and +: methods to a tuple, but it has a low priority. What people do in their projects is basically summed up in an SO question [2].
If you do what they propose, and create classes from TupleOps2 to Tuple22` it still wouldn't be possible. Look at the code below:
def array2Tuple[T](array: Array[T]): TupleN = {
if(array.length == 2)
(array(0), array(1))
else
array(0) +: array2Tuple(array.drop(1))
}
It is still not possible, to deduce N in TupleN return type. The problem here is of course with the size of the array vs. size of Tuple. Size of array is contained inside the object, while size of Tuple is contained in a type name.
References:
[1] https://issues.scala-lang.org/browse/SI-7305
[2] How to append or prepend an element to a tuple in Scala
I don't think you can do better than
"size_known".split("_") match { case Array(a, b) => (a, b) }
or
Some("size_is_unknown".split("_")) collect { case Array(a, b) => (a, b) }
without reinventing some features of Shapeless, that makes it easy
I'm currently learning Scala, and I just wondered at fold-left.
Since fold-left is curried, you should be able to get a partially applied function(PAF) with a first parameter as below.
(0 /: List(1, 2, 3)) _
But actually, I've got an error.
<console>:8: error: missing arguments for method /: in trait TraversableOnce;
follow this method with `_' if you want to treat it as a partially applied function
Then I tried same thing by fold-right such as below
(List(1, 2, 3) :\ 0) _
In this way, it went correctly, and I could get a PAF such as ((Int, Int) => Int) => Int
I know I can get a PAF by using foldLeft method, but I wonder whether it is possible to express it with '/:' or not.
The underscore syntax does not work well with right-associative methods that take multiple parameter lists. Here are the options I see:
Declare a variable type:
val x: ((Int, Int) => Int) => Int = 0 /: List(1, 2, 3)
Similarly, use type ascription:
val x = (0 /: List(1,2,3)) : ((Int, Int) => Int) => Int
Use the postfix notation:
val x = List(1,2,3)./:(0) _
Use the foldLeft synonym:
val x = List(1,2,3).foldLeft(0) _
I played around with it, and couldn't find a configuration that works.
There's always the more explicit:
val f = List(1,2,3,4,5).foldLeft(0)_
Which is arguably neater. I'll keep poking around though.
Edit:
There's this:
val f2 = (0 /: List(1,2,3,4,5))(_: (Int,Int) => Int)
val x = f2(_+_)
But that's getting pretty ugly. Without the type annotation, it complains. That's the best I could do though.
I'm taking my first interesting steps (non-hello-world level) with Scala (2.9.1) and I'm stuck trying to understand a very uninformative error message.
It goes much like this:
error: type mismatch;
found : (Int, Array[InputEntry]) => (Int, Double)
required: (Int, Array[InputEntry]) => ?
entries.groupBy(grouper).map((k: Int, ies: Array[InputEntry]) => (k, doMyStuff(ies)))
As you can guess process in this snippet is supposed to be where some processing goes on, and it's actually a well defined function with signature Array[InputEntry] => Double.
Grouper's signature, instead, is Array[InputEntry] => Int.
I've tried to extract a function and replace the lambda but it was useless, and I'm stuck trying to understand the question mark in the error...
Any ideas?
Edit: I should clarify that InputEntry is a class I've defined, but for the sake of this example it seems to me like it's hardly relevant.
This looks like the problem:
.map((k: Int, ies: Array[InputEntry]) => (k, doMyStuff(ies)))
You need to use a case statement to unapply the params and assign them to local variables. You also need to use {} instead of () because it is an anonymous function now.
entries.groupBy(grouper).map{case (k, ies) => (k, doMyStuff(ies))}
Here's a more simple example.
scala> val x = List(("a",1),("b",2))
x: List[(java.lang.String, Int)] = List((a,1), (b,2))
scala> x.map{ case (str, num) => num }
res5: List[Int] = List(1, 2)
If you don't want to use a case statement, you have to keep the tuple as a single variable.
scala> x.map(tuple => tuple._2)
res6: List[Int] = List(1, 2)
I'm having trouble mapping a function that takes an optional parameter. I get the same type error as I would if the parameter were not optional. Here's a simple illustration:
scala> def multiple(m: Int, n: Int = 2) = m * n
multiple: (m: Int,n: Int)Int
scala> multiple(5)
res0: Int = 10
scala> multiple(5, 7)
res1: Int = 35
scala> (1 to 10).map(multiple)
<console>:7: error: type mismatch;
found : (Int, Int) => Int
required: (Int) => ?
(1 to 10).map(multiple)
Here's one way to make it work, but it requires repeating the default argument, which is a maintenance nightmare:
scala> (1 to 5).map { n => multiple(n, 2) }
res6: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8, 10)
Is there a better way to do it? More generally, why does a function with an optional parameter seem to have the same type as it would if the parameter was not optional? What is the actual type of multiple?
This seems to work:
(1 to 10).map(multiple(_))
//res0: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)
When used in a situation expecting a function, Scala will "lift" a method to a FunctionN[T1,...,R].
In this situation, because multiple takes 2 parameters, it is lifted effectively as:
(1 to 10).map(new Function2[Int,Int,Int]{ def apply(v1: Int, v2: Int) = multiple(v1, v2) })
Even though the original method has a default argument, FunctionN objects do not. The type error should now be clear here. When multiple(_) is used, this is a call to multiple with a single argument with the second defaulted and so is treated like:
(1 to 10).map(new Function1[Int,Int]{ def apply(v1: Int) = multiple(v1) })
This type checks ok as others have shown.
Note that (multiple _ ) is not the same as multiple(_). The former represents multiple with all arguments wild carded and so is a Function2, whereas the latter is applying multiple to a single wild card argument, causing the other argument to be defaulted at that point, and so is a Function1.
Defaults are implemented at compile time by introducing a new method which returns the default value. Where the defaulted method is called, if arguments are missing, the compiler will add the necessary calls to the extra methods for the default parameters before adding the call to the method itself. This means that the method itself is compiled to code which itself has no knowledge of default parameters. To see this, compile the following sample class:
class Defaults {
def m(a: Int, b: Int = 3) = a * b
def a = m(1)
def b = m(1, 2)
}
then run: javap -c Defaults
For being able to write
scala> (1 to 10).map(multiple)
you can pass a partially applied function
def multiple(m: Int, n: Int) = m * n
val mul2 = multiple(_: Int, 2)
(1 to 10) map mul2
Here's one way to make it work, but it
requires repeating the default
argument, which is a maintenance
nightmare:
Btw this works too:
scala> (1 to 5).map { n => multiple(n) }
res0: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8, 10)
So you don't need to repeat the default argument either ;) [Scala 2.9.0.RC1]
Consider this Map[String, Any]:
val m1 = Map(("k1" -> "v1"), ("k2" -> 10))
Now let's write a for:
scala> for ((a, b) <- m1) println(a + b)
k1v1
k210
So far so good.
Now let's specify the type of the second member:
scala> for ((a, b: String) <- m1) println(a + b)
k1v1
scala> for ((a, b: Integer) <- m1) println(a + b)
k210
Here, as I specify a type, filtering takes place, which is great.
Now say I want to use an Array[Any] instead:
val l1 = Array("a", 2)
Here, things break:
scala> for (v: String <- l1) println(v)
<console>:7: error: type mismatch;
found : (String) => Unit
required: (Any) => ?
My double question is:
why doesn't the second match filter as well?
is there a way to express such filtering in the second scenario without using a dirty isInstanceOf?
Well, the latter example doesn't work because it isn't spec'ed to. There's some discussion as to what would be the reasonable behavior. Personally, I'd expect it to work just like you. The thing is that:
val v: String = (10: Any) // is a compile error
(10: Any) match {
case v: String =>
} // throws an exception
If you are not convinced by this, join the club. :-) Here's a workaround:
for (va # (v: String) <- l1) println(v)
Note that in Scala 3, you can:
for (case v: String <- l1) println(v)
The main reason for the speced behavior is that we want to encourage people to add type annotations, for clarity. If in for comprehensions, they get potentially very costly filter operations instead, that's a trap we want to avoid. However, I agree that we should make it easier to specify that something is a pattern. Probably a single pair of parens should suffice.
val x: String = y // type check, can fail at compile time
val (x: String) = y // pattern match, can fail at run time
for (x: String <- ys) // type check, can fail at compile time
for ((x: String) <- ys) // pattern match, can filter at run time