我现在正在学习Scala。我认为,在为新val赋值时指定类型是不必要的。但是,请考虑以下代码:
object MyObject {
def firstResponse(r: Array[String]): String = r(0)
def mostFrequent(r: Array[String]): String = {
(r groupBy identity mapValues (_.length) maxBy(_._2))._1
}
def mostFrequent(r: Array[String], among: Int): String = { mostFrequent(r take among) }
// throws compile error
val heuristics = Array(
firstResponse(_), mostFrequent(_, 3), mostFrequent(_, 4), mostFrequent(_, 5)
)
}如果我更改最后一行并显式指定类型,则错误将消失。
val heuristics: Array[Array[String] => String] = Array(
firstResponse, mostFrequent(_, 3), mostFrequent(_, 4), mostFrequent(_, 5)
)这里怎么了?
编辑:正如@mdm正确指出的,
//This works
val heuristics = Array(firstResponse(_), firstResponse(_))
//This does not work
val heuristics = Array(mostFrequent(_,1), mostFrequent(_,2))公开的问题是,为什么Scala能够正确地确定firstResponse(_)的类型,而对于mostFrequent(_,1)却很难这样做。
发布于 2017-08-17 15:49:04
编译器发出类似于以下内容的抱怨:
错误:(28,29)扩展函数缺少参数类型((x$3:) => mostFrequent(x$3,3))
正如您可能已经知道的,这是因为当您使用_时,编译器无法自动(推断)出这些函数的输入参数的类型。更准确地说,它不能推断出mostFrequent(_, 3)的类型。
因此,如果您通过val heuristics: Array[Array[String] => String] =或以下方法给编译器一个提示:
val heuristics = Array(
(a : Array[String]) => firstResponse(a),
(a : Array[String]) => mostFrequent(a, 3),
(a : Array[String]) => mostFrequent(a, 4),
(a : Array[String]) => mostFrequent(a, 5)
)事情会如预期的那样运作。
查看有关_使用的文章(如这或这 ),您将看到它可能意味着很多事情,这取决于上下文。在这种情况下,我怀疑混淆是因为您使用_将一个调用转换为一个具有多个参数的方法到一个匿名函数。
请注意,以下两种方法都可以正常工作:
val heuristics = Array(
firstResponse(_),
firstResponse(_),
firstResponse(_)
)
val heuristics2 = Array(
firstResponse(_),
mostFrequent(_: Array[String], 3)
)至于有多个参数的方法不能转化为匿名函数的具体原因,而具有一个参数的方法可以转换为匿名函数的具体原因,我将委托给对编译器推理机制有更深入了解的人。
发布于 2017-08-17 17:44:09
有时,当您对参数使用underscores as placeholders时,编译器可能没有足够的信息来推断缺少的参数类型。因此,您需要显式地提供类型信息。占位符语法在需要“填充”的表达式中充当“空白”,您可以填充它的任何值。因此,编译器将不了解该占位符的类型。
val foo = _ + _
//will fail - error: missing parameter type for expanded function ((x$1: <error>, x$2) => x$1.$plus(x$2))上述表达式将失败,因为编译器将无法找到填充占位符的值类型。因此,需要有某种方式让编译器知道类型。一种方法是显式地提供变量/方法的类型信息.
val foo: (String, String) => String = _ + _上述表达式将成功编译。因为,编译器从变量foo类型解析参数的类型(第1和第2占位符都是String)。
在某些情况下,编译器可以从值解析类型:
List(1,2,3).foreach(println(_))在上述情况下,List(1,2,3)是类型Int的列表,因此编译器将知道println(_)中占位符的类型信息为Int,该信息从List的值中解析。
此外,还可以显式地提供值的类型,以便让编译器知道类型。
val foo = (_:String) + (_:String) //will return function (String, String) => String在某些情况下,如果方法只有一个参数,则不需要提供显式类型参数,否则需要为占位符语法提供类型,如下所示:
scala> def firstResponse(r: Array[String]): String = r(0)
firstResponse: (r: Array[String])String
scala> val foo = firstResponse(_) //no need to provide type information
foo: Array[String] => String = <function1>
scala> def firstResponse2(r: Array[String], index:Int): String = r(index)
firstResponse2: (r: Array[String], index: Int)String
scala> val foo = firstResponse2(_, 3) //will fail, need to provide type information.
<console>:12: error: missing parameter type for expanded function ((x$1) => firstResponse2(x$1, 3))
val foo = firstResponse2(_, 3)
^
scala> val foo = firstResponse2((_:Array[String]), 3)
foo: Array[String] => String = <function1>现在开始讨论您的案例:
val heuristics = Array(
firstResponse(_), mostFrequent(_, 3), mostFrequent(_, 4), mostFrequent(_, 5)
)在这里,编译器将不知道类型是什么,因为:
您已经通过向Array[Array[String] => String]提供类型heuristics val来解决这个问题,就像在案例1中一样,因此编译器可以很好地编译它。
对于案例2,您可以如下所示修改代码:
val heuristics = Array(
firstResponse(_), mostFrequent(_:Array[String], 3), mostFrequent(_:Array[String], 4), mostFrequent(_:Array[String], 5)
)发布于 2017-08-17 22:15:16
奇怪的是,val foo = firstResponse(_)可以工作,因为规格直接禁止它:
如果函数文本没有预期类型,则必须显式地指定所有形式参数类型
Ti,并且未定义预期的e类型。
我认为它可以被视为等同于eta-扩张 firstResponse _,因为firstResponse没有重载,所以它可以在没有预期类型的情况下工作,但它被定义为相反的方式:firstResponse _的意思与x => firstResponse(x)的意思相同,根据上面的引用,x => firstResponse(x)不应该工作。
所以严格地说,它似乎是一个bug,您也应该编写firstResponse(_: Array[String])。
虽然在这种情况下,为了避免重复,我提供了预期的类型为
val heuristics = Array[Array[String] => String](
firstResponse(_), mostFrequent(_, 3), mostFrequent(_, 4), mostFrequent(_, 5)
)https://stackoverflow.com/questions/45738500
复制相似问题