【问题标题】:How to return a function in scala如何在scala中返回一个函数
【发布时间】:2011-05-14 19:37:17
【问题描述】:

如何在 Scala 中返回 函数 side-effecting lexical closure1

例如,我在看this code sample in Go

...    
// fib returns a function that returns
// successive Fibonacci numbers.
func fib() func() int {
    a, b := 0, 1
    return func() int {
        a, b = b, a+b
        return b
    }
}
...
println(f(), f(), f(), f(), f())

打印 1 2 3 5 8

我不知道如何在 Scala 中编写相同的代码。

1. Apocalisp评论后更正

【问题讨论】:

    标签: function scala return-value go


    【解决方案1】:

    知道了!!经过一些试验和错误:

    def fib() : () => Int = {
        var a = 0
        var b = 1
        return (()=>{ 
            val t = a;
            a = b
            b = t + b
            b
        })
    }
    

    测试:

    val f = fib()
    println(f(),f(),f(),f())
    
    1 2 3 5 8
    

    【讨论】:

    • 您可以使用元组初始化:var (a, b) = (0, 1)。但是,没有元组赋值。
    【解决方案2】:

    稍微短一点,不需要退货。

    def fib() = {
        var a = 0
        var b = 1
        () => { 
            val t = a;
            a = b
            b = t + b
            b
        }
    }
    

    【讨论】:

    • 感谢您发布此信息(只是帮助了我)。我想补充一点,如果希望明确说明返回类型,可以在结束的 "}" 之后添加 ": ReturnType" (例如 "} : Int" )。
    【解决方案3】:

    啊!可变变量?!

    val fib: Stream[Int] =
      1 #:: 1 #:: (fib zip fib.tail map Function.tupled(_+_))
    

    您可以返回获取第 n 个 fib 的文字函数,例如:

    val fibAt: Int => Int = fib drop _ head
    

    编辑:由于您要求“每次调用 f 时获取不同的值”的功能方式,这就是您将如何做到的。这使用了 Scalaz 的 State monad:

    import scalaz._
    import Scalaz._
    
    def uncons[A](s: Stream[A]) = (s.tail, s.head)
    val f = state(uncons[Int])
    

    f 是一个状态转换函数。给定一个流,它将返回其头部,并通过取其尾部“改变”侧面的流。请注意,f 完全忽略了fib。这是一个 REPL 会话,说明了它是如何工作的:

    scala> (for { _ <- f; _ <- f; _ <- f; _ <- f; x <- f } yield x)
    res29: scalaz.State[scala.collection.immutable.Stream[Int],Int] = scalaz.States$$anon$1@d53513
    
    scala> (for { _ <- f; _ <- f; _ <- f; x <- f } yield x)
    res30: scalaz.State[scala.collection.immutable.Stream[Int],Int]  = scalaz.States$$anon$1@1ad0ff8
    
    scala> res29 ! fib
    res31: Int = 5
    
    scala> res30 ! fib
    res32: Int = 3
    

    显然,您获得的价值取决于您拨打f 的次数。但这都是纯功能性的,因此是模块化和可组合的。例如,我们可以传递任何非空 Stream,而不仅仅是 fib

    所以你看,你可以有没有副作用的效果。

    【讨论】:

    • +1 为答案。这个例子很棒,但是,并不是我真正想要/问的。无论如何,谢谢。
    • 嗯,你确实要求一个函数。一个附带作用的词法闭包强调不是一个函数。
    • 我在 Scala 2.8.1 上收到了 error: type mismatch; found: Int; required: (Int, Int)。这里有什么问题?
    • @Apocalisp 我只是将整个表达式括在括号内,而不是使用点符号。 (fib zip fib.tail map Function.tupled(_+_))。更重要的是,使用def 而不是val 意味着这个定义不是O(1)。
    • 这是 Scala 处事方式更准确的答案,这个答案应该被接受
    【解决方案4】:

    虽然我们正在分享与问题无关的斐波那契函数的酷实现,但这里有一个记忆版本:

    val fib: Int => BigInt = {                         
       def fibRec(f: Int => BigInt)(n: Int): BigInt = {
          if (n == 0) 1 
          else if (n == 1) 1 
          else (f(n-1) + f(n-2))                           
       }                                                     
       Memoize.Y(fibRec)
    }
    

    它使用记忆定点组合器作为这个问题的答案:In Scala 2.8, what type to use to store an in-memory mutable data table?

    顺便说一句,组合器的实现建议了一种稍微更明确的技术来实现您的 function 副作用词法闭包:

    def fib(): () => Int = {
       var a = 0
       var b = 1
       def f(): Int = {
          val t = a;
          a = b
          b = t + b
          b
      }
      f
    }
    

    【讨论】:

    • 可以使用元组初始化:var (a, b) = (0, 1).
    【解决方案5】:

    使用元组时不需要临时变量:

    def fib() = {
      var t = (1,-1)
      () => { 
        t = (t._1 + t._2, t._1)
        t._1
      }
    }
    

    但在现实生活中,您应该使用 Apocalisp 的解决方案。

    【讨论】:

    • +1 呵呵,在现实生活中我不会编写斐波那契代码 :) 我同意 Apocalisp 解决方案很有启发性
    猜你喜欢
    • 1970-01-01
    • 2018-07-16
    • 1970-01-01
    • 1970-01-01
    • 2021-02-14
    • 1970-01-01
    • 2021-12-21
    • 1970-01-01
    • 2016-11-17
    相关资源
    最近更新 更多