首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Scala中的自动类型识别

Scala中的自动类型识别
EN

Stack Overflow用户
提问于 2017-08-17 14:54:14
回答 3查看 188关注 0票数 1

我现在正在学习Scala。我认为,在为新val赋值时指定类型是不必要的。但是,请考虑以下代码:

代码语言:javascript
复制
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)
  )
}

如果我更改最后一行并显式指定类型,则错误将消失。

代码语言:javascript
复制
val heuristics: Array[Array[String] => String] = Array(
  firstResponse, mostFrequent(_, 3), mostFrequent(_, 4), mostFrequent(_, 5)
)

这里怎么了?

编辑:正如@mdm正确指出的,

代码语言:javascript
复制
//This works
val heuristics = Array(firstResponse(_), firstResponse(_))
//This does not work
val heuristics = Array(mostFrequent(_,1), mostFrequent(_,2))

公开的问题是,为什么Scala能够正确地确定firstResponse(_)的类型,而对于mostFrequent(_,1)却很难这样做。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2017-08-17 15:49:04

编译器发出类似于以下内容的抱怨:

错误:(28,29)扩展函数缺少参数类型((x$3:) => mostFrequent(x$3,3))

正如您可能已经知道的,这是因为当您使用_时,编译器无法自动(推断)出这些函数的输入参数的类型。更准确地说,它不能推断出mostFrequent(_, 3)的类型。

因此,如果您通过val heuristics: Array[Array[String] => String] =或以下方法给编译器一个提示:

代码语言:javascript
复制
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)
  )

事情会如预期的那样运作。

查看有关_使用的文章(如 ),您将看到它可能意味着很多事情,这取决于上下文。在这种情况下,我怀疑混淆是因为您使用_将一个调用转换为一个具有多个参数的方法到一个匿名函数。

请注意,以下两种方法都可以正常工作:

代码语言:javascript
复制
val heuristics = Array(
    firstResponse(_),
    firstResponse(_),
    firstResponse(_)
  )

val heuristics2 = Array(
    firstResponse(_),
    mostFrequent(_: Array[String], 3)
  )

至于有多个参数的方法不能转化为匿名函数的具体原因,而具有一个参数的方法可以转换为匿名函数的具体原因,我将委托给对编译器推理机制有更深入了解的人。

票数 1
EN

Stack Overflow用户

发布于 2017-08-17 17:44:09

有时,当您对参数使用underscores as placeholders时,编译器可能没有足够的信息来推断缺少的参数类型。因此,您需要显式地提供类型信息。占位符语法在需要“填充”的表达式中充当“空白”,您可以填充它的任何值。因此,编译器将不了解该占位符的类型。

代码语言:javascript
复制
val foo = _ + _
//will fail - error: missing parameter type for expanded function ((x$1: <error>, x$2) => x$1.$plus(x$2))

上述表达式将失败,因为编译器将无法找到填充占位符的值类型。因此,需要有某种方式让编译器知道类型。一种方法是显式地提供变量/方法的类型信息.

代码语言:javascript
复制
val foo: (String, String) => String =  _ + _

上述表达式将成功编译。因为,编译器从变量foo类型解析参数的类型(第1和第2占位符都是String)。

在某些情况下,编译器可以从值解析类型:

代码语言:javascript
复制
List(1,2,3).foreach(println(_))

在上述情况下,List(1,2,3)是类型Int的列表,因此编译器将知道println(_)中占位符的类型信息为Int,该信息从List的值中解析。

此外,还可以显式地提供值的类型,以便让编译器知道类型。

代码语言:javascript
复制
val foo =  (_:String) + (_:String) //will return function (String, String) => String

在某些情况下,如果方法只有一个参数,则不需要提供显式类型参数,否则需要为占位符语法提供类型,如下所示:

代码语言:javascript
复制
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>

现在开始讨论您的案例:

代码语言:javascript
复制
val heuristics = Array(
   firstResponse(_), mostFrequent(_, 3), mostFrequent(_, 4), mostFrequent(_, 5)
)

在这里,编译器将不知道类型是什么,因为:

  1. val启发式方法没有类型
  2. 未显式提供占位符语法的类型。

您已经通过向Array[Array[String] => String]提供类型heuristics val来解决这个问题,就像在案例1中一样,因此编译器可以很好地编译它。

对于案例2,您可以如下所示修改代码:

代码语言:javascript
复制
val heuristics = Array(
   firstResponse(_), mostFrequent(_:Array[String], 3), mostFrequent(_:Array[String], 4), mostFrequent(_:Array[String], 5)
)
票数 1
EN

Stack Overflow用户

发布于 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])

虽然在这种情况下,为了避免重复,我提供了预期的类型为

代码语言:javascript
复制
val heuristics = Array[Array[String] => String](
   firstResponse(_), mostFrequent(_, 3), mostFrequent(_, 4), mostFrequent(_, 5)
)
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45738500

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档