【问题标题】:Underscore usage for eta expansion下划线用于 eta 扩展
【发布时间】:2017-11-06 00:51:55
【问题描述】:
scala> val names = List("Peter", "Paul", "Mary")
names: List[String] = List(Peter, Paul, Mary)

scala> names.map(_.toUpperCase)
res12: List[String] = List(PETER, PAUL, MARY)

在这种情况下,下划线表示唯一的输入参数,即名称元素。该字符串被隐式转换为 StringOps 并调用 toUpperCase。

但是,这不起作用:

scala> names.map(StringOps.toUpperCase _)
<console>:14: error: value toUpperCase is not a member of object scala.collection.immutable.StringOps
       names.map(StringOps.toUpperCase _)

我认为这种语法是我从 toUpperCase 方法获取函数引用的方式。

【问题讨论】:

  • 我试过你的代码,但得到了不同的错误。那是错误:未找到:值 StringOps。 StringOps 没有伴随对象。 String to StringOps 使用隐式 def augmentString(x: String): StringOps defined in Predef .

标签: scala


【解决方案1】:

首先,_.toUppercase没有隐式转换String转换为StringOps,由于toUppercase属于String类型,所以没必要转换为StringOps

所以_.toUppercase 实际扩展为高阶函数:val a: String =&gt; String = (str: String) =&gt; str.toUpperCase

StringOps 隐式转换定义在Predef.scalaaugmentString,并且这种转换只有在使用StringOps的方法时才会发生,比如:slicestripSuffix , stripPrefix 等,例如:

  "name".slice(0, 2)    // convert "name" to new StringOps("name").slice(0,2)
  "name".stringPrefix

【讨论】:

    【解决方案2】:

    StringOps 是一个类,因此要使用它的方法,您需要实例化一个实例:

    names.map( x => (new StringOps(x)).toUpperCase )
    

    或者

    names.map( new StringOps(_) toUpperCase )
    

    【讨论】:

    • 感谢您的回复。所以这行得通,但我认为您需要下划线来告诉 Scala 将方法 toUpperCase 转换为函数?所以,例如names.map( x =&gt; (new StringOps(x)).toUpperCase _)
    • x =&gt; (new StringOps(x)).toUpperCase 已经是 map 函数的函数类型参数,因此没有必要(也没有意义)进一步扩展 toUpperCase。 Scala 中_ 的用法和语法有时会令人困惑。这个SO link 关于各种_ 语法可能很有趣。
    • 啊,对了!所以我想我的问题是 - “1. 有没有办法使用带有下划线的 eta 扩展语法而不是显式扩展函数来编写它?2. 如果没有,这与你可以使用的情况有何不同?”跨度>
    • 我建议的第二个解决方案中使用的placeholder 语法本质上等同于将new StringOps(_) toUpperCase 扩展为x =&gt; (new StringOps(x)).toUpperCase 的eta 扩展。更多示例请见here
    • 我见过的每个演示 eta 扩展的示例都有下划线 在方法名称之后。在这种情况下,似乎下划线被用作函数x =&gt; (new StringOps(x)).toUpperCase 右侧的一个参数,这就是为什么它没关系,但在我看来这似乎是一种不同的用途?即使是我见过的零参数方法示例也有尾随下划线。但是,我看到的示例使用来自同一类或来自包对象的方法 - 所以在这种情况下,使用常规成员方法可能会有所不同?
    【解决方案3】:

    StringOps 是 String 类的包装类,这意味着它使用您正在使用的其他方法“丰富”了此类。
    当你想在一个字符串实例上调用一个方法时,你只需这样做:

    instanceName.methodName
    

    这正是您在第一个示例中所做的。

    但是,在您的第二个示例中,您正在执行以下操作:

    methodName(instanceName)
    

    toUpperCase 不带参数,它在字符串本身的实例上调用。

    【讨论】:

      猜你喜欢
      • 2023-04-03
      • 1970-01-01
      • 1970-01-01
      • 2013-10-31
      • 1970-01-01
      • 2012-10-17
      • 2014-06-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多