【问题标题】:Reading a scala function读取 Scala 函数
【发布时间】:2017-02-14 19:44:53
【问题描述】:

我正在阅读this page,我在 scala 中遇到了这个函数定义:

def upper: String => String = _.toUpperCase

试用了一下,好像和这个一样:

def upper = (str: String) => str.toUpperCase()

两个功能一样吗?如果是这样,我如何阅读上述功能(第一个)?这种定义定义的方式有特定的名称吗?

【问题讨论】:

    标签: scala function


    【解决方案1】:

    这实际上不是一个函数而是一个方法。 可以阅读一下两者的区别hereand here

    这两者都是一样的,都是一个没有参数的调用upper的方法,它返回一个从String到String的函数。

    def upper: String => String = _.toUpperCase
    

    String => String是upper的返回类型。等号之后是实现。下划线只是用于引用函数参数的语法糖。

    【讨论】:

    • 其实就是一个返回函数的方法。另外,准确地说,String => String 不是签名,而是upper 的返回类型。也许OP想要def upper(str: String) = str.toUpperCase之类的东西,这是定义接受字符串并返回字符串的方法的正常方式?
    • @Jean-PhilippePellet 确实是返回类型。更新。谢谢!
    【解决方案2】:

    我正在阅读 […],我在 scala 中遇到了这个函数定义:

    def upper: String => String = _.toUpperCase
    

    那是不是一个函数定义。这是一个方法定义,这是非常不同的东西。

    试用了一下,好像和这个一样:

    def upper = (str: String) => str.toUpperCase()
    

    是的,完全一样。

    两个功能一样吗?

    两个方法是一样的。 (它们不是函数,它们是方法。)

    如果是这样,我如何阅读上述函数(第一个)?这种定义定义的方式有特定的名称吗?

    您所说的“这种方式”不是很清楚。两者都只是普通的方法定义。两者有四个区别:

    • 在第一个定义中,方法的返回类型被显式注释,在第二个定义中,它留给编译器进行推断。第一个特性的名称是“类型注释”或“显式键入”,第二个特性的名称是“类型推断”或“隐式键入”。
    • 第一个定义使用占位符语法构造要从方法返回的函数,第二个定义使用函数字面量
    • 在第二个定义中,匿名函数的参数用类型显式注释,在第一个定义中,甚至没有提及,因为我们使用占位符语法。
    • 在第一个定义中,toUpperCase 是在没有参数列表的情况下调用的,在第二个定义中,toUpperCase 是在一个空参数列表的情况下调用的。 toUpperCase 实际上是用一个空的参数列表定义的,所以应该用一个空的参数列表来调用它;但是,Scala 允许调用使用空参数列表定义的方法,而没有参数列表(但不能反过来,因为明显的歧义)。 (请注意,根据社区 Scala 风格指南,toUpperCase 应该在没有参数列表的情况下定义,因为它没有副作用;但是,在 Scala 的原始实现中,它实际上是作为 Java 方法实现的,Java 方法必须总是只有一个参数列表,没有参数列表就不可能定义 Java 方法。)

    请注意,这应该是val 而不是def,尽管:val 只被评估一次,当它被初始化时,def 每次被调用时都会被评估。但是这个def 总是返回同样的东西,所以你也可以把它设为val。 (或者lazy val,如果你想避免构造一个可能不需要的函数。)

    我还注意到您用 标记了您的问题,但这里没有点名。在这个例子中,一切都是按值调用。

    以下是编写相同方法的其他几种方法:

    def upper: Function1[String, String] = (str: String) => str.toUpperCase()
    

    这是最明确的一个,所有其他的只是从这个中留下一些东西并留给编译器来解决。但在所有情况下,编译器都会找出 this 并且编译后的代码将是相同的。

    选项 1:对方法的通用返回类型使用中缀类型构造函数语法:

    def upper: String Function1 String = (str: String) => str.toUpperCase()
    //         ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
    

    选项 2:对方法的通用返回类型使用函数类型的语法糖:

    def upper: String => String = (str: String) => str.toUpperCase()
    //         ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
    

    选项3:省略函数参数的显式类型:

    def upper: Function1[String, String] = str => str.toUpperCase()
    //                                     ↑↑↑
    

    选项 4:省略方法的显式返回类型:

    def upper = (str: String) => str.toUpperCase()
    //      ↑↑↑
    

    选项5:省略toUpperCase的空参数列表:

    def upper: Function1[String, String] = (str: String) => str.toUpperCase
    //                                                                    ↑↑
    

    选项 6:对函数使用占位符语法:

    def upper: Function1[String, String] = _.toUpperCase()
    //                                     ↑
    

    您还可以组合其中的几个选项,例如#3、#5 和 #6:

    def upper: String => String = _.toUpperCase
    

    但是!至少需要一个类型注解,这样编译器既可以从方法的返回类型中判断出函数的参数类型,也可以从函数的类型中判断出方法的返回类型。省略两者将不起作用:

    def upper = str => str.toUpperCase()
    // <console>:11: error: missing parameter type
    //            def upper = str => str.toUpperCase()
    //                        ^
    
    def upper = _.toUpperCase()
    // <console>:11: error: missing parameter type for expanded function ((x$1: <error>) => x$1.toUpperCase())
    //            def upper = _.toUpperCase()
    //                        ^
    

    (顺便说一下,这里也可以看到编译器是如何扩展占位符语法的。)

    最后但同样重要的是,您可以使用 而不是=&gt; 来表示函数类型:

    def upper: String ⇒ String = (str: String) => str.toUpperCase()
    

    还有函数字面量:

    def upper: Function1[String, String] = (str: String) ⇒ str.toUpperCase()
    

    如果我没有记错的话,那么这给了你总共 44 种不同的表达方式:

    def upper = (str: String) ⇒ str.toUpperCase
    def upper = (str: String) ⇒ str.toUpperCase()
    def upper = (str: String) => str.toUpperCase
    def upper = (str: String) => str.toUpperCase()
    def upper: Function1[String, String] = _.toUpperCase
    def upper: Function1[String, String] = _.toUpperCase()
    def upper: Function1[String, String] = (str: String) ⇒ str.toUpperCase
    def upper: Function1[String, String] = (str: String) ⇒ str.toUpperCase()
    def upper: Function1[String, String] = (str: String) => str.toUpperCase
    def upper: Function1[String, String] = (str: String) => str.toUpperCase()
    def upper: Function1[String, String] = str ⇒ str.toUpperCase
    def upper: Function1[String, String] = str ⇒ str.toUpperCase()
    def upper: Function1[String, String] = str => str.toUpperCase
    def upper: Function1[String, String] = str => str.toUpperCase()
    def upper: String ⇒ String = _.toUpperCase
    def upper: String ⇒ String = _.toUpperCase()
    def upper: String ⇒ String = (str: String) ⇒ str.toUpperCase
    def upper: String ⇒ String = (str: String) ⇒ str.toUpperCase()
    def upper: String ⇒ String = (str: String) => str.toUpperCase
    def upper: String ⇒ String = (str: String) => str.toUpperCase()
    def upper: String ⇒ String = str ⇒ str.toUpperCase
    def upper: String ⇒ String = str ⇒ str.toUpperCase()
    def upper: String ⇒ String = str => str.toUpperCase
    def upper: String ⇒ String = str => str.toUpperCase()
    def upper: String => String = _.toUpperCase
    def upper: String => String = _.toUpperCase()
    def upper: String => String = (str: String) ⇒ str.toUpperCase
    def upper: String => String = (str: String) ⇒ str.toUpperCase()
    def upper: String => String = (str: String) => str.toUpperCase
    def upper: String => String = (str: String) => str.toUpperCase()
    def upper: String => String = str ⇒ str.toUpperCase
    def upper: String => String = str ⇒ str.toUpperCase()
    def upper: String => String = str => str.toUpperCase
    def upper: String => String = str => str.toUpperCase()
    def upper: String Function1 String = _.toUpperCase
    def upper: String Function1 String = _.toUpperCase()
    def upper: String Function1 String = (str: String) ⇒ str.toUpperCase
    def upper: String Function1 String = (str: String) ⇒ str.toUpperCase()
    def upper: String Function1 String = (str: String) => str.toUpperCase
    def upper: String Function1 String = (str: String) => str.toUpperCase()
    def upper: String Function1 String = str ⇒ str.toUpperCase
    def upper: String Function1 String = str ⇒ str.toUpperCase()
    def upper: String Function1 String = str => str.toUpperCase
    def upper: String Function1 String = str => str.toUpperCase()
    

    【讨论】:

    • 谢谢,这有很大帮助!对于刚接触 Scala 的人来说,这是非常有用且综合的信息。
    • 我刚刚意识到我错过了另外两个选项,使可能的变化总数达到 44 个。
    【解决方案3】:

    两个功能一样吗?

    是的,两种方法都是一样的。后者由编译器扩展以匹配前者。这种语法称为placeholder syntax for anonymous functions

    这个定义是一个产生函数的方法。在 Scala 中,方法和函数的行为不同,因为方法没有值(也称为 non-value types)。

    【讨论】:

    • 很好的答案。 !
    【解决方案4】:

    两者都是返回函数对象的方法,传递的参数将应用于该对象。

    第一个方法返回一个函数对象(Function[A, B] 的简写),因此使用显式定义它takes A(String) and emits B(String)。 (静态打字)

    scala> def toUpperMethod: Function[String,String] = { case x => x.toUpperCase }
    toUpperMethod: Function[String,String]
    
    scala> toUpperMethod.apply("apply me") //.apply() is optional
    res9: String = APPLY ME
    
    scala> toUpperMethod("apply me")
    res10: String = APPLY ME
    

    虽然第二个是匿名方法,它也返回一个函数对象,它接受String,但可以返回Any

    scala> def toUpperAnonymous = (x: String) => x.contains("hello") match { case true => x.toUpperCase case false => x.length } 
    upperFun: String => Any
    
    scala> toUpperAnonymous.apply("hello") //.apply() is optional
    res8: Any = HELLO
    
    scala> toUpperAnonymous("yello")
    res9: Any = 5
    

    返回值的传统方法(不是上面示例中的函数对象)如下所示,它没有apply 函数。

    scala> def toUpperPureMethod(x: String) : String = x.toUpperCase
    toUpperPureMethod: (x: String)String //has type (input)output
    
    scala> toUpperPureMethod.apply("scala")
    <console>:14: error: missing argument list for method toUpperPureMethod
    Unapplied methods are only converted to functions when a function type is expected.
    You can make this conversion explicit by writing `toUpperPureMethod _` or `toUpperPureMethod(_)` instead of `toUpperPureMethod`.
           toUpperPureMethod.apply("scala")
    

    但它可以被包装成如下函数返回,

    scala> def methodReturningFunction = toUpperPureMethod _
    methodReturningFunction: String => String
    
    scala> methodReturningFunction.apply("process me")
    res11: String = PROCESS ME
    

    NOTE

    • input =&gt; output 类型的任何内容都是函数类型。
    • input(output) 类型的任何内容都表示方法

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-02-10
      • 1970-01-01
      • 2022-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-26
      • 2015-08-04
      相关资源
      最近更新 更多