我有一个与 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)
^
你们知道为什么我有这个奇怪的错误吗?我真的不明白...
提前感谢,
你不知道,但是这个:
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]
的句法糖。换句话说,Int => 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)
}
现在,你不同意这与你想写的东西相去甚远吗?:-(
(*(实际上,我认为t
和q
在原文中是一样的,但是我没有想出任何方法可以在没有句法糖的情况下将其写出来。
你的意思是:
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)
的意思是"对于myList
中的每个x
,如果(x % 5) == 0
,请保留它"。
怎么样:
myList.filter(_ % 5 == 0)