python - Haskell、Python 和 Ruby 中的列表理解

标签 python ruby haskell list-comprehension

我已经开始查看 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 = = 0mod 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/

相关文章:

python selenium 获取表的内容

ruby-on-rails - 生成 CSV 创建问题,因为我在数据中有引号或逗号

ruby-on-rails - 从 Rails 获取 authenticity_token

haskell - 连接到 GHC 运行时系统

haskell - 访问元组中的特定元素

Python查找二维数组中节点周围邻居的列表

python - 在python2中添加

python Selenium : find and click an element

Ruby:如何将字符串转换为 boolean 值

haskell - 如何使用CallStack抛出异常?