scala type matching
I want to write a generic m开发者_开发百科ethod to get the element at position x of a String or Int. Now my question is, how can I force Scala to convert T to Int or to call a Int specific method.
Here is the code:
def key[T](elm:T,pos:Int) = elm match{
case x:Int => {
def digit(number:Int, pos:Int):Int = {
if(pos == 0) number % 10
else digit(number/10, pos-1)
}
digit(elm.toInt,(elm.toInt).length-pos-1)
}
case x:String => (elm.toString).charAt(pos)
}
Thanks!
def key[T](elm:T,pos:Int) = elm match {
case x:Int => x.toString.charAt(pos)
case s:String => s.charAt(pos)
}
A solution using views:
implicit def i2s(i:Int):String = i.toString
def key2[T <% String](elm:T,pos:Int) = elm match {
case x:Int => x.charAt(pos)
case s:String => s.charAt(pos)
}
I would say, your problem is, that you have two types on which you like to operate, an Int and a String, and you want to perform a key-method on that input, but you don't specify, what you like to return.
My first idea for the Int would be to return an Int. toString().charAt (n) seems like a form of cheating, but maybe cheating is alright, so let's start cheating:
def cheat (a: Any, n: Int) = a.toString ().charAt (n)
cheat (2345, 2)
// res414: Char = 4
cheat ("foobar", 2)
// res415: Char = o
That was nice and easy, wasn't it? But what, if you like to return an Int for an input of Int, and an String, for an Input of String? Char seems more natural for an input of type String, and a byte would be sufficient to return a digit from 0-9, but we have a simpler type dependency, if we return a T for a T.
I'm not convinced of my solution, but I'm not convinced about the cheating, and the first solutions are only a more complicated way of cheating, aren't they?
class Atti [T] (val fun: (Int => T)) {
def at (n: Int): T = fun (n)
}
I defined an atti-class, which, for a type T needs a function, which takes an Int, and returns an T, and if you call at
on this class, it will invoke that function.
Now we define a function iGet, to get from an Int the digit at position n, and we use two parameter lists, ...
def iGet (i: Int) (n: Int) : Int = {
if (n == 0) i % 10
else iGet (i / 10)(n - 1) }
... to pass the Int, on which to operate, eagerly, to define a partially defined function:
val iAt = new Atti[Int] (iGet (2345) _)
(0 to 3).map (iAt.at (_))
// res412: scala.collection.immutable.IndexedSeq[Int] = Vector(5, 4, 3, 2)
Analog function for a string, and declaration of sAt:
def sGet (s: String) (n: Int) : String = "" + s.charAt (n)
val sAt = new Atti[String] (sGet ("foobar") _)
(0 to 3).map (sAt.at (_))
// res413: scala.collection.immutable.IndexedSeq[String] = Vector(f, o, o, b)
I guess you see that the String is evaluated left to right, while the Int is evaluated right to left, as in your introduction to the problem.
What I don't like myself is, that sAt with the payload 'foobar' isn't a String anymore, and 2345 is somehow hidden in the iAt-object.
And the function to use for a String needs to be specified for every String - which gives you more flexibility than you asked for, and more work for every instance.
I tried to build a trait and mix it into Int or String, but wasn't successful:
trait Key [T] { def at (n: Int): T }
val s : String with Key[String] = "Foobar" { def at (n: Int) : String = "" + s.charAt (n) }
<console>:7: error: type mismatch;
found : Unit
required: Int
val s : String with Key[String] = "Foobar" { def at (n: Int) : String = "" + s.charAt (n) }
^
def key[T](elm:T,pos:Int) = elm match {
case x:Int => {
def digit(number:Int, pos:Int):Int = {
if(pos == 0) number % 10
else digit(number/10, pos-1)
}
digit(x,x.length-pos-1)
}
case x:String => x.charAt(pos)
}
how can I force Scala to convert T to Int
You can't. Well, you could with some implicit checking, but that's way harder than what you have to do. Instead of using elm
, use x
:
def key[T](elm:T,pos:Int) = elm match{
case x:Int => {
def digit(number:Int, pos:Int):Int = {
if(pos == 0) number % 10
else digit(number/10, pos-1)
}
digit(x,x.toString.length-pos-1)
}
case x:String => x.charAt(pos)
}
精彩评论