Scala 如何使此代码更具功能性?
我是函数式编程的新手。我只是尝试解决以下问题:Scala 如何使此代码更具功能性?,scala,refactoring,functional-programming,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,5
[ 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 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(余数,基数)
,并免费获得Nil
。此尾部递归函数删除两个可变变量和循环:
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))
}
}
我认为这是一种非常昂贵的解决问题的方法,但非常直观:
scala> Stream.iterate(255)(_ / 10).takeWhile(_ > 0).map(_ % 10).reverse
res6: scala.collection.immutable.Stream[Int] = Stream(2, 5, 5)
Rahul,正如我所说,Scala中没有展开
函数。里面有一个,所以我要用这个来展示解决方案。下面的解决方案只是简单地适应使用展开而不是递归
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))
}
}
我不熟悉scala,因此无法调整您的代码。为了更接近函数式,while循环可能应该转换为尾部递归函数。当然,如果scala没有尾部调用优化,那么这个解决方案是有争议的。它可以用
unfold
函数来解决,但scala没有。也许Scalaz会。@Daniel:你能在这里发布这个解决方案吗?+1,但你不需要为递归函数指定结果类型吗?+1,哇!这个展开
似乎是一个非常有用的函数。我想知道为什么标准库不提供它。@Rahul几乎所有的Scalaz都非常有用,但它也非常抽象,非常关注正确性。它非常倾向于Haskell,我认为,这就是此类函数无法进入Scala std库的原因。Odersky非常担心Scala对新手来说并不令人畏惧和陌生,如果Scala代码经常使用Scalaz中的类似代码,它肯定会有这种感觉。老实说,如果您不知道该代码应该做什么,那么如果您之前不了解unfold,您理解它有多容易?不,可能不知道。但是像foldLeft
和flatMap
这样的函数也是如此。直到我真正研究了它们,我才知道它们是干什么的。@Rahul,好吧,这不是一条明确定义的线——它甚至不是一个明确定义的主题我自己也经常抱怨标准库中缺少explode
。尽管如此,如果你喜欢它,那么你应该关注Scalaz。这是最起码的。