【问题标题】:Leave off underscore in function literal?在函数文字中去掉下划线?
【发布时间】:2012-02-24 06:49:35
【问题描述】:
scala> val alist = List(1,2,3,4,5)
alist: List[Int] = List(1, 2, 3, 4, 5)

scala> alist filter { 2.< }
res2: List[Int] = List(3, 4, 5)

scala> alist filter { 2 < }
res3: List[Int] = List(3, 4, 5)

scala> alist filter { > 3 }
<console>:1: error: ';' expected but integer literal found.
       alist filter { > 3 }

为什么{ 2.&lt; }{2 &lt;} 会起作用?我想至少我应该写{ 2 &lt; _ } 对吧?

一个不需要参数的方法,你也可以去掉点并使用后缀运算符表示法:

scala> val s = "Hello, world!"
s: java.lang.String = Hello, world!
scala> s toLowerCase
res4: java.lang.String = hello, world!

但是这里&lt;方法不是那种不需要参数的方法吧?

你能告诉我这是什么用法吗?

【问题讨论】:

标签: scala


【解决方案1】:

这样做的原因是2是一个对象,所以如果你写2.&lt;2 &lt;(在Scala中实际上是一样的),那么你是在对象@上调用一个方法&lt; 987654325@.

如果你只写&lt;&gt;,编译器会在本地范围内寻找这样的方法,但不会找到。类似地,写&gt; 3,编译器需要一个方法&gt;可用,但没有。

您也可以直接在控制台中看到这种行为:

scala> 3.<
<console>:8: error: ambiguous reference to overloaded definition,
both method < in class Double of type (x: Char)Boolean
and  method < in class Double of type (x: Short)Boolean
match expected type ?
               3.<
                 ^

如您所见,定义了几个隐含,它们将3 转换为定义&lt; 方法的类的对象。所以这在原则上有效,但不能独立存在。但是,如果您有更多类型信息(如示例中的),它会起作用。

将此与以下内容进行对比:

scala> <(3)
<console>:8: error: not found: value <
              <(3)
              ^

在这里您可以看到编译器在某处寻找独立的&lt;。请注意,错误消息显示值,但这仍然意味着它可能是一个函数,因为值类型可能是 (Int, Int) =&gt; Boolean 或类似的东西。

【讨论】:

  • 那么为什么你可以调用像 2.&lt; 这样不带参数的方法,为什么它等同于 2 &lt; 呢?任何地方正式解决这种用法?为什么我不能用空参数调用我的方法?
  • 在此上下文中,scala 允许您编写 (x: Int) =&gt; 2 &lt; xx =&gt; 2.&lt;(x)2.&lt;(_)2 &lt; _2.&lt; 或只是 2 &lt;。在所有这些情况下,scala 将创建Int =&gt; Boolean 类型的匿名函数,在调用时将调用方法 &lt;
  • @incrop,您在另一篇文章中的链接确实回答了为什么可以省略 _,但 2.&lt; 语法与语法规则不一致。如果你可以调用这样的方法,则意味着它没有任何参数,并且不会产生副作用,例如 s.toString,除非我可以在某处看到这种用法是正式的地址。
  • 这里应用的规则在 scala 参考的 §6.26.2(方法转换)和 §6.26.5(Eta 扩展)中进行了解释。
【解决方案2】:

正在发生的是 Eta 扩展 (6.26.5):

Eta-expansion 将方法类型的表达式转换为等价的 函数类型的表达。

在这种情况下,2 &lt; 是一个方法类型:Int 上的方法&lt; (其中之一)。但是,filter 需要一个函数类型。在这种情况下,Scala 会自动进行 eta 扩展。

请注意,由于filter 所期望的类型是已知的,它可以正确推断出正在调用的2 &lt; 方法。

【讨论】:

    【解决方案3】:

    2.&lt; 对象2的方法&lt;,而2.&lt;(_)返回一个带有一个参数的新函数。后者是(扩展为)(x: Int) =&gt; 2 &lt; x 的快捷方式,其中 Int 类型是由 scala 编译器从 alist 的元素类型推断出来的。

    &gt; 3 在您的情况下不引用任何对象的任何方法或对象。 &gt; 是合法的 scala 标识符(用于方法、函数或对象),但 3 不是合法标识符(它以数字开头)。 &gt; a 可以是对象&gt; (&gt;.a) 的成员a 的引用。但是您的示例中都不存在这些。 _ &gt; 3 然而返回一个带有一个参数的新函数,你也可以写成(x: Int) =&gt; x &gt; 3

    这在本质上与 Daniel C. Sobral 的回答和 incrop 对 Frank 的回答的评论相同,但不那么正式并且有更多示例。希望这有助于获得直觉。

    【讨论】:

      猜你喜欢
      • 2021-09-06
      • 1970-01-01
      • 2015-02-07
      • 1970-01-01
      • 2021-05-28
      • 2020-02-04
      • 1970-01-01
      • 2016-12-02
      • 2016-10-06
      相关资源
      最近更新 更多