【发布时间】:2017-02-14 19:44:53
【问题描述】:
我正在阅读this page,我在 scala 中遇到了这个函数定义:
def upper: String => String = _.toUpperCase
试用了一下,好像和这个一样:
def upper = (str: String) => str.toUpperCase()
两个功能一样吗?如果是这样,我如何阅读上述功能(第一个)?这种定义定义的方式有特定的名称吗?
【问题讨论】:
我正在阅读this page,我在 scala 中遇到了这个函数定义:
def upper: String => String = _.toUpperCase
试用了一下,好像和这个一样:
def upper = (str: String) => str.toUpperCase()
两个功能一样吗?如果是这样,我如何阅读上述功能(第一个)?这种定义定义的方式有特定的名称吗?
【问题讨论】:
【讨论】:
String => String 不是签名,而是upper 的返回类型。也许OP想要def upper(str: String) = str.toUpperCase之类的东西,这是定义接受字符串并返回字符串的方法的正常方式?
我正在阅读 […],我在 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,如果你想避免构造一个可能不需要的函数。)
我还注意到您用callbyname 标记了您的问题,但这里没有点名。在这个例子中,一切都是按值调用。
以下是编写相同方法的其他几种方法:
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()
// ^
(顺便说一下,这里也可以看到编译器是如何扩展占位符语法的。)
最后但同样重要的是,您可以使用⇒ 而不是=> 来表示函数类型:
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()
【讨论】:
两个功能一样吗?
是的,两种方法都是一样的。后者由编译器扩展以匹配前者。这种语法称为placeholder syntax for anonymous functions。
这个定义是一个产生函数的方法。在 Scala 中,方法和函数的行为不同,因为方法没有值(也称为 non-value types)。
【讨论】:
两者都是返回函数对象的方法,传递的参数将应用于该对象。
第一个方法返回一个函数对象(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
input => output 类型的任何内容都是函数类型。input(output) 类型的任何内容都表示方法【讨论】: