在 Scala 中定义方法的九种方法?

Posted

技术标签:

【中文标题】在 Scala 中定义方法的九种方法?【英文标题】:Nine ways to define a method in Scala? 【发布时间】:2012-01-08 09:11:15 【问题描述】:

因此,我一直在尝试解决您可以在 Scala 中定义内容的各种方式,因为我对 块的处理方式缺乏了解,这使情况变得复杂:

object NewMain extends Thing

    def f1 = 10
    def f2 10
    def f3 = 10
    def f4() = 10
    def f5() 10
    def f6() = 10
    def f7 = () => 10
    def f8 = () => 10
    def f9 = () => 10

    def main(args: Array[String])
        println(f1)     // 10
        println(f2)     // ()
        println(f3)     // 10
        println(f4)     // 10
        println(f4())   // 10
        println(f5)     // ()
        println(f5())   // ()
        println(f6)     // 10
        println(f6())   // 10
        println(f7)     // <function0>
        println(f7())   // 10
        println(f8)     // <function0>
        println(f8())   // 10
        println(f9)     // <function0>
        println(f9())   // 10
    


大概其中一些是等价的,其中一些是对其他人的句法糖,还有一些是我不应该使用的东西,但我终生无法弄清楚。我的具体问题是:

println(f2)println(f5()) 怎么会给出unit?块中的最后一项不是10吗?它与println(f3()) 有什么不同,它给出了10

如果println(f5) 给出unit,那么println(f5()) 不应该是无效的,因为unit 不是函数吗?这同样适用于println(f6)println(f6())

在所有打印 10 的人中: f1, f3, f4, f4(), f6, f6(), f7(), f8(), f9(), 它们之间是否有任何功能差异(就它的作用而言)或使用差异(就我何时应该使用哪个而言)?还是它们都等价?

【问题讨论】:

我认为标题有点误导,因为它暗示有 9 种方法来定义方法。没有。这就像说你可以通过 57 种方式找到a + b(a + b)a; b(a) + ba + (b)(a + (b))(a) - b……其中一些是错误的,所有这些都是不必要的复杂。 讽刺的是,f10 是正确的方式。 【参考方案1】:

六年后,在未来更进一步发布的未来版本的 Scala 中,情况有所改善:

定义 f2f5 已被删除为“procedure syntax” 可以在没有括号 has been removed 的情况下调用 f4 f5f6

这将我们定义函数的 9 种方法和调用它们的 15 种方法缩减为定义函数的 7 种方法和调用它们的 10 种方法:

object NewMain extends Thing

    def f1 = 10
    def f3 = 10
    def f4() = 10
    def f6() = 10
    def f7 = () => 10
    def f8 = () => 10
    def f9 = () => 10

    def main(args: Array[String])
        println(f1)     // 10
        println(f3)     // 10
        println(f4())   // 10
        println(f6())   // 10
        println(f7)     // <function0>
        println(f7())   // 10
        println(f8)     // <function0>
        println(f8())   // 10
        println(f9)     // <function0>
        println(f9())   // 10
    

另见lampepfl/dotty2570lampepfl/dotty#2571

因此,哪些语法是可选的(例如s)以及哪些定义是等价的(例如def f4() = 10def f7 = () =&gt; 10)相对清晰。希望有一天,当 Dotty/Scala-3.0 发布时,学习该语言的新手将不再面临六年前的困惑。

【讨论】:

f4 和 f7 是等价的吗? """ scala> f4 res15: Int = 10 scala> f7 res16: () => Int = $$Lambda$1059/402310578@75dc1d5a scala> f4() res17: Int = 10 scala> f7() res18: Int = 10 """【参考方案2】:

按顺序回答您的问题:

f2f5() 返回 Unit 因为 scala 将任何不带“=”的 def 作为返回 Unit 的函数,无论块中的最后一项是什么。这是一件好事,否则定义一个不返回任何内容的函数就不会太冗长了。 println(f5()) 是有效的,即使它返回 Unit 因为在 scala 中 Unit 是一个有效的对象,尽管不可否认你不能实例化。例如,Unit.toString() 是一个有效的(如果不是通常有用的话)声明。 并非所有打印出10 的版本都相同。最重要的是,f7f8f9 实际上是返回返回 10 的函数的函数,而不是直接返回 10。当你声明 def f8 = () =&gt; 10 时,你声明了一个函数 f8,它不带参数并返回一个不带参数并返回单个整数的函数。当您调用 println(f8) 时,f8 会努力将该函数返回给您。当您调用 println(f8()) 时,它会返回该函数,然后立即调用它。 f1f3f4f6 函数在功能上基本相同,只是在风格上有所不同。

正如“用户未知”所表明的,大括号仅用于确定范围,对您的用例没有任何影响。

【讨论】:

为了清楚起见,您应该将“声明为 def foo = 10 的函数”更改为“方法……”。 关于你的最后一点,因为foo 被执行,无论你是否在它后面加上(),如果我想要函数foo 本身,而不是它的执行后返回值,如何你会这样做吗? 最后一点不正确。 Scala 允许您在拨打电话时省略空括号。 (你甚至可以def foo()()()()()()()() = 10 并用val x = foo 调用它。)@LiHaoyi - 你正在寻找foo _。在这种情况下,存在多少个括号很重要(零个或一个:()=&gt;Int;更多会产生一个柯里化链 ()=&gt;()=&gt;...。) 你可以写foo _。这与写() =&gt; foo 相同。顺便提一句。声明为def foo() = 10 的方法也可以不带括号调用。 "因为在 scala 中 Unit 是一个有效的对象,虽然不可否认你不能实例化" 你可以:() 是类型 Unit 的唯一值。【参考方案3】:
def f() ...

是语法糖

def f(): Unit = ...

因此,如果您省略“=”,该方法将始终返回 Unit 类型的对象。 在 Scala 中,方法和表达式总是返回一些东西。

def f() = 10
is sytactic sugar for
def f() = 
10

如果你写def f() = () => 10,就和写一样

def f() = 
() => 10

这意味着 f 返回一个函数对象。 但是你可以写

val f = () => 10

当你用 f() 调用它时,它返回 10 函数对象和方法在大多数情况下可以互换使用,但有一些语法差异。 例如 当你写

def f() = 10
println(f)

你得到“10”,但是当你写的时候

val f = () => 10
println(f)

你得到

<function0>

另一方面,当你有这个时

val list = List(1,2,3)
def inc(x: Int) = x+1
val inc2 = (x: Int) => x+1
println(list.map(inc))
println(list.map(inc2))

两个 println 都会打印同样的东西

List(2,3,4)

当您在预期函数对象的位置使用方法名称并且方法签名与预期函数对象的签名匹配时,它会自动转换。 所以list.map(inc) 会被 scala 编译器自动转换成

list.map(x => inc(x))

【讨论】:

继上一个示例之后,即使没有括号也可以调用零参数方法(如f4)如何工作?在我看来存在歧义,因为f4 可能意味着函数本身或评估函数的结果,如果零参数方法不是幂等的或有副作用,这可能会导致不同的结果, 它也适用于零参数方法。我想不出一个模棱两可的例子。但是,如果零 arg 方法的定义不带括号“def f = 10”,那么由于某种原因它将不再起作用。 def f(): Unit = ...又是def f(): Unit = ...; ()的语法糖【参考方案4】:
def f1 = 10    
def f2 10    

第二种形式不使用赋值。因此,您可以将其视为一个过程。它并不意味着返回某些东西,因此返回 Unit,即使最后一条语句可以用于返回特定的东西(但它可能是一个 if 语句,它只会在一个分支中具有特定的东西)。

def f1 = 10    
def f3 = 10  

这里不需要大括号。例如,如果您定义了一个 val,则您需要它们,因此此 val 的范围仅限于封闭块。

def sqrGtX (n:Int, x: Int) = 
  val sqr = n * n
  if (sqr > x) 
    sqr / 2 
  else x / 2 
  

您需要花括号在此处定义 val sqr。如果 val 是在内部分支中声明的,则花括号不需要位于方法的顶层:

def foo (n:Int, x: Int) = 
  if (n > x) 
    val bar = x * x + n * n
    println (bar) 
    bar - 2  
   else x - 2 

当两个方法返回相同结果时,为了进一步调查,您可以编译它们并比较字节码。两个二进制相同的方法将是相同的。

【讨论】:

以上是关于在 Scala 中定义方法的九种方法?的主要内容,如果未能解决你的问题,请参考以下文章

python中常用的九种预处理方法

数组的九种遍历方法

跨域问题的九种解决方法

图解常见的九种设计模式

清浮动的九种方式

前端图解常见的九种设计模式