Is there a way to destructure input parameters of a function in Scala (akin to Clojure)?
So, instead of
scala> def f(p: (Int, Int)) = p._1
f: (p: (Int, Int))Int
I'd like to have this (it doesn't work):
scala> def f((p1, p2): (Int, Int)) = p1
I guess in scala you would use pattern matching to achieve the same, e.g. like this:
val f: (Int, Int) => Int = { case (p1, p2) => p1 }
Or, equivalently:
def f(p: (Int, Int)) = p match { case(p1, p2) => p1 }
If the types can be inferred, the (Int, Int) => Int can be dropped:
List((1, 2), (3, 4)) map { case (p1, p2) => p1 }
def f(p: ((Int, Int), (Int, Int))) = p._1 > f: (p: ((Int, Int), (Int, Int)))(Int, Int)
f((1,2), (3,4)) > res1: (Int, Int) = (1,2)
Related
I want to sum the Map("one" -> 2, "two" -> 3, "three" -> 4)'s values 2+3+4, of course I can use the following methods:
Map("one" -> 2, "two" -> 3, "three" -> 4).foldLeft(0)(_ + _._2)
Map("one" -> 2, "two" -> 3, "three" -> 4).values.sum()
I found Map has a another more direct API sum: def sum: A, but, I don't search any example about this API, How to use it?
For sum[B >: (A, B)](implicit num: Numeric[B]): B, as you see, it needs the implicit parameter and the parameter type is Numeric[B] type.
Scala Numeric is trait that defined a series of math operation.
http://www.scala-lang.org/api/current/scala/math/Numeric.html
for your case, the type is Map[String, Int], so you need to implement the implicit for Numeric[(String, Int)] for sum method, like:
trait MyTupleNumeric extends Numeric[(String, Int)] {
def plus(x: (String, Int), y: (String, Int)) = ("", x._2 + y._2)
override def minus(x: (String, Int), y: (String, Int)): (String, Int) = ("", x._2 - x._2)
override def times(x: (String, Int), y: (String, Int)): (String, Int) = ("", x._2 * y._2)
override def negate(x: (String, Int)): (String, Int) = ("", -x._2)
override def fromInt(x: Int): (String, Int) = ("", x)
override def toInt(x: (String, Int)): Int = x._2
override def toLong(x: (String, Int)): Long = x._2.toLong
override def toFloat(x: (String, Int)): Float = x._2.toFloat
override def toDouble(x: (String, Int)): Double = x._2.toDouble
override def compare(x: (String, Int), y: (String, Int)): Int = x._2 - y._2
}
implicit object MyTupleNumericImplicit extends MyTupleNumeric
val f = implicitly[Numeric[(String, Int)]] // implicitly is used find implicits base on the type
println(f.plus(("one", 2), ("two", 3)))
val r = Map("one" -> 2, "two" -> 3, "three" -> 4)
println(r.sum._2)
println(r.sum(MyTupleNumericImplicit))
As the above code, we have implement own Numeric type with (String, Int), and implements methods.
And we implicit this into our scope, so we can use implicitly to get the function and call.
And the sum method also could find the implicit parameter for Numeric[(String, Int)]
The Map.sum method doesn't do what you want -- your functions look good as they are.
The reason why Map has a sum method is that all TraversableOnce classes have a sum method, which only works if the collection type is numeric. However, a Map[K, V] is a TraversableOnce[(K, V)], so this approach won't work (the key-value tuple is not a numeric type).
Why is the following code does not work, and how can I overcome it using Iterator?
def f(str : String) : (String, String) = {
str.splitAt(1)
}
var with_id : Iterator[(String, Int)] = List(("test", 1), ("list", 2), ("nothing", 3), ("else", 4)).iterator
println(with_id.mkString(" "))
val result = with_id map { (s : String, i : Int) => (f(s), i) }
println(result.mkString(" "))
Expected output is:
(("t", "est"), 1) (("l", "ist"), 2) ...
Error:
Error:(28, 54) type mismatch;
found : (String, Int) => ((String, String), Int)
required: ((String, Int)) => ?
val result = with_id map { (s : String, i : Int) => (f(s), i) }
^
The problem is that (s : String, i : Int) => (f(s), i) is a Function2 (i.e a function that takes 2 arguments):
scala> (s : String, i : Int) => (f(s), i)
res3: (String, Int) => ((String, String), Int) = <function2>
whereas .map expects a Function1 (taking a tuple as its argument).
You could define a Function1 with
scala> val g = (t: (String, Int)) => (f(t._1), t._2)
g: ((String, Int)) => ((String, String), Int) = <function1>
scala> val result = with_id map g
result: Iterator[((String, String), Int)] = non-empty iterator
But it seems much better (to me at least) to use the more idiomatic pattern matching anonymous function (note the added case) :
scala> val result = with_id map { case (s : String, i : Int) => (f(s), i) }
result: Iterator[((String, String), Int)] = non-empty iterator
with_id.map expects a ((String, Int) => ?) function as input. That is, a function that takes a Tuple as input, not two parameters.
You can use it like this:
with_id map{ case (s,i) => (f(s), i)} //match the input tuple to s and i
Dеar Scala,
scala> val f1: ((Int, Int)) => Int = { case (a, b) => a + b }
f1: ((Int, Int)) => Int = <function1>
scala> val f2: (Int, Int) => Int = { case (a, b) => a + b }
f2: (Int, Int) => Int = <function2>
huh?!
scala> f1(1, 2)
res2: Int = 3
Ok...
scala> def takesIntInt2Int(fun: (Int, Int) => Int) = fun(100, 200)
takesIntInt2Int: (fun: (Int, Int) => Int)Int
scala> def takesTuple2Int(fun: ((Int, Int)) => Int) = fun(100, 200)
takesTuple2Int: (fun: ((Int, Int)) => Int)Int
scala> takesIntInt2Int(f2)
res4: Int = 300
scala> takesIntInt2Int(f1)
<console>:10: error: type mismatch;
found : ((Int, Int)) => Int
required: (Int, Int) => Int
takesIntInt2Int(f1)
^
scala> takesTuple2Int(f1)
res6: Int = 300
scala> takesTuple2Int(f2)
<console>:10: error: type mismatch;
found : (Int, Int) => Int
required: ((Int, Int)) => Int
takesTuple2Int(f2)
Right. And now, look at this!
scala> takesTuple2Int { case (a, b, c) => a + b + c }
<console>:9: error: constructor cannot be instantiated to expected type;
found : (T1, T2, T3)
required: (Int, Int)
takesTuple2Int { case (a, b, c) => a + b + c }
^
scala> takesIntInt2Int { case (a, b, c) => a + b + c }
<console>:9: error: constructor cannot be instantiated to expected type;
found : (T1, T2, T3)
required: (Int, Int)
takesIntInt2Int { case (a, b, c) => a + b + c }
Like, srsly? o_O Both result in required: (Int, Int) error.
Why then use case at all in such anonymous functions?
See section 8.5 of the Scala reference (http://www.scala-lang.org/files/archive/nightly/pdfs/ScalaReference.pdf). The expression { case (a, b) => a + b } is interpreted differently based on the expected type. In your definition of f1 it created a PartialFunction[(Int, Int), Int] which was cast to a Function1[(Int, Int), Int], i.e. ((Int, Int)) => Int whereas in the definition of f2 it created a Function2[Int, Int, Int], i.e. (Int, Int) => Int.
These two interpretations relate to the two situations where you would commonly use case in an anonymous function.
One is for writing anonymous functions that accept tuples and work on their components, as you did with f1. An example would be the function you pass to the foreach or map method on a Map, e.g. Map(1 -> 2, 3 -> 4) map { case (k, v) => k + v }.
The second is for writing an anonymous function that does a match on its sole parameter. Your f2 is doing this, but not in any useful way. An example would be the anonymous function passed to collect, e.g. List(1, -2, 3) collect { case x if x > 0 => -x }.
Note that the two can be combined, that is functions like f1 can do complex matching as well. For example, Map(1 -> 2, 3 -> 4) collect { case (k, v) if k < 2 => v }.
Edit: res2 works because of tupling. If an application doesn't type check, the compiler will try wrapping the args in a tuple before failing.
But that is tried just for applications; it's not a general conversion, as you discovered. It will not try to upgrade a value Function2[A, B, C] to Function1[(A, B), C].
can someone explain the best way to get around the following,
rather curious type error. Suppose I create a list of tuples like so:
scala> val ys = List((1,2), (3,4), (5,6))
ys: List[(Int, Int)] = List((1,2), (3,4), (5,6))
Now, if I want to map this to a List(Int)
scala> ys.map((a: Int, b: Int) => a + b)
<console>:9: error: type mismatch;
found : (Int, Int) => Int
required: ((Int, Int)) => ?
ys.map((a: Int, b: Int) => a + b)
^
Any clues? I know I can use the for comprehension
scala> for ((a, b) <- ys) yield a + b
res1: List[Int] = List(3, 7, 11)
But it feels wrong to bust out a comprehension in this setting. Thanks!
try:
ys.map { case (a: Int, b: Int) => a + b }
or:
ys.map(p: (Int, Int) => p._1 + p._2)
What's happening is that ys is a List of (Int,Int), so map expects a function from a single argument, which happens to be a tuple (Int,Int), to something else (technically, map expects an argument of Function1[(Int,Int),Int]. The function (a: Int, b: Int) => a+b is not actually a function from a single argument (Int, Int) to Int; instead it's a function of two arguments, both Ints, to an Int (a Function2[Int,Int,Int]). The difference is subtle, but important since Scala makes a distinction:
val f: Function1[(Int,Int),Int] = (p: (Int,Int)) => p._1 + p._2
ys.map(f) // fine
val g: Function1[(Int,Int),Int] = { case (a: Int, b: Int) => a + b }
ys.map(g) // fine, technically a PartialFunction[(Int,Int),Int]
val h: Function2[Int,Int,Int] = (a: Int, b: Int) => a + b
ys.map(h) // ERROR!
To explain my suggestions at the top of the answer: In the first example, we have changed the definition of the function given to map to use case, which tells Scala to unpack the single (Int,Int) argument into its two parts. (Note also the use of curly braces instead of parentheses.) In the second example, we have a function of a single tuple argument, p, and we manually extract each part of the tuple.
Finally, note that you don't need the type annotations either. These work just as well:
ys.map { case (a,b) => a + b }
ys.map(p => p._1 + p._2)
try:
val ys = List((1,2),(3,4),(5,6))
ys map (t => t._1 + t._2)
scala> val m = Map(1 -> 2)
m: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2)
scala> m.map{case (a, b) => (a+ 1, a+2, a+3)}
res42: scala.collection.immutable.Iterable[(Int, Int, Int)] = List((2,3,4))
What I want is for the result type to be List[(Int, Int, Int)]. The only way I found is:
scala> m.map{case (a, b) => (a+ 1, a+2, a+3)}(breakOut[Map[_,_], (Int, Int, Int), List[(Int, Int, Int)]])
res43: List[(Int, Int, Int)] = List((2,3,4))
Is there a shorter way?
You can make it a bit more concise by letting the type parameters to breakOut be inferred from the return type:
scala> m.map{case (a, b) => (a+1, a+2, a+3)}(breakOut) : List[(Int, Int, Int)]
res3: List[(Int, Int, Int)] = List((2,3,4))
Whilst Ben's is the correct answer, an alternative would have been to use a type alias:
type I3 = (Int, Int, Int)
m.map{case (a, b) => (a+ 1, a+2, a+3)}(breakOut[Map[_,_], I3, List[I3]])
Combining Ben and oxbow_lakes' answers, you can get a little shorter still:
type I3 = (Int, Int, Int)
m.map {case (a, b) ⇒ (a+1, a+2, a+3)}(breakOut): List[I3]