以下是一些命令式代码:
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:请注意,以上只是用于说明问题的示例代码;它不是来自真正的应用程序代码。
答案 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
}
(注意for
比while
慢,因为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