2010-02-19 85 views
2

我是功能性编程的新手。我只是试图解决以下问题:如何让这段代码更实用?

[ 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循环&两个可变变量并使这个代码更加有用。

任何帮助将不胜感激。

谢谢。在斯卡拉2.8 :)

+0

我不熟悉scala,所以我无法调整您的代码。为了将其更接近功能样式,可以将while循环转换为尾递归函数。当然,如果scala没有tail-call优化,那么这个解决方案是有争议的。 – nlucaroni 2010-02-19 21:16:09

+0

它可以用'unfold'函数解决,但Scala没有。也许斯卡拉兹呢。 – 2010-02-20 15:03:33

+0

@Daniel:你可以在这里发布解决方案吗? – missingfaktor 2010-02-20 16:08:13

回答

3

这尾递归函数删除你的两个可变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)) 
    } 
} 
3

尾递归解决方案:

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

+0

+1但你不需要指定递归函数的结果类型吗? – missingfaktor 2010-02-20 01:58:43

+0

对不起 - 是的,你这样做。我没有真正测试代码。 – 2010-02-20 04:09:29

+0

感谢Daniel为我修复:) – 2010-02-20 15:39:34

2

我认为这是解决问题相当昂贵的方式,但很直观的一个恕我直言:

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

拉胡尔,正如我所说的,在Scala中有不是unfold函数。在Scalaz中有一个,所以我要使用那个来展示解决方案。下面的解决方案是简单地适应Patrick'sanswer使用展开而不是递归。

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

+1,哇!这个“展开”似乎是一个非常有用的功能。我想知道为什么标准库不提供它。 – missingfaktor 2010-02-21 07:46:35

+0

@Rahul几乎所有的斯卡拉兹都非常有用,但它也非常抽象,并且非常关心正确性。它主要倾向于Haskell,我认为这就是这种功能无法进入Scala std库的原因。 Odersky非常担心Scala对新手来说似乎并不令人望而生畏,如果Scala代码经常使用Scalaz中的类似代码,它肯定会有这种感觉。说实话,如果你不知道代码应该做什么,那么在没有展开知识的情况下你会多么容易理解它? – 2010-02-21 14:15:13

+0

不,可能不是。但同样适用于'foldLeft'和'flatMap'等功能。除非我真的研究过它们,否则我不知道它们是什么。 – missingfaktor 2010-02-21 16:09:59