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在原版中是相同的,但我没有想出任何方法来写出它没有语法糖.
你的意思是:
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".