3

我有一个与 Scala 中的占位符语法相关的小问题。所以我有一个简单的数字列表:

myList = List(13, 24, 10, 35)

首先,我尝试像这样过滤这个列表

myList.filter(_ => (_ % 5) == 0)

并且编译器抱怨,因为它无法推断参数类型:

error: missing parameter type for expanded function ((x$2) => x$2.$percent(5))

好的,没问题:我为参数添加了一个类型

myList.filter(_:Int => _ % 5 == 0)

现在编译器给了我这个:

identifier expected but integer literal found.
       someNumbers.filter(_:Int => _ % 5 == 0)
                                       ^

你们知道为什么我有这个奇怪的错误吗?我真的不明白...

提前致谢,

4

3 回答 3

16

你不知道,但是这个:

identifier expected but integer literal found.
       someNumbers.filter(_:Int => _ % 5 == 0)
                                       ^

是一个可怕的错误!好吧,不是人们想要的那种感觉,但是你设法写了一些语法正确的东西,并且与你的意图完全不同。首先,让我们看看重写有效的代码:

someNumbers.map(_: Int => _ <:< Any)

现在,您会看到它与您编写的内容(直到错误位置)只有两个不同:%替换为<:5替换为Any(如编译器所希望的那样——标识符而不是数字)。

你可以编译并运行上面的代码,它会返回一些东西,所以让我们试着分解它。首先,考虑以下两个陈述:

someNumbers.map(_ + 1)
someNumber.map(_) // does not compile

它们各自的含义略有不同,可以这样重写:

someNumbers.map(x => x + 1)
x => someNumbers.map(x)

第一个编译是因为在x声明参数时,编译器知道预期的类型。第二个没有编译,因为在编译器看到x时,它不知道将如何使用它。当然,这只是因为编译器重写了代码,但事情就是这样。

这里重要的是,当您添加 时: Int,编译器开始尝试编译您以第二种方式编写的内容。

你打算写的东西不是有效的代码。例如:

someNumbers.map(x => x + 1) // valid code
someNumbers.map((x: Int) => x + 1) // valid code
someNumbers.map(x : Int => x + 1) // "invalid" code

更准确地说,第三个例子是“无效的”,因为编译器不知道类型在哪里结束!要了解原因,请查看以下语句:

val f: Int => Int = x => x

在这里我们=>出现了两次,但每次都有不同的含义!第一种情况,Int => Int,是 的语法糖Function1[Int, Int]。换句话说,=>inInt => Int是类型的一部分。

在第二种情况下,x => x(大致)代表new Function1[Int,Int] { def apply(x: Int) = x }. 此=>代码中的 表示存在匿名函数,并将其参数与其主体分开。

现在我们可以理解编译器是如何解释的了someNumbers.filter(_: Int => _ % 5 == 0)。像这样:

someNumbers.filter(_: Int => _ % 5 == 0) // gets rewritten as
(x: Int => _ % 5 == 0) => someNumbers.filter(x)

意思Int => _ % 5 == 0是假定为类型。我们已经看到了为什么=>不停止它,但是错误只发生在5!这里和那里之间发生了什么?

首先,我将回到我的可编译示例。它使用了一个不太容易理解的 Scala 构造,也不是很常见:中缀类型表示法。该示例可以重写如下:

someNumbers.map(_: Int => _ <:< Any)
someNumbers.map(_: Int => <:<[_, Any])

换句话说,就像2 * 2代表2.*(2)Int Map String代表Map[Int, String]。这就解释了为什么编译器没有停下来%——它认为它代表一种类型——但它仍然给我们留下了_.

然而,在这一点上, 的含义_,尤其是重写后的形式,不应该看起来很神秘:它是一种存在类型!更具体地说,它是通配符存在类型。整个事情可以这样重写:

(x: Function1[Int,<:<[t, Any] forSome { type t }]) => someNumbers.map(x)

或者,没有任何语法糖(但在实现上略有不同*):

new Function1[Function1[Int, <:<[t, Any] forSome { type t }], List[<:<[q, Any] forSome { type q }]] { 
  def apply(x) = someNumbers.map(x) 
}

现在,您不同意这与您想写的内容相去甚远吗?:-)

(*) 其实我觉得tq原著是一样的,但是我还没有想出什么办法不用语法糖把它写出来。

于 2012-05-18T00:06:41.277 回答
6

你的意思是:

val myList = List(13, 24, 10, 35)
myList.filter(x => (x % 5) == 0)

在这种情况下,占位符_myList.map(_ + 5)制作函数的简写myList.map(x => x + 5)。所以myList.map(_ => _ + 5)因为它有点说“将每个项目映射myList到一个函数x => x和 5 的总和”。

类似这样的占位符的另一种用法是忽略参数。所以myList.map(_ => 1)意味着“忽略该项目并将所有内容映射到 5”。在您的情况下,您需要该项目来决定是否过滤它,因此_ =>没有意义。

该表达式的myList.filter(x => (x % 5) == 0)意思是“对于每个xin myList,如果保留它(x % 5) == 0”。

于 2012-05-17T18:15:53.803 回答
3

关于什么:

myList.filter(_ % 5 == 0)
于 2012-05-17T19:48:29.057 回答