views:

63

answers:

4

I'll try to be concise this time around! I'm still working Project Euler, this time back to #2. My real issue here is I'm terrible with Ruby. When I run the following code

x = 1
y = 2
sum = 2
while x >= 4_000_000 do |x|

  sum += y if y % 2 == 0

  z = x + y

  x = x ^ y   # xor magic
  y = x ^ y   # xor magic
  x = x ^ y   # xor magic

  y = z 
end

p sum

My interpreter kicks out the following output:

/Users/Andy/Documents/Programming/Ruby/ProjectEuler/P2.rb:4: syntax error, unexpected '|'
while x >= 4_000_000 do |x|
                         ^

I'm reading why's (Poignant) Guide to Ruby, and I'm pretty sure I have the pipe syntax correct for the Do. Could someone point out what I'm doing wrong here? I've tried messing around in a lot of different ways and am coming up short handed

+6  A: 
while (x >= 4_000_000)
    foo
end

You don't even have to pass in x, because it's accessible in the scope of the enclosing block.

Borealid
+6  A: 

while does not take a block. Remove the do |x| part.

icktoofay
+4  A: 

while is not a method that takes a block, it is a ruby looping statement. It considers the part between the while and do (or newline) to be the logical test and the part between the do (or newline) and end keyword to be the loop body.

while x < 10 do x += 1; puts x; end
while x < 10
  x += 1
  puts x
end

Contrast this with something like the Array's each method which takes in a block. Here the each method calls your block for each element of the array (passed into the block as x)

[1,2,3].each do |x|  
  puts x
end

You accidentally combined the two, asking the while loop to call your code block with the loop counter to be passed in as x. That is not how while works... hence the parsing exception.

Gishu
+2  A: 

What an interesting question! It inspired me to take a shot at the problem, too. Here's my solution.

First, some preparatory work:

class Enumerator
  def lazy_select
    Enumerator.new do |y|
      each do |el|
        y.yield(el) if yield el
      end
    end
  end

  alias_method :lazy_find_all, :lazy_select
end

module Enumerable
  def sum
    reduce(:+)
  end
end

module Math
  ROOT5 = Math.sqrt(5)
  PHI = 0.5 + ROOT5/2

  def self.fibonacci(n)
    Integer(0.5 + PHI**n/ROOT5)
  end
end

class Integer
  def fibonacci
    Math.fibonacci(self)
  end
end

Now an Enumerator which generates an infinite sequence of Fibonacci Numbers:

fibs = Enumerator.new do |y|
  n = -1
  loop do
    y.yield (n += 1).fibonacci
  end
end

And the nice thing is that we can now directly express the original problem statement in code:

Find the sum of all the even-valued terms in the sequence which do not exceed four million.

puts fibs.lazy_find_all(&:even?).take_while {|n| n <= 4_000_000 }.sum

I think that this is a much more Rubyish way to solve the problem. You write in your question that you are terrible with Ruby. But that's not actually the problem. The real problem is that you are good with C! In other words, the real problem is that you simply aren't writing Ruby, you are writing C with Ruby syntax.

Two good examples are:

y % 2 == 0

and

x = x ^ y
y = x ^ y
x = x ^ y

The Ruby way to write these would be

y.even?

and

x, y = y, x
Jörg W Mittag
Note: I *deliberately* didn't put any comments or explanations in the code. Feel free to ask me questions, but try to figure it out for yourself first. I won't be near a computer for the next 12 hours or so, but I'll definitely get back to you.
Jörg W Mittag
Also, since this doesn't actually answer your question, I made it Community Wiki so that I don't gain any reputation from it. And as a nice side benefit, this means that it's much easier for others or yourself to edit it and incorporate answers, questions and ideas.
Jörg W Mittag
Okay, so I've reduced it down to<code> x = 1 y = 2 sum = 2 while x < 4_000_000 sum += x if x.even? x, y = y, x+y end p sum</code>And it's yielding the incorrect answer. Is this a syntax issue or an ID10T issue?
Andrew
Sorry, I can't figure out how to format code in the comments section. :(
Andrew
@Andrew: your solution, i.e. your `sum` is exactly 2 too big. Can you figure out why? (Oh, and the comment boxes accept a subset of the markdown format that the questions and answers provide. You can format code the same way you would in an answer: by enclosing it in \`backticks\` )
Jörg W Mittag
@Jörg: I really like your comment about me and C, by the way. I guess it's obvious I learned on perl before moving quickly to C? I'm really going to need some more time with the documentation and a good tutorial, because I don't understand much of your code at all. In fact, it'd be easier to quantify what I DO understand. ;)
Andrew