Scala reduction with non-primitive lists - scala

The following function calculates the Euclidean distance between two 2D points in Scala:
def euclideanDist(pt1: List[Double], pt2: List[Double]): Double =
sqrt(pow(pt1(0)-pt2(0), 2)+pow(pt1(1)-pt2(1), 2))
I would like to design a perimeter function to accumulate the distances between each consecutive points in a list of points (or a ListBuffer).
For instance
val arr:ListBuffer[List[Double]] = ListBuffer(List(0, 0), List(0,1), List(1,1), List(1,0), List(0, 0))
perimeter(arr)
should give the output as 4.
This is what I tried:
def perimeter(arr: ListBuffer[List[Double]]): Double =
arr.reduceLeft(euclideanDist)
On execution, the compiler throws this error
Name: Unknown Error
Message: <console>:43: error: type mismatch;
found : (List[Double], List[Double]) => Double
required: (Any, List[Double]) => Any
arr.reduceLeft(euclideanDist)
^
<console>:43: error: type mismatch;
found : Any
required: Double
arr.reduceLeft(euclideanDist)
^
StackTrace:
I could go imperative and do the whole thing with a for-loop, but would like to know if this can be solved simpler in the Scala way.

What with this:
val arr:List[List[Double]] = List(List(0, 0), List(0,1), List(1,1), List(1,0), List(0, 0))
arr.sliding(2).map{case List(a,b) => euclideanDist(a,b)}.sum

Related

Scala Map's get vs apply operation: "type mismatch"

I am learning Scala and found the following:
List(('a', 1)).toMap get 'a' // Option[Int] = Some(1)
(List(('a', 1)).toMap) apply 'a' // Int = 1
(List(('a', 1)).toMap)('a') // Error: type mismatch;
found : Char('a')
required: <:<[(Char, Int),(?, ?)
(List(('a', 1)).toMap)('a')
But then assigning it to a variable works again.
val b = (List(('a', 1)).toMap)
b('a') // Int = 1
Why is this so?
The standard docs gives:
ms get k
The value associated with key k in map ms as an option, None if not found.
ms(k) (or, written out, ms apply k)
The value associated with key k in map ms, or exception if not found.
Why doesn't the third line work?
It's essentially just an idiosyncratic collision of implicit arguments with apply-syntactic sugar and strange parentheses-elimination behavior.
As explained here, the parentheses in
(List(('a', 1)).toMap)('a')
are discarded a bit too early, so that you end up with
List(('a', 1)).toMap('a')
so that the compiler attempts to interpret 'a' as an implicit evidence of (Char, Int) <:< (?, ?) for some unknown types ?, ?.
This here works (it's not useful, it's just to demonstrate what the compiler would usually expect at this position):
(List(('a', 1)).toMap(implicitly[(Char, Int) <:< (Char, Int)]))('a')
Assigning List(...).toMap to a variable also works:
({val l = List((1, 2)).toMap; l})(1)
Alternatively, you could force toMap to stop accepting arguments by feeding it to identity function that does nothing:
identity(List((1, 2)).toMap)(1)
But the easiest and clearest way to disambiguate implicit arguments and apply-syntactic sugar is to just write out .apply explicitly:
List((1, 2)).toMap.apply(1)
I think at this point it should be obvious why .get behaves differently, so I won't elaborate on that.
The signature is slightly different:
abstract def get(key: K): Option[V]
def apply(key: K): V
The issue is error handling: get will return None when an element is not found and apply will throw an exception:
scala> Map(1 -> 2).get(3)
res0: Option[Int] = None
scala> Map(1 -> 2).apply(3)
java.util.NoSuchElementException: key not found: 3
at scala.collection.immutable.Map$Map1.apply(Map.scala:111)
... 36 elided
Regarding the failing line: toMap has an implicit argument ev: A <:< (K,V) expressing a type constraint. When you call r.toMap('a') you are passing an explicit value for the implicit but it has the wrong type. Scala 2.13.0 has a companion object <:< that provides a reflexivity method (using the given type itself instead of a proper sub-type). Now the following works:
scala> List(('a', 1)).toMap(<:<.refl)('a')
res3: Int = 1
Remark: i could not invoke <:<.refl in Scala 2.12.7, the addition seems to be quite recent.

Scala inferring wrong type when using Seq append operator "+:"

I'm still pretty new to Scala. I'm having trouble trying to append two Sequences together because the compiler is complaining about the type of the Seq. I would like to start with a Seq[String] var and replace it with the addition of two Seq[String]'s. In the REPL session below, we see that y :+ x is a Seq[Object], but why?
Welcome to Scala version 2.11.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_71).
Type in expressions to have them evaluated.
Type :help for more information.
scala> val x = Seq[String]("a")
x: Seq[String] = List(a)
scala> var y = Seq[String]("b")
y: Seq[String] = List(b)
scala> y = y :+ x
<console>:9: error: type mismatch;
found : Seq[Object]
required: Seq[String]
y = y :+ x
^
scala> val z = y :+ x
z: Seq[Object] = List(b, List(a))
It's because the :+ operator expects a single item, not a sequence. So what you're trying to do is comparable to var y:List[String] = List("b", List("a")), which isn't valid. You can see this in the documentation of Seq, which shows the type of :+ to be A => Seq[A].
I think you probably want to use the ++ operator instead.

+= on a Vector gives strange / wrong type errors

I have a variable v that is a Vector, and I'm trying to add an element to it using +=. It complains that it expects a String instead of an Int:
Welcome to Scala version 2.10.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_45).
Type in expressions to have them evaluated.
Type :help for more information.
scala> var v = Vector[Int]()
v: scala.collection.immutable.Vector[Int] = Vector()
scala> v += 3
<console>:9: error: type mismatch;
found : Int(3)
required: String
v += 3
^
Why does it expect a String? When I give it a String (which is ofcourse wrong), it says it expects a Vector[Int]:
scala> v += "three"
<console>:9: error: type mismatch;
found : String
required: scala.collection.immutable.Vector[Int]
v += "three"
^
And when I give it a Vector[Int], it again expects a String:
scala> v += Vector(3)
<console>:9: error: type mismatch;
found : scala.collection.immutable.Vector[Int]
required: String
v += Vector(3)
^
Why does this happen?
I know I can add an element using +:=. But why can I not use +=, like for a Set?
Let's go through this cases one by one:
scala> v += 3
<console>:9: error: type mismatch;
found : Int(3)
required: String
v += 3
^
Here is the main problem that Vector have no + method, so compiler will default to string concatination (which is highly criticized recently as a design flaw, by the way). The problem is that left side (vector) is convertible automatically to string (via Vector.toString), but right one is not.
scala> v += "three"
<console>:9: error: type mismatch;
found : String
required: scala.collection.immutable.Vector[Int]
v += "three"
^
Here concatenation is ok, but you're trying to put result of type String to variable of type Vector[Int], which is why compiler complains. But if you define v as Any compiler will stop complaining:
var v: Any = Vector[Int]()
v += "foo"
// res1: Any = Vector()foo
Now, next case
scala> v += Vector(3)
<console>:9: error: type mismatch;
found : scala.collection.immutable.Vector[Int]
required: String
v += Vector(3)
^
String concatenation again, and again, result of type String goes to the variable of type Vector.
Now, talking about why Vector does not have the very same + operation: ordinary Set have no notion of order, whereas Vector, and Seq in general have and + would be confusing: do I add to the end or to the start? So instead of implicit rule, you have to explicitly decide whether you use :+ or +:.

Adding value to Scala map

Why does this work:
val x = Map[Int,Int]()
val y = (1, 0)
x + y
but not this?
val x = Map[Int,Int]()
x + (1, 0)
The error produced is:
<console>:11: error: type mismatch;
found : Int(1)
required: (Int, ?)
x + (1,0)
^
If I were to enter (1,0) into the REPL, it correctly types it as (Int,Int).
I should add that this works fine:
x + (1 -> 0)
This is an ambiguity caused by the similarity between the notation for tuples and the one for parameter lists :
x + (1,0) is notation for x.+(1,0) but sadly there is no method on x that takes two Int parameters. What you want is x.+((1,0)), i.e. x + ((1,0)).
There is something in Scala called auto-tupling, see this question and answers, which rewrites, for example, println (1,2) to println((1,2)). Except this will not work here because the + method takes a variable number of arguments and not a single one like println.
You get that strange error message because it expect every value in your parameter list (1,0) to be a tuple, as in myMap + ((1,2), (1,3), (3,4)). It finds an Int instead of a (Int, Int), hence the error.
add another pair of parentheses to make it work:
val x = Map[Int,Int]()
x + ((1, 0))

Polymorphic dot product in Scala and anonymous function shorthand

I'd like to implement a "matrix dot product" in Scala in the following way:
type Real = Double
type Row = Array[Real]
type Matrix = Array[Row]
def dot[T](f: (T,T) => Real)(as: Iterable[T], bs: Iterable[T]): Real =
(for ((a, b) <- as zip bs) yield f(a, b)) sum
def rowDot(r1: Row, r2: Row) = dot(_*_)(r1, r2)
def matDot(m1: Matrix, m2: Matrix) = dot(rowDot)(m1, m2)
However, the definition of rowDot doesn't work. Scala needs explicit type annotations for the anonymous function (_*_), so instead I must write
def rowDot(r1: Row, r2: Row) = dot((x:Real, y: Real) => x*y)(r1, r2)
or
def rowDot = dot((x:Real, y: Real) => x*y) _
Is there some way to change the definition of dot so that the shorthand (_*_) can be used?
Edit: Another confusion: matDot also gives type errors in certain circumstances. It fails with Arrays of Arrays, but not with Lists of Arrays
scala> matDot(Array(Array(1.0,2.0)), Array(Array(1.0,2.0,3.0)))
<console>:27: error: type mismatch;
found : Array[Array[Double]]
required: Iterable[Iterable[Real]]
matDot(Array(Array(1.0,2.0)), Array(Array(1.0,2.0,3.0)))
^
scala> matDot(List(Array(1.0,2.0)), List(Array(1.0,2.0,3.0)))
res135: Real = 5.0
What's the difference?
specifying dot[Real] explicitly should work too.
def rowDot(r1: Row, r2: Row) = dot[Real](_*_)(r1, r2)
EDIT
replying to your edit: I think the issue is that the implicit conversion from Array to WrappedArray is not applied recursively when you have a Array[Array].
Array[Int] is not an Iterable[Int]; normally, when you assign it to a Iterable, an Array[Int] is implicitly converted to a WrappedArray[Int] (where WrappedArray is a Iterable[Int]). This is what happens when you use List[Array[Int]] (you get a List[WrappedArray[Int]] implicitly).
However, as I said, the implicit conversion is not applied recursively, so an Array[Array[Int]] is not implicitly converted to WrappedArray[WrappedArray[Int]].
Here's a REPL session that demonstrates the problem:
A List[Array[Int]] can be assigned to Iterable[Iterable[Int]] (note that Array is converted to WrappedArray)
scala> val i : Iterable[Iterable[Int]] = List(Array(1,2), Array(1,2,3))
i: Iterable[Iterable[Int]] = List(WrappedArray(1, 2), WrappedArray(1, 2, 3))
An Array[Array[Int]] does not work automatically (as you discovered)
scala> val j : Iterable[Iterable[Int]] = Array(Array(1,2), Array(1,2,3))
<console>:9: error: type mismatch;
found : Array[Array[Int]]
required: Iterable[Iterable[Int]]
val j : Iterable[Iterable[Int]] = Array(Array(1,2), Array(1,2,3))
^
However, with some hand-holding (converting manually the inner Arrays to WrappedArrays) everything works again:
scala> import scala.collection.mutable.WrappedArray
import scala.collection.mutable.WrappedArray
scala> val k : Iterable[Iterable[Int]] = Array(WrappedArray.make(Array(1,2)),
WrappedArray.make(Array(1,2,3)))
k: Iterable[Iterable[Int]] = WrappedArray(WrappedArray(1, 2), WrappedArray(1, 2,
3))
Yes - if you switch your argument lists around. Type inference on function parameters works more effectively when the function parameter is alone in the last argument list:
def dot[T](as: Iterable[T], bs: Iterable[T])(f: (T,T) => Real): Real =
(for ((a, b) <- as zip bs) yield f(a, b)) sum
def rowDot(r1: Row, r2: Row) = dot(r1, r2)(_*_)