【问题标题】:How to make this code more functional?如何让这段代码更实用?
【发布时间】:2011-01-18 22:58:36
【问题描述】:

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

[ 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 循环和两个可变变量,让这段代码更实用。

任何帮助将不胜感激。

谢谢。 :)

【问题讨论】:

  • 我不熟悉 scala,所以我无法调整您的代码。为了将其转换为更接近功能样式,while 循环可能应该转换为尾递归函数。当然,如果 scala 没有尾调用优化,那么该解决方案是值得商榷的。
  • 可以用unfold 函数解决,但Scala 没有。也许 Scalaz 会。
  • @Daniel:你能把这个解决方案贴在这里吗?

标签: scala refactoring functional-programming


【解决方案1】:

这个尾递归函数删除了你的两个可变变量和循环:

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】:

    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】:

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

    【讨论】:

    • +1,哇!这个unfold 似乎是一个非常有用的功能。我想知道为什么标准库不提供它。
    • @Rahul 几乎所有的 Scalaz 都非常有用,但它也非常抽象,并且非常关注正确性。它对 Haskell 有很大的影响,我认为,这就是此类函数无法进入 Scala 标准库的原因。 Odersky 非常担心 Scala 对新手来说并不令人生畏和陌生,如果 Scala 代码经常求助于 Scalaz 中的类似,那肯定会有这种感觉。老实说,如果您不知道该代码应该做什么,那么在没有展开之前的知识的情况下,您将如何轻松理解它?
    • 不,可能不是。但同样适用于foldLeftflatMap 等函数。在我真正研究它们之前,我不知道它们的用途。
    • @Rahul 好吧,这不是一个明确定义的行——它甚至不是一个明确定义的主题! ——我自己也经常抱怨标准库中缺少unfold。尽管如此,如果你喜欢它,那么你应该关注 Scalaz。这是最少的。
    【解决方案4】:

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

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

    【讨论】:

      猜你喜欢
      • 2012-12-23
      • 2010-11-03
      • 2015-08-31
      • 2013-06-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多