【问题标题】:Generate a sequence of Fibonacci number in Scala [duplicate]在Scala中生成斐波那契数列[重复]
【发布时间】:2012-04-09 12:09:03
【问题描述】:
def fibSeq(n: Int): List[Int] = { var ret = scala.collection.mutable.ListBuffer[Int](1, 2) while (ret(ret.length - 1) < n) { val temp = ret(ret.length - 1) + ret(ret.length - 2) if (temp >= n) { return ret.toList } ret += temp } ret.toList }

以上是我使用 Scala 生成斐波那契数列的代码,其值为n。我想知道在 Scala 中是否有更优雅的方法来做到这一点?

【问题讨论】:

  • 你应该在programmers.se上问这个。事实上,这个问题太宽泛,无法合理回答。定义斐波那契数列的方法有很多种,每种方法都有自己的长处和短处。

标签: scala sequence list-comprehension fibonacci


【解决方案1】:

定义斐波那契数列的方法有很多种,但我最喜欢的是这个:

val fibs:Stream[Int] = 0 #:: 1 #:: (fibs zip fibs.tail).map{ t => t._1 + t._2 }

当您需要特定的斐波那契数时,这会创建一个延迟评估的流。

编辑: 首先,正如 Luigi Plinge 所指出的,一开始的“懒惰”是不必要的。 其次,去看看他的回答,他几乎做了同样的事情,只是更优雅。

【讨论】:

  • 是否可以使用 for-comprehension 构造?
  • 不需要是懒惰的val;懒惰只是意味着它不会急切地评估第一个术语 0,您已经将其作为文字给出
  • 似乎应该有更好的方法来做(foo zip bar).map{ t =&gt; f(t._1, t._2) }。在 Haskell 中是 zipWith f foo bar,在 Racket 中是 (map f foo bar)
  • @DanBurton:在 Scala 中,如果 f 需要一个元组,你可以写 (foo zip bar) map f,如果 f 需要两个参数,你可以写 (foo zip bar) map f.tupled
  • 与我之前的评论相反,如果它被定义为局部变量而不是对象/类字段,则 确实 需要是 lazy val。因为当它是一个字段时,编译器会将fibs 转换为this.fibs,所以你可以不用lazy 就可以逃脱。嗯。可能最好保持一致。
【解决方案2】:

这更优雅一点:

val fibs: Stream[Int] = 0 #:: fibs.scanLeft(1)(_ + _)

使用 Streams,您可以“获取”多个值,然后您可以将其转换为列表:

scala> fibs take 10 toList
res42: List[Int] = List(0, 1, 1, 2, 3, 5, 8, 13, 21, 34)

更新:我写了一个blog post,其中详细介绍了这个解决方案的工作原理,以及为什么你最终会得到一个斐波那契数列!

【讨论】:

  • 哦,我不知道scanLeft,这真的很酷。
  • @LuigiPlinge 这不是前向引用吗?仅当我应用 lazy 关键字时才有效。
  • @HunterMcMillen 实际上取决于你在哪里定义它。如果在 object 的顶层或 REPL 中,则不需要。如果它在一个方法中,那么你确实需要lazy
  • @DCKing 这是由于范围。类的成员可以引用任何其他成员,它们的定义顺序无关紧要。但在方法中,您只能引用上面已定义的内容。
  • @LuigiPlinge 我理解你的意思,但我想使用这个斐波那契数列在 Scala 中学习不可变样式编程。
【解决方案3】:

不像 Streams 那样优雅,不懒惰,但尾递归和处理 BigInt(这也很容易用 Luigis scanLeft 来做,但用 Tal 的 zip 就不那么容易了——也许只适合我)。

@tailrec 
def fib (cnt: Int, low: BigInt=0, high: BigInt=1, sofar: List[BigInt]=Nil): List[BigInt] = {
  if (cnt == 0) (low :: sofar).reverse else fib (cnt - 1, high, low + high, low :: sofar) }

scala> fib (75)
res135: List[BigInt] = List(0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765 ,10946,17711,28657,46368,75025,121393,196418,317811,514229,832040,1346269,2178309,3524578,5702887,9227465,14930352,24157817,39088169,63245986,102334155,165580141,267914296,433494437,701408733,1134903170 ,1836311903,2971215073,4807526976,7778742049,12586269025,20365011074,32951280099,53316291173,86267571272,139583862445,225851433717,365435296162,591286729879,956722026041,1548008755920,2504730781961,4052739537881,6557470319842,10610209857723,17167680177565,27777890035288,44945570212853,72723460248141,117669030460994,190392490709135 , 308061521170129, 498454011879264, 806515533049393, 1304969544928657, 2111485077978050)

【讨论】:

  • 类似:def fib(n: Int, s: List[BigInt] = List(1, 0)): List[BigInt] = if (n &lt;= 2) s.reverse else fib(n - 1, s(0) + s(1) :: s)
  • 顺便说一句,要将 Tal 的版本转换为处理 BigInt,所有您只需将左侧的 [Int] 更改为 [BigInt]!右边的 Int 字面量是隐式转换的。
【解决方案4】:

这是在中间元组上再次使用 *Stream*s 的另一种方法:

scala> val fibs = Stream.iterate( (0,1) ) { case (a,b)=>(b,a+b)  }.map(_._1) 
fibs: scala.collection.immutable.Stream[Int] = Stream(0, ?)

scala> fibs take 10 toList
res68: List[Int] = List(0, 1, 1, 2, 3, 5, 8, 13, 21, 34)

【讨论】:

    【解决方案5】:

    我发现这个实现更清晰:

    def fibonacci: Stream[Int] = {
        def loop(a: Int, b: Int): Stream[Int] = (a + b) #:: loop(b, b + a)
        loop(0, 1)
    }
    

    【讨论】:

      【解决方案6】:
      def fib:Stream[Int] ={
        def go(f0: Int, f1:Int): Stream[Int] = {
          Stream.cons(f0,go(f1,f0+f1))
        }
        go(0,1)
      }
      

      【讨论】:

        【解决方案7】:

        我最喜欢的版本是:

        def fibs(a: Int = 0, b: Int = 1): Stream[Int] = Stream.cons(a, fibs(b, a+b))
        

        使用默认值,您只需调用fibs() 并获得无限Stream

        我也认为尽管它是单行的,但它的可读性很高。

        如果你只想要第一个n,那么你可以使用take,比如fibs() take n,如果你需要它作为一个列表fibs() take n toList

        【讨论】:

          猜你喜欢
          • 2011-12-18
          • 2015-04-25
          • 2017-11-13
          • 2011-02-20
          • 1970-01-01
          • 2012-04-26
          • 1970-01-01
          • 2013-02-24
          • 1970-01-01
          相关资源
          最近更新 更多