Scala:为什么我不能用占位符正确过滤我的Int List?例如:myList.filter(_:Int => _%5 == 0)

kaf*_*ein 3 scala type-inference placeholder scala-collections

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

myList = List(13, 24, 10, 35)
Run Code Online (Sandbox Code Playgroud)

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

myList.filter(_ => (_ % 5) == 0)
Run Code Online (Sandbox Code Playgroud)

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

error: missing parameter type for expanded function ((x$2) => x$2.$percent(5))
Run Code Online (Sandbox Code Playgroud)

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

myList.filter(_:Int => _ % 5 == 0)
Run Code Online (Sandbox Code Playgroud)

现在编译器给了我这个:

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

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

提前致谢,

Dan*_*ral 16

你不知道,但是这个:

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

是一个很棒的错误!好吧,从某种意义上说,人们并不喜欢它,但是你几乎写出了语法上正确的东西,并且与你的意图完全不同.首先,让我们看一下有效代码的重写:

someNumbers.map(_: Int => _ <:< Any)
Run Code Online (Sandbox Code Playgroud)

现在,您看到它与您编写的内容之间只有两个不同之处(直到错误位置):%替换为<:,并5替换为Any(如编译器所需 - 标识符而不是数字).

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

someNumbers.map(_ + 1)
someNumber.map(_) // does not compile
Run Code Online (Sandbox Code Playgroud)

它们各自意味着略有不同,可以像这样重写:

someNumbers.map(x => x + 1)
x => someNumbers.map(x)
Run Code Online (Sandbox Code Playgroud)

第一个编译因为,在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
Run Code Online (Sandbox Code Playgroud)

更精确地说,在第三个例子是"无效的",因为编译器不知道哪里的类型结束!要了解原因,请查看此声明:

val f: Int => Int = x => x
Run Code Online (Sandbox Code Playgroud)

在这里,我们=>出现了两次,但每次都有不同的含义!第一种情况Int => Int是,语法糖Function1[Int, Int].换句话说,=>in 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)
Run Code Online (Sandbox Code Playgroud)

意味着Int => _ % 5 == 0被认为是类型.我们已经看到为什么=>不阻止它,但错误只发生在5!这里和那里之间发生了什么?

首先,我将回到我的可编辑示例.它使用了一个不太清楚的Scala结构,并且经常看不到:中缀类型表示法.该示例可以重写如下:

someNumbers.map(_: Int => _ <:< Any)
someNumbers.map(_: Int => <:<[_, Any])
Run Code Online (Sandbox Code Playgroud)

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

然而,在这一点上_,特别是在改写形式中,其含义不应该是神秘的:它是一种存在主义的类型!更具体地说,它是通配符存在类型.整个事情可以像这样重写:

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

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

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

现在,你不同意这几乎不能远离你想写的东西吗?:-)

(*)实际上,我认为t并且q在原版中是相同的,但我没有想出任何方法来写出它没有语法糖.


dhg*_*dhg 6

你的意思是:

val myList = List(13, 24, 10, 35)
myList.filter(x => (x % 5) == 0)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,占位符_就像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)意味着"对于每个x人来说myList,保留它(x % 5) == 0".

  • 非常感谢这个明确的解释.接下来我做了这个:myList.filter(_%5 == 0)并且它摇滚:) ...谢谢 (2认同)