在Haskell,Python和Ruby中列出理解 [英] List comprehension in Haskell, Python and Ruby

查看:99
本文介绍了在Haskell,Python和Ruby中列出理解的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经开始研究项目Euler网站,作为学习Haskell的一种方式,并且改进我的Python和Ruby 。我认为Haskell和Python版本都可以,但我相信Ruby必须有更清晰的方式。



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



这是问题1:


Q :添加1000以下的所有自然数,它们是3或5的倍数。


Haskell:

  sum [x | x < -  [1..999],mod x 3 == 0 || mod x 5 == 0] 

Python:

  sum([x for range(1,1000)if x%3 == 0 or x%5 == 0])


$ b

Ruby:

 (1 .. 999)。 map {| x | x如果x%3 == 0 || x%5 == 0}。紧凑。注入(:+)

他们都给出相同的答案。






好吧,所以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)。选择{| x | x%3 == 0 || x%5 == 0}。注入(:+)

 (1..999)。选择{| x | x%3 == 0或x%5 == 0}。减少(:+)

我假设不像 map select 不会产生'nul',因此不需要调用 compact



Haskell也可以:

  letƒn = ƒ3 +ƒ5  - ƒ15 

中的sum [0,n..999]在f 3 + f 5 - f(lcm 3)中更清楚:

  letƒn = sum [0,n .. 999] 5)

作为让我们自己提供这两个数字的函数:

 ƒ::(Integral a)=> a  - > a  - >在$ x $ y  -  f(lcm xy)
ƒxy = letƒn = sum [0,n..999] >

解决方案

对于Haskell我喜欢

 在s 3 + s 5  -  s 15 



<$>中设置sn = sum [0,n..999]或者

$ $ p $ $ code $ sum $ filter((> 1)。(gcd 15))[0..999]



  import Data.Bits 

sum $ zipWith(*)[1..999] $ zipWith(。|。)(cycle [0,0,1])(cycle [0,0,0,0,1])$ ​​b $ b






<好的,解释时间。

第一个版本定义了一个小函数,它将n的所有倍数总和为999.如果我们将所有3的倍数和5的所有倍数相加,倍数15次两次(每次列表中有一次),因此我们需要一次减去它们。第二个版本使用3和5为素数的事实。如果一个数字包含因素3和5中的一个或两个,则该数字的gcd和15将是3,5或15,因此在任何情况下gcd都将大于1。对于没有公共因子的其他数字,gcd变为1.这是在一个步骤中测试两种条件的好方法。但要小心,它不适用于任意数字,例如当我们有4和9时,测试 gdc x 36> 1 将不起作用,因为 gcd 6 36 == 6 ,但既不 mod 6 4 == 0 也不是 mod 6 9 == 0



第三个版本非常有趣。 循环一遍又一遍地重复列表。 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来说,这是非常重要的。你需要发现模式,选择技巧和成语,并且玩得很多,以便获得灵活性以有效地使用这种语言。像欧拉问题这样的挑战是一个很好的机会。

I have started looking at the project Euler site as a way to learn Haskell, and improve my Python and Ruby. I think the Haskell and Python versions are ok, but I'm sure there must be a cleaner way for Ruby.

This is not about how can I make one language look like another one.

This is 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(:+)

They all give the same answer.


OK, so Python can become:

sum ( x for x in range(1,1000) if x % 3 == 0 or x % 5 == 0 )

it is now a generator (a good thing as we are not storing the list)

but even more fun is:

sum( set(range(0,1000,3)) | set(range(0,1000,5)) )

For some reason I was looking at this again and tried a summation approach which should be constant time. In 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 can become:

(1..999) . select {|x| x % 3 == 0 || x % 5 == 0} . inject(:+)

or

(1..999) . select {|x| x % 3 == 0 or x % 5 == 0} . reduce(:+)

I am presuming as unlike map, select doesn't produce 'nul' and therefore there is no need to call compact. nice.

Haskell can also be:

let ƒ n = sum [0,n..999] in ƒ 3 + ƒ 5 - ƒ 15

or to be clearer:

let ƒ n = sum [ 0 , n .. 999 ] in ƒ 3 + ƒ 5 - ƒ (lcm 3 5)

as a function that lets us provide the two numbers ourselves:

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

解决方案

For Haskell I like

let s n = sum [0,n..999] in s 3 + s 5 - s 15

or

sum $ filter ((>1).(gcd 15)) [0..999]

For fun the Rube-Goldberg version:

import Data.Bits

sum $ zipWith (*) [1..999] $ zipWith (.|.) (cycle [0,0,1]) (cycle [0,0,0,0,1])


Okay, explanation time.

The first version defines a little function s that sums up all multiples of n up to 999. If we sum all multiples of 3 and all multiples of 5, we included all multiples of 15 twice (once in every list), hence we need to subtract them one time.

The second version uses the fact that 3 and 5 are primes. If a number contains one or both of the factors 3 and 5, the gcd of this number and 15 will be 3, 5 or 15, so in every case the gcd will be bigger than one. For other numbers without a common factor with 15 the gcd becomes 1. This is a nice trick to test both conditions in one step. But be careful, it won't work for arbitrary numbers, e.g. when we had 4 and 9, the test gdc x 36 > 1 won't work, as gcd 6 36 == 6, but neither mod 6 4 == 0 nor mod 6 9 == 0.

The third version is quite funny. cycle repeats a list over and over. cycle [0,0,1] codes the "divisibility pattern" for 3, and cycle [0,0,0,0,1] does the same for 5. Then we "or" both lists together using zipWith, which gives us [0,0,1,0,1,1,0,0,1,1,0,1...]. Now we use zipWith again to multiply this with the actual numbers, resulting in [0,0,3,0,5,6,0,0,9,10,0,12...]. Then we just add it up.

Knowing different ways to do the same thing might be wasteful for other languages, but for Haskell it is essential. You need to spot patterns, pick up tricks and idioms, and play around a lot in order to gain the mental flexibility to use this language effectively. Challenges like the project Euler problems are a good opportunity to do so.

这篇关于在Haskell,Python和Ruby中列出理解的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆