如何在Scala中返回一个函数

huangapple go评论90阅读模式
英文:

How to return a function in scala

问题

我该如何在Scala中返回一个<strike>函数</strike> 具有副作用的词法闭包<sup>1</sup>?

例如,我在查看Go中的这个代码示例

...    
// fib返回一个返回连续斐波那契数的函数。
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中编写相同的代码。

<sup>1. 在Apocalisp的评论之后进行了更正</sup>

英文:

How can I return a <strike>function</strike> side-effecting lexical closure<sup>1</sup> in Scala?

For instance, I was looking at 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())

prints
1 2 3 5 8

And I can't figure out how to write the same in Scala.

<sup>1. Corrected after Apocalisp comment</sup>

答案1

得分: 21

稍微简短一些,不需要返回值。

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

Slightly shorter, you don't need the return.

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

答案2

得分: 20

Gah!可变变量?!

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

你可以返回一个字面函数,用于获取第n个斐波那契数,例如:

val fibAt: Int =&gt; Int = fib drop _ head

编辑:由于你要求以函数式的方式“每次调用f时获得不同的值”,这里是如何实现的。这里使用了Scalaz的State单子:

import scalaz._
import Scalaz._

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

f是一个状态转换函数。给定一个流,它将返回其头部,并通过取其尾部“改变”流的状态。请注意,ffib是完全无知的。下面是一个REPL会话,说明了这个过程:

scala&gt; (for { _ &lt;- f; _ &lt;- f; _ &lt;- f; _ &lt;- f; x &lt;- f } yield x)
res29: scalaz.State[scala.collection.immutable.Stream[Int],Int] = scalaz.States$$anon$1@d53513

scala&gt; (for { _ &lt;- f; _ &lt;- f; _ &lt;- f; x &lt;- f } yield x)
res30: scalaz.State[scala.collection.immutable.Stream[Int],Int]  = scalaz.States$$anon$1@1ad0ff8

scala&gt; res29 ! fib
res31: Int = 5

scala&gt; res30 ! fib
res32: Int = 3

显然,你得到的值取决于你调用f的次数。但这完全是函数式的,因此是模块化和可组合的。例如,我们可以传递任何非空流,而不仅仅是fib

所以你看,你可以在没有副作用的情况下获得效果。

英文:

Gah! Mutable variables?!

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

You can return a literal function that gets the nth fib, for example:

val fibAt: Int =&gt; Int = fib drop _ head

EDIT: Since you asked for the functional way of "getting a different value each time you call f", here's how you would do that. This uses Scalaz's State monad:

import scalaz._
import Scalaz._

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

The value f is a state transition function. Given a stream, it will return its head, and "mutate" the stream on the side by taking its tail. Note that f is totally oblivious to fib. Here's a REPL session illustrating how this works:

scala&gt; (for { _ &lt;- f; _ &lt;- f; _ &lt;- f; _ &lt;- f; x &lt;- f } yield x)
res29: scalaz.State[scala.collection.immutable.Stream[Int],Int] = scalaz.States$$anon$1@d53513

scala&gt; (for { _ &lt;- f; _ &lt;- f; _ &lt;- f; x &lt;- f } yield x)
res30: scalaz.State[scala.collection.immutable.Stream[Int],Int]  = scalaz.States$$anon$1@1ad0ff8

scala&gt; res29 ! fib
res31: Int = 5

scala&gt; res30 ! fib
res32: Int = 3

Clearly, the value you get out depends on the number of times you call f. But this is all purely functional and therefore modular and composable. For example, we can pass any nonempty Stream, not just fib.

So you see, you can have effects without side-effects.

答案3

得分: 8

虽然我们在分享与问题只有间接关系的斐波那契函数的酷炫实现时,这里有一个使用记忆化的版本:

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)
}

它使用了作为对这个问题的回答实现的记忆化不动点组合子:

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

While we're sharing cool implementations of the fibonacci function that are only tangentially related to the question, here's a memoized version:

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

It uses the memoizing fixed-point combinator implemented as an answer to this question: In Scala 2.8, what type to use to store an in-memory mutable data table?

Incidentally, the implementation of the combinator suggests a slightly more explicit technique for implementing your <strike>function</strike> side-effecting lexical closure:

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

答案4

得分: 3

明白了!经过一些尝试和错误之后:

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
英文:

Got it!! after some trial and error:

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

Testing:

val f = fib()
println(f(),f(),f(),f())

1 2 3 5 8

答案5

得分: 1

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

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

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

英文:

You don't need a temp var when using a tuple:

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

But in real life you should use Apocalisp's solution.

huangapple
  • 本文由 发表于 2010年11月24日 07:50:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/4262241.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定