在Haskell,Python和Ruby中列出理解

beo*_*ver 14 ruby python haskell list-comprehension

我已经开始将项目Euler站点作为一种学习Haskell的方法,并改进我的Python和Ruby.我认为Haskell和Python版本都可以,但我确信Ruby必须有一个更简洁的方法.

这不是关于如何使一种语言看起来像另一种语言.

这是问题1:

问:添加1000以下的所有自然数,即3或5的倍数.

哈斯克尔:

sum [ x | x <- [1..999], mod x 3 == 0 || mod x 5 == 0 ]
Run Code Online (Sandbox Code Playgroud)

蟒蛇:

sum ( [ x for x in range(1,1000) if x % 3 == 0 or x % 5 == 0 ] )
Run Code Online (Sandbox Code Playgroud)

红宝石:

(1..999) . map {|x| x if x % 3 == 0 || x % 5 == 0 } . compact . inject(:+)
Run Code Online (Sandbox Code Playgroud)

他们都给出了相同的答案.


好的,所以Python可以成为:

sum ( x for x in range(1,1000) if x % 3 == 0 or x % 5 == 0 )
Run Code Online (Sandbox Code Playgroud)

它现在是一个生成器(因为我们没有存储列表是一件好事)

但更有趣的是:

sum( set(range(0,1000,3)) | set(range(0,1000,5)) )
Run Code Online (Sandbox Code Playgroud)

出于某种原因,我再次看到这个,并尝试了一个总和方法,这应该是恒定的时间.在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)
Run Code Online (Sandbox Code Playgroud)

Ruby可以成为:

(1..999) . select {|x| x % 3 == 0 || x % 5 == 0} . inject(:+)
Run Code Online (Sandbox Code Playgroud)

要么

(1..999) . select {|x| x % 3 == 0 or x % 5 == 0} . reduce(:+)
Run Code Online (Sandbox Code Playgroud)

我假设不同map,select不产生'nul',因此没有必要打电话compact.不错.

Haskell也可以是:

let ƒ n = sum [0,n..999] in ƒ 3 + ƒ 5 - ƒ 15
Run Code Online (Sandbox Code Playgroud)

或者更清楚:

let ƒ n = sum [ 0 , n .. 999 ] in ƒ 3 + ƒ 5 - ƒ (lcm 3 5)
Run Code Online (Sandbox Code Playgroud)

作为一个功能,让我们自己提供两个数字:

ƒ :: (Integral a) => a -> a -> a
ƒ x y = let ƒ n = sum [0,n..999] in ƒ x + ƒ y - ƒ (lcm x y)
Run Code Online (Sandbox Code Playgroud)

Lan*_*dei 7

对于Haskell,我喜欢

let s n = sum [0,n..999] in s 3 + s 5 - s 15
Run Code Online (Sandbox Code Playgroud)

要么

sum $ filter ((>1).(gcd 15)) [0..999]
Run Code Online (Sandbox Code Playgroud)

为了好玩Rube-Goldberg版本:

import Data.Bits

sum $ zipWith (*) [1..999] $ zipWith (.|.) (cycle [0,0,1]) (cycle [0,0,0,0,1])
Run Code Online (Sandbox Code Playgroud)

好的,解释时间.

第一个版本定义了一个小函数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来说,它是必不可少的.你需要发现模式,拾取技巧和习语,并且玩弄很多,以获得有效使用这种语言的精神灵活性.项目欧拉问题等挑战是一个很好的机会.

  • @ user969617:包含 - 排除原则. (2认同)