如何使这段代码更实用?

时间:2010-02-19 20:47:45

标签: scala refactoring functional-programming

我是函数式编程的新手。我刚试过解决以下问题:

[ a rough specification ]

e.g.1:
dividend : {3,5,9}
divisor : {2,2}
radix = 10
ans (remainder) : {7}

Procedure :
dividend = 3*10^2+5*10^1+9*10^0 = 359
similarly, divisor = 22
so 359 % 22 = 7

e.g.2:
dividend : {555,555,555,555,555,555,555,555,555,555}
divisor: {112,112,112,112,112,112,112,112,112,112}
radix = 1000
ans (remainder) : {107,107,107,107,107,107,107,107,107,107}

我对此问题的解决方案是:

object Tornedo {
  def main(args: Array[String]) {
    val radix: BigInt = 1000
    def buildNum(segs: BigInt*) = (BigInt(0) /: segs.toList) { _ * radix + _ }
    val dividend = buildNum(555,555,555,555,555,555,555,555,555,555)
    val divisor = buildNum(112,112,112,112,112,112,112,112,112,112)
    var remainder = dividend % divisor
    var rem = List[BigInt]()
    while(remainder > 0) {
      rem = (remainder % radix) :: rem
      remainder /= radix
    }
    println(rem)
  }
}

虽然我对这段代码非常满意,但我想知道如何消除while循环和放大器。两个可变变量,使这个代码更具功能性。

非常感谢任何帮助。

感谢。 :)

4 个答案:

答案 0 :(得分:3)

Scala 2.8中的尾递归解决方案:

def reradix(value: BigInt, radix: BigInt, digits:List[BigInt] = Nil): List[BigInt] = {
  if (remainder==0) digits
  else reradix(value/radix ,radix ,(value % radix) :: digits)
}

这个想法通常是将一段时间转换为一个递归解决方案,在那里你可以跟踪你的解决方案(所以它可以是尾递归,就像它在这里一样)。如果您改为使用

(value % radix) :: reradix(value/radix, radix)

你也会计算解决方案,但它不会是尾递归的,所以部分答案会被推到堆栈上。使用默认参数,添加一个允许存储累积答案并使用尾递归的最终参数在语法上很好,因为您可以调用reradix(remainder,radix)并免费传递Nil

答案 1 :(得分:3)

这个尾递归函数删除你的两个mutable var和循环:

object Tornedo {
  def main(args: Array[String]) {
    val radix: BigInt = 1000
    def buildNum(segs: BigInt*) = (BigInt(0) /: segs.toList) { _ * radix + _ }
    val dividend = buildNum(555,555,555,555,555,555,555,555,555,555)
    val divisor = buildNum(112,112,112,112,112,112,112,112,112,112)
    def breakup(n: BigInt, segs: List[BigInt]): List[BigInt] = 
      if (n == 0) segs else breakup(n / radix, n % radix :: segs)
    println(breakup(dividend % divisor, Nil))
  }
}

答案 2 :(得分:3)

Rahul,正如我所说,在Scala中不是 unfold函数。 Scalaz中有一个,所以我将使用那个显示解决方案。下面的解决方案只是调整Patrick's answer来使用展开而不是递归。

import scalaz.Scalaz._

object Tornedo {
  def main(args: Array[String]) {
    val radix: BigInt = 1000
    def buildNum(segs: BigInt*) = (BigInt(0) /: segs.toList) { _ * radix + _ }
    val dividend = buildNum(555,555,555,555,555,555,555,555,555,555)
    val divisor = buildNum(112,112,112,112,112,112,112,112,112,112)
    val unfoldingFunction = (n: BigInt) => 
      if (n == 0) None else Some((n % radix, n / radix))
    println((dividend % divisor).unfold[List, BigInt](unfoldingFunction))
  }
}

答案 3 :(得分:2)

我认为这是解决问题的相当昂贵的方法,但非常直观的恕我直言:

scala> Stream.iterate(255)(_ / 10).takeWhile(_ > 0).map(_ % 10).reverse
res6: scala.collection.immutable.Stream[Int] = Stream(2, 5, 5)