建议一个更清洁的功能方式

时间:2011-02-06 10:39:23

标签: scala functional-programming idioms

以下是一些命令式代码:

var sum = 0
val spacing = 6
var x = spacing
for(i <- 1 to 10) {
  sum += x * x
  x += spacing
}

以下是我尝试“功能化”上述代码的两个尝试:

// Attempt 1
(1 to 10).foldLeft((0, 6)) {
  case((sum, x), _) => (sum + x * x, x + spacing)
}

// Attempt 2
Stream.iterate ((0, 6)) { case (sum, x) => (sum + x * x, x + spacing) }.take(11).last

我认为可能有更清洁,更好的功能性方法。会是什么?

PS:请注意,以上只是用于说明问题的示例代码;它不是来自真正的应用程序代码。

7 个答案:

答案 0 :(得分:7)

用N替换10,你有spacing * spacing * N * (N + 1) * (2 * N + 1) / 6

这是通过注意你为1..N的范围求和(spacing * i)^ 2。这个和作为间距^ 2 *(1 ^ 2 + 2 ^ 2 + ... + N ^ 2),后者的和是众所周知的N *(N + 1)*(2 * N + 1) )/ 6(见Square Pyramidal Number

答案 1 :(得分:5)

在这种情况下,我实际上喜欢懒惰序列的想法。您可以按两个逻辑步骤拆分算法。

首先,你想要处理所有自然数(好的..不是全部,但最多是max int),所以你要这样定义它们:

val naturals = 0 to Int.MaxValue

然后,您需要定义有关如何计算要求的数字的知识:

val myDoubles = (naturals by 6 tail).view map (x => x * x)

将这一切放在一起:

val naturals = 0 to Int.MaxValue
val myDoubles = (naturals by 6 tail).view map (x => x * x)
val mySum = myDoubles take 10 sum

我认为这是数学家解决这个问题的方式。而且因为所有收藏品都经过懒惰评估 - 你不会失去记忆。

修改

如果你想进一步发展数学符号的概念,你可以实际定义这种隐式转换:

implicit def math[T, R](f: T => R) = new {
  def ∀(range: Traversable[T]) = range.view map f
}

然后像这样定义myDoubles

val myDoubles = ((x: Int) => x * x) ∀ (naturals by 6 tail)

答案 2 :(得分:4)

我个人最喜欢的是:

val x = (6 to 60 by 6) map {x => x*x} sum

或者将spacing作为输入变量:

val x = (spacing to 10*spacing by spacing) map {x => x*x} sum

val x = (1 to 10) map (spacing*) map {x => x*x} sum

答案 3 :(得分:3)

有两个不同的方向。如果你想表达自己,假设你不能使用内置范围功能(因为你实际上想要更复杂的东西):

Iterator.iterate(spacing)(x => x+spacing).take(10).map(x => x*x).foldLeft(0)(_ + _)

这是一个非常通用的模式:指定你开始的内容以及如何获得前一个;然后拿出你需要的物品数量;然后以某种方式改变它们;然后将它们组合成一个答案。在简单的情况下,几乎所有这些都有快捷方式(例如,最后一个折叠是sum),但这是一种通常的方法。

但我也想知道 - 最大速度的可变命令方法有什么问题?它非常清楚,Scala允许您故意混合使用这两种样式:

var x = spacing
val last = spacing*10
val sum = 0
while (x <= last) {
  sum += x*x
  x += spacing
}

(注意forwhile慢,因为Scala编译器将for循环转换为最大通用性而非最大速度的构造。)

答案 4 :(得分:1)

这是一个简单的转换,你用一个类似SML的语法写一个尾递归函数。

val spacing = 6
fun loop (sum: int, x: int, i: int): int =
  if i > 0 then loop (sum+x*x, x+spacing, i-1)
  else sum
val sum = loop (0, spacing, 10)

这是你在找什么? (你说“清洁”和“更好”的方式是什么意思?)

答案 5 :(得分:1)

这个怎么样?

def toSquare(i: Int) = i * i
val spacing = 6
val spaceMultiples = (1 to 10) map (spacing *)
val squares = spaceMultiples map toSquare
println(squares.sum)

您必须将代码分成小部分。这可以大大提高可读性。

答案 6 :(得分:1)

这是一个单行:

(0 to 10).reduceLeft((u,v)=>u + spacing*spacing*v*v)

请注意,您需要从0开始才能获得正确的结果(否则第一个值6将仅添加,但不会被平方)。

另一种选择是首先生成正方形:

(1 to 2*10 by 2).scanLeft(0)(_+_).sum*spacing*spacing