给定以下用于在Scala中定义函数的构造,您能解释一下它们之间的区别,以及它们的含义是什么?
def foo = {}
与
def foo() = {}
更新
感谢您的快速回复。这些都很棒。对我来说唯一的问题是:
如果我省略括号,是否还有办法传递函数?这是我在repl中得到的:
scala> def foo = {}
foo: Unit
scala> def baz() = {}
baz: ()Unit
scala> def test(arg: () => Unit) = { arg }
test: (arg: () => Unit)() => Unit
scala> test(foo)
<console>:10: error: type mismatch;
found : Unit
required: () => Unit
test(foo)
^
scala> test(baz)
res1: () => Unit = <function0>
更新2012-09-14
以下是我注意到的一些类似问题:
如果在定义中包含括号,则可以在调用方法时将其省略。如果在定义中省略它们,则在调用方法时无法使用它们。
scala> def foo() {}
foo: ()Unit
scala> def bar {}
bar: Unit
scala> foo
scala> bar()
<console>:12: error: Unit does not take parameters
bar()
^
此外,您可以使用更高阶的功能执行类似操作:
scala> def baz(f: () => Unit) {}
baz: (f: () => Unit)Unit
scala> def bat(f: => Unit) {}
bat: (f: => Unit)Unit
scala> baz(foo)
scala> baz(bar)
<console>:13: error: type mismatch;
found : Unit
required: () => Unit
baz(bar)
^
scala> bat(foo)
scala> bat(bar) // both ok
在这里baz
将只采取foo()
而不是bar
。这是什么用,我不知道。但它确实表明类型是不同的。
让我复制我在a duplicated question上发布的答案:
可以使用或不使用括号()
来定义0-arity的Scala方法。这用于通知用户该方法具有某种副作用(如打印出std out或销毁数据),而不是没有的那种,后来可以实现为val
。
这种无参数的方法在Scala中很常见。相比之下,使用空括号定义的方法(例如def height():Int)称为empty-paren方法。建议的约定是在没有参数时使用无参数方法,并且该方法仅通过读取包含对象的字段来访问可变状态(特别是,它不会改变可变状态)。
该公约支持统一访问原则[...]
总而言之,鼓励Scala中的样式定义不带参数且没有副作用的方法作为无参数方法,即省略空括号。另一方面,您永远不应该定义一个没有括号的副作用的方法,因为那个方法的调用看起来像一个字段选择。
要回答你的第二个问题,只需添加一个_
:
scala> def foo = println("foo!")
foo: Unit
scala> def test(arg: () => Unit) = { arg }
test: (arg: () => Unit)() => Unit
scala> test(foo _)
res10: () => Unit = <function0>
scala> test(foo _)()
foo!
scala>
我建议总是用以下函数开始定义:
def bar {}
并且只有在您被迫时才将其更改为:
def bar() {}
原因:让我们从可能的用途角度考虑这两个功能。他们如何被吸入以及他们可以通过哪里。
我根本不称这是一个功能:
def bar {}
它可以被调用为:
bar
但不是功能:
bar()
当我们使用call-by-name参数定义高阶函数时,我们可以使用此栏:
def bat(f: => Unit) {
f //you must not use (), it will fail f()
}
我们应该记住,=> Unit
- 甚至不是一个功能。你完全无法使用thunk,就好像它是一个函数,因为你不能选择将它视为要存储或传递的函数值。您只能触发实际参数表达式的评估(任意数量)。 Scala: passing function as block of code between curly braces
使用()
定义的函数具有更大的使用范围。它可以在与bar
相同的上下文中使用:
def foo() = {}
//invokation:
foo
//or as a function:
foo()
它可以通过call-by-name参数传递给函数:
bat(foo)
另外,如果我们定义一个更高阶的函数,它不接受按名称调用的pamameter,而是一个真正的函数:
def baz(f: () => Unit) {}
我们也可以将foo
传递给baz
:
baz(foo)
我们可以看到像foo
这样的标准函数有更大的使用范围。但是使用没有()
定义的函数加上定义高阶函数,接受call-by-name参数,让我们使用更清晰的语法。
如果您不尝试存档更好,更易读的代码,或者您需要能够将您的代码片段传递给使用call-by-name参数定义的函数和使用实际函数定义的函数,那么请定义作为标准功能:
def foo() {}
如果您希望编写更清晰易读的代码,并且您的函数没有副作用,请将函数定义为:
def bar {}
PLUS尝试定义您的高阶函数以接受按名称调用参数,但不接受函数。只有在您被迫时,才会使用上一个选项。