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
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.
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.
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 +:.
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))
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)(_*_)