如何改进此代码

时间:2009-09-12 15:44:17

标签: scala

我是Scala的新手,刚开始学习这门语言。

我从Project Euler页面解决了Problem 8

代码看起来像这样(我删除了读取输入文件所需的所有代码):

def max(n1: Int, n2: Int): Int = Math.max(n1, n2)

def max_product(digits: List[Int], num: Int): Int = {
    def max_core(lst: List[Int], curr_max: Int): Int = lst match {
        case a if lst.length >= num => 
            max_core(a.tail, max(lst.slice(0, num).reduceLeft(_*_), curr_max))
        case _ => curr_max
    }

    max_core(digits, 0)
}

println(max_product(1::2::3::4::2::3::Nil, 2))

工作正常,结果是正确的。但是,我对这个解决方案并不完全满意。我不喜欢max_core子功能,并且感觉它可以改进。我对FP的理解是,你应该迭代一个列表,切片似乎不是这里的方式。

问题是:如何?

4 个答案:

答案 0 :(得分:7)

首先,我不会重新发明轮子...方法max已在RichInt中定义,因此您可以为a max ba整数编写b

另外,slice已弃用,因此而不是lst.slice(0, num)我会使用lst.take(num)。当Scala 2.8启动时,不推荐使用的方法可能会消失。

编辑:确实,正如Daniel指出的那样,slice(Int, Int)并未弃用。我最初写这篇文章的时候非常匆忙,我想的是slice(Int),相当于drop(Int)。我仍然发现lst.take(num)lst.slice(0, num) :)更清晰。

(nitpick)您的最后一行也没有编译,因为您忘记将Nil添加到缺点序列的末尾。 1::2::3::4,最终会在::上调用Int,而Nil没有此方法。这就是为什么你需要将::添加到最后(在Nil上调用val numbers = /*"--the string of numbers--"*/.map(_.asDigit).toList def sliding[A](xs: List[A], w: Int): List[List[A]] = { for(n <- List.range(0, xs.size - w)) yield xs drop n take w } def product(xs: List[Int]): Int = (1 /: xs) (_ * _) sliding(numbers, 5).map(product).sort(_ > _).head )。

此外,您使用的算法乍一看并不明显。我写这个的方式如下:

sort(_ > _).head

我觉得最后一行很好地解释了算法应该做什么 - 采用列表的滑动窗口,在该滑动窗口中计算产品,然后获得计算产品的最大值(我实现了最大值)作为/:的懒惰,我可以做一些O(n)而不是O(n log(n))如果性能很关键......它仍然会在不到一秒的时间内运行。

请注意,滑动函数将位于Scala 2.8库中(请参阅Daniel's post,我在撰写此滑动定义时受到启发)。

编辑:哎呀...抱歉product。我只是喜欢它的简洁性以及折叠的初始元素出现在列表之前的事实。您可以等效地将def product(xs: List[Int]): Int = xs.foldLeft(1)(_ * _) 写为以下内容,以便更明确:

-- Flaviu Cipcigan

{{1}}

答案 1 :(得分:1)

这就是我这样做的方式。没有什么花哨。在您的代码中,您在每次迭代中都占用了列表的长度,这非常浪费。我只是将一些1(与连续数字的数量相同)附加到列表的末尾,所以我不需要检查列表的长度以终止循环。

val s = ... // string of digits
val ds = s.map(_.asDigit).toList

def findMaxProduct(ds: List[Int], n: Int, max: Int): Int = ds match {
  case Nil => max
  case _ :: rest => findMaxProduct(rest, n, Math.max(max, ds.take(n).reduceLeft(_ * _)))
}

val n = 5 // number of consecutive digits
println(findMaxProduct(ds ::: List.make(n, 1), n, -1))

答案 2 :(得分:1)

val str = ... //数字字符串

val nums = str.map {_.asDigit}
(0到nums.size-5).map {i =&gt; nums.slice(i,i + 5).product} .max

另一个,效率更高:

(0到nums.size-5).foldLeft(-1){case(r,i)=&gt; r max nums.slice(i,i + 5).product}

BTW:适用于scala2.8

答案 3 :(得分:1)

val bigNumber = """73167176531330624919225119674426574742355349194934
96983520312774506326239578318016984801869478851843
85861560789112949495459501737958331952853208805511
12540698747158523863050715693290963295227443043557
66896648950445244523161731856403098711121722383113
62229893423380308135336276614282806444486645238749
30358907296290491560440772390713810515859307960866
70172427121883998797908792274921901699720888093776
65727333001053367881220235421809751254540594752243
52584907711670556013604839586446706324415722155397
53697817977846174064955149290862569321978468622482
83972241375657056057490261407972968652414535100474
82166370484403199890008895243450658541227588666881
16427171479924442928230863465674813919123162824586
17866458359124566529476545682848912883142607690042
24219022671055626321111109370544217506941658960408
07198403850962455444362981230987879927244284909188
84580156166097919133875499200524063689912560717606
05886116467109405077541002256983155200055935729725
71636269561882670428252483600823257530420752963450""".replaceAll("\\s+","")

def getMax(m: Int, l:List[Seq[Int]]): Int = 
  if (l.head.isEmpty) m else
  getMax(m max l.foldLeft(1) ((acc, l) => acc * l.head), l map (_ tail))

def numDigits(bigNum: String, count: Int) = 
  (1 until count).foldLeft(List(bigNumber map (_ asDigit))) ((l, _) => l.head.tail :: l)

def solve(bigNum: String, count: Int) = getMax(0, numDigits(bigNum, count))

solve(bigNumber, 5)
相关问题