Почему неявное преобразование не работает в списках? - PullRequest
9 голосов
/ 18 июля 2011

Может ли кто-нибудь дать быстрое объяснение, почему неявное преобразование не работает в этих случаях? Спасибо.

scala> implicit def strTwoInt (s: String):Int = s.toCharArray.map{_.asDigit}.sum
strTwoInt: (s: String)Int

scala> List[Int]("1","2","3") sum
res3: Int = 6

scala> List("1","2","3") sum
<console>:9: error: could not find implicit value for parameter num: Numeric[java.lang.String]
        List("1","2","3") sum

scala> val a = List("1","2","3")                          
scala> a.foldLeft(0)((i:Int, j:Int) => i+j)
<console>:10: error: type mismatch;
 found   : (Int, Int) => Int
 required: (Int, java.lang.String) => Int

Ответы [ 2 ]

7 голосов
/ 18 июля 2011

Ваше неявное преобразование преобразует строку в Int. В вашем первом примере это вызвано тем, что вы пытаетесь поместить строки в список Ints.

Во втором примере у вас есть список строк и вы вызываете метод sum, который принимает неявное значение Numeric[String]. Ваше преобразование не применяется, потому что вы не пытаетесь передать строку в то место, где компилятор ожидал Int, или вы пытались вызвать метод, который определен в Int, а не в String. В этом случае вы можете либо определить Numeric[String], который использует явно ваше преобразование, либо использовать метод, который принимает List[Int] в качестве параметра (давая подсказку компилятору):

scala> def sumIt( xs: List[Int] ) = xs sum
sumIt: (xs: List[Int])Int

scala> sumIt( List("1","2","3") )
res5: Int = 6

В третьем примере второй аргумент foldLeft должен иметь тип:

(Int,String) => Int

тот, который вы на самом деле передали, имеет тип:

(Int,Int) => Int

однако вы не определили никакого неявного преобразования между этими двумя типами. Но:

a.foldLeft(0)((i:Int, j:String) => i+j)

запускает ваше преобразование и работает.

Редактировать: Вот как реализовать Numeric[String]:

implicit object StringNumeric extends math.Numeric[String] {
  val num = math.Numeric.IntIsIntegral
  def plus(x: String, y: String) = num.plus(x,y).toString
  def minus(x: String, y: String) = num.minus(x,y).toString
  def times(x: String, y: String) = num.times(x,y).toString
  def negate(x: String): String = num.negate(x).toString
  def fromInt(x: Int) = x.toString
  def toInt(x: String) = x
  def toLong(x: String) = toInt(x)
  def toFloat(x: String) = toInt(x)
  def toDouble(x: String) = toInt(x)
  def compare(x:String,y:String) = num.compare(x,y)
}

scala> List("1","2","3") sum
res1: java.lang.String = 6

Это работает, но в результате получается строка.

4 голосов
/ 18 июля 2011

Вот краткое объяснение: неявные преобразования применяются к типам, из которых выполняется преобразование и в которые напрямую (например, String и Int в вашем случае), а не к любому параметризованному типу T[String] или T[Int] - если тольконеявные преобразования были определены для самого T, и это не относится к спискам.

Ваше неявное преобразование не применяется в ваших двух случаях (и даже если у вас было неявное преобразование из List[String] вList[Int], это не будет применяться).Он будет автоматически применяться только тогда, когда вам нужно значение типа Int и вместо этого вы передаете String.Здесь, в первом случае, метод sum запрашивает неявный параметр Numeric[String] - неявное преобразование из String в Int здесь не вступает в игру.

Аналогичная проблема для вашего следующегопопытка: для foldLeft в вашей коллекции требуется функция типа (Int, String) => Int.Представьте себе беспорядок, в который мы попали бы, если бы на основе неявного преобразования из String в Int компилятор автоматически предоставил неявное преобразование из (Int, Int) => Int в (Int, String) => Int

Для всех этих случаевСамый простой способ исправить это - заранее вызвать .map(stringToInt) в вашей коллекции.

...