英文:
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
() => {
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 => 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
是一个状态转换函数。给定一个流,它将返回其头部,并通过取其尾部“改变”流的状态。请注意,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
的次数。但这完全是函数式的,因此是模块化和可组合的。例如,我们可以传递任何非空流,而不仅仅是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 => 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> (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
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 => 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)
}
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(): () => 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() : () => Int = {
var a = 0
var b = 1
return (()=>{
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)
() => {
t = (t._1 + t._2, t._1)
t._1
}
}
But in real life you should use Apocalisp's solution.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论