List comprehension in Haskell, Python and Ruby

前端 未结 5 950
天命终不由人
天命终不由人 2021-01-01 21:26

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 the

相关标签:
5条回答
  • 2021-01-01 21:45

    Not a list comprehension, I know, but to solve that I would use:

    3*((999/3)**2+999/3)/2+5*((999/5)**2+999/5)/2-15*((999/15)**2+999/15)/2
    

    Faster then any list comprehension one might come up with, and works in any language ;)

    Only posting to show another way of looking at the same problem using http://en.wikipedia.org/wiki/Summation.

    0 讨论(0)
  • 2021-01-01 21:45

    Try something like this:

    (1...1000).inject(0) do |sum, i|
      if (i % 3 == 0) or (i % 5 == 0)
        sum + i
      else
        sum
      end
    
    0 讨论(0)
  • 2021-01-01 21:47

    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.

    0 讨论(0)
  • 2021-01-01 21:54

    Try this for Ruby:

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

    Or a little different approach:

    (1..999).reduce(0) {|m, x| (x % 3 == 0 or x % 5 == 0) ? m+x : m }
    
    0 讨论(0)
  • 2021-01-01 22:09

    I think the following is a better Ruby one:

    (1..999).select{|x| x % 3 == 0 || x % 5 == 0}.reduce(:+)
    
    0 讨论(0)
提交回复
热议问题