我已经开始查看 project Euler site作为学习 Haskell 和改进我的 Python 和 Ruby 的一种方式。我认为 Haskell 和 Python 版本还可以,但我确信 Ruby 一定有更简洁的方法。
这不是关于如何让一种语言看起来像另一种语言。
这是 Problem 1 :
Q: Add all the natural numbers below one thousand that are multiples of 3 or 5.
haskell :
sum [ x | x <- [1..999], mod x 3 == 0 || mod x 5 == 0 ]
python :
sum ( [ x for x in range(1,1000) if x % 3 == 0 or x % 5 == 0 ] )
ruby :
(1..999) . map {|x| x if x % 3 == 0 || x % 5 == 0 } . compact . inject(:+)
他们都给出了相同的答案。
好的,这样 Python 就可以变成:
sum ( x for x in range(1,1000) if x % 3 == 0 or x % 5 == 0 )
它现在是一个生成器(这是一件好事,因为我们没有存储列表)
但更有趣的是:
sum( set(range(0,1000,3)) | set(range(0,1000,5)) )
出于某种原因,我又看了一遍,并尝试了一种应该是恒定时间的求和方法。在 Python 3 中:
def step_sum(mn,mx,step):
amax = mx - (mx - mn) % step
return (mn + amax) * ((1 + ((amax - mn) / step)) / 2)
step_sum(3,999,3) + step_sum(5,999,5) - step_sum(15,999,15)
Ruby 可以变成:
(1..999) . select {|x| x % 3 == 0 || x % 5 == 0} . inject(:+)
或
(1..999) . select {|x| x % 3 == 0 or x % 5 == 0} . reduce(:+)
我假设与 map
不同,select
不会产生“nul”,因此不需要调用 compact
。不错。
Haskell 也可以是:
let ƒ n = sum [0,n..999] in ƒ 3 + ƒ 5 - ƒ 15
或者更清楚:
let ƒ n = sum [ 0 , n .. 999 ] in ƒ 3 + ƒ 5 - ƒ (lcm 3 5)
作为一个让我们自己提供两个数字的函数:
ƒ :: (Integral a) => a -> a -> a
ƒ x y = let ƒ n = sum [0,n..999] in ƒ x + ƒ y - ƒ (lcm x y)
最佳答案
我喜欢 Haskell
let s n = sum [0,n..999] in s 3 + s 5 - s 15
或
sum $ filter ((>1).(gcd 15)) [0..999]
为了好玩,Rube-Goldberg 版本:
import Data.Bits
sum $ zipWith (*) [1..999] $ zipWith (.|.) (cycle [0,0,1]) (cycle [0,0,0,0,1])
好的,说明时间。
第一个版本定义了一个小函数 s,它将所有 n 的倍数相加,直到 999。如果我们对所有 3 的倍数和所有 5 的倍数求和,我们将所有 15 的倍数包含两次(在每个列表中一次),因此我们需要将它们减去一次。
第二个版本利用了 3 和 5 是质数这一事实。如果一个数包含因子 3 和 5 中的一个或两个,则该数与 15 的 gcd 将为 3、5 或 15,因此在每种情况下 gcd 都将大于 1。对于与 15 没有公因数的其他数字,gcd 变为 1。这是一步测试两个条件的好技巧。但要小心,它不适用于任意数字,例如当我们有 4 和 9 时,测试 gdc x 36 > 1
将不起作用,因为 gcd 6 36 == 6
,但 mod 6 4 = = 0
或 mod 6 9 == 0
。
第三个版本很有趣。 cycle
一遍又一遍地重复一个列表。 cycle [0,0,1]
对 3 的“整除模式”进行编码,而 cycle [0,0,0,0,1]
对 5 进行相同的处理。然后我们使用 zipWith
将两个列表“或”在一起,这给了我们 [0,0,1,0,1,1,0,0,1,1,0,1.. .]
。现在我们再次使用 zipWith
将其与实际数字相乘,得到 [0,0,3,0,5,6,0,0,9,10,0,12。 ..]
。然后我们把它加起来。
知道做同一件事的不同方法对于其他语言来说可能是一种浪费,但对于 Haskell 来说这是必不可少的。您需要发现模式、学习技巧和习语,并经常尝试,以获得有效使用这门语言的思维灵 active 。像欧拉项目问题这样的挑战是这样做的好机会。
关于python - Haskell、Python 和 Ruby 中的列表理解,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9737525/