"break" vs "raise StopIteration" in a Ruby Enumerator - ruby

If I use Ruby Enumerators to implement a generator and a filter:
generator = Enumerator.new do |y|
x = 0
loop do
y << x
x += 1
break if x > CUTOFF
end
end.lazy
filter = Enumerator.new do |y|
loop do
i = generator.next
y << i if i.even?
end
end
does it make any difference whether I break out of the generator's loop using
break if x > CUTOFF
vs
raise StopIteration if x > CUTOFF
?
Both seem to work. I imagine break would be more performant, but is raise more idiomatic here?

In Ruby it's considered a bad practice to use raise/fail for control flow as they are really slow.
So to answer your question raise StopIteration if x > CUTOFF isn't an idiomatic way of breaking from the loop

Related

Palindrome product

I'm trying to find the largest product of 2 three-digit numbers that is a palindrome. The answer is 993 * 913 = 906609.
This is my code:
x = 123
until x == x.to_s.reverse.to_i
999.downto(100) do |i|
i.downto(100) do |j|
x = i * j
puts "#{i} * #{j} = #{x}"
end
end
end
puts x
I wrapped everything in an until statement that is supposed to check its palindrome-ness, but my code keeps going on even after it hits the correct value. Does anyone see what I am doing incorrectly here?
Even if your code worked, it would pick 995 * 585 = 580085 as there is no logic to pick the palindrome with highest value.
So, you may want to collect all palindromes in an array and then find max from that array as shown below:
arr = []
999.downto(100) do |i|
i.downto(100) do |j|
x = i * j
arr << x if x.to_s == x.to_s.reverse
end
end
p arr.max
#=> 906609
Try and think about what you are telling your code to do in plain english:
"Until x is a palindrome, do this doubly nested loop to completion"
The until loop is never breaking because it is FULLY running both loops BEFORE checking if x is a palindrome. The solution would be to instead break when you find a palindrome Try this:
999.downto(100) do |i|
i.downto(100) do |j|
x = i * j
break if x == x.to_s.reverse.to_i
puts "#{i} * #{j} = #{x}"
end
break if x == x.to_s.reverse.to_i
end
puts x
Ah but now we have arrived at another problem - looping in this way does not guarantee that the product will be the highest product. We can modify slightly to achieve this:
palindromes = []
999.downto(100) do |i|
i.downto(100) do |j|
x = i * j
palindromes << x if x == x.to_s.reverse.to_i
end
end
puts palindromes.max
This probably isn't the best solution, but it works.
The problem is that the condition is never being checked, as the inner loops run until they finish without returning the execution to the until loop
You could to it this way
x = 123
999.downto(100) do |i|
i.downto(100) do |j|
x = i * j
puts "#{i} * #{j} = #{x}"
break if x == x.to_s.reverse.to_i
end
end
puts x
Anyway this (as your initial solution if it worked) will not give you the biggest palindrome, but the first one it finds.
The outer until loop is controlled by the condition x == x.to_s.reverse.to_i, but the inner two downto loops are not controlled by that condition. When the condition is satisfied in the middle of the two downto loops, that does not have any effect in stopping in the middle of the two downto loops, it only stops the until loop from continuing to the next iteration.
As pointed out by Wand Maker, it also does not give the correct result even if it worked. The problem is with the order of iteration. You are decrementing i and j in the order such as:
...
..., [i, j], [i, j - 1], ...,
..., [i - 1, j], [i -1, j - 1], ...,
...
Under this order, you are assuming for example that i * (j - 1) is greater than (i - 1) * j, and hence if they both satisfy the condition, the earlier among them, i * (j - 1), should be picked as the greater number, stopping the iteration to go on to the latter. But that assumption about the ordering is wrong.

Ruby add / sum variable

I'm looking for an explanation as to why this works:
def somesum
x = 0
(1..10).each do |number|
x += number
end
puts x
end
whereas this does not work (undefined method '+' for nil class):
def somesum
(1..10).each do |number|
x += number
end
puts x
end
neither does this:
def somesum
(1..10).each do |number, x|
x += number
end
puts x
end
The difference why your first example works, is because you initialized x:
x = 0
whereas the last two example try to add 1 to nil in the first loop, which doesn't work, since nil has no method '+'.
Please consider, that your example is a bad implementation achieving the goal.
A better solution is to use the accumulator approach. For example Enumerable#inject:
(1..10).inject(0){ |sum, number| sum += number }
or even better:
(1..10).inject(:+)
which accumulates implicitly.
Enumerable#reduce works the same:
(1..10).reduce(:+)
For a better understanding, please go reading Iterators: inject and reject
In the second and third examples x has no value. Remember that:
x += number
is a shortening for:
x = x + number
so you're doing:
x = nil + number
and nil has no :+ method.

Why does this Ruby code not return the expected answer?

I'm trying the list all factors of a number using the following method:
def find_factors(n)
factors = []
2.upto(n-1) {|x| factors << x if n % x == 0}
end
factor = find_factors(24)
puts factor
Its printing out the following:
2
instead of the list of factors! What am if doing incorrectly?
upto used with a block returns the receiver, which is 2.
A better way to write this would be:
def find_factors(n)
2.upto(n-1).select{|x| (n % x).zero?}
end
In ruby, whenever you see an array initialization before a loop, you can generally convert it to a more functional approach:
def find_factors(n)
2.upto(n-1).select{|x| n % x == 0}
end
This tends to be more compact and often more readable too.
You have to return factors at the end of your find_factors method:
def find_factors(n)
factors = []
2.upto(n-1) {|x| factors << x if n % x == 0}
factors
end

Syntax Error in Ruby, Unexpected Pipe Character in a Do

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
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.
while does not take a block. Remove the do |x| part.
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.
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

More ruby-like solution to this problem?

I am learning ruby and practicing it by solving problems from Project Euler.
This is my solution for problem 12.
# Project Euler problem: 12
# What is the value of the first triangle number to have over five hundred divisors?
require 'prime'
triangle_number = ->(num){ (num *(num + 1)) / 2 }
factor_count = ->(num) do
prime_fac = Prime.prime_division(num)
exponents = prime_fac.collect { |item| item.last + 1 }
fac_count = exponents.inject(:*)
end
n = 2
loop do
tn = triangle_number.(n)
if factor_count.(tn) >= 500
puts tn
break
end
n += 1
end
Any improvements that can be made to this piece of code?
As others have stated, Rubyists will use methods or blocks way more than lambdas.
Ruby's Enumerable is a very powerful mixin, so I feel it pays here to build an enumerable in a similar way as Prime. So:
require 'prime'
class Triangular
class << self
include Enumerable
def each
sum = 0
1.upto(Float::INFINITY) do |i|
yield sum += i
end
end
end
end
This is very versatile. Just checking it works:
Triangular.first(4) # => [1, 3, 7, 10]
Good. Now you can use it to solve your problem:
def factor_count(num)
prime_fac = Prime.prime_division(num)
exponents = prime_fac.collect { |item| item.last + 1 }
exponents.inject(1, :*)
end
Triangular.find{|t| factor_count(t) >= 500} # => 76576500
Notes:
Float::INFINITY is new to 1.9.2. Either use 1.0/0, require 'backports' or do a loop if using an earlier version.
The each could be improved by first checking that a block is passed; you'll often see things like:
def each
return to_enum __method__ unless block_given?
# ...
Rather than solve the problem in one go, looking at the individual parts of the problem might help you understand ruby a bit better.
The first part is finding out what the triangle number would be. Since this uses sequence of natural numbers, you can represent this using a range in ruby. Here's an example:
(1..10).to_a => [1,2,3,4,5,6,7,8,9,10]
An array in ruby is considered an enumerable, and ruby provides lots of ways to enumerate over data. Using this notion you can iterate over this array using the each method and pass a block that sums the numbers.
sum = 0
(1..10).each do |x|
sum += x
end
sum => 55
This can also be done using another enumerable method known as inject that will pass what is returned from the previous element to the current element. Using this, you can get the sum in one line. In this example I use 1.upto(10), which will functionally work the same as (1..10).
1.upto(10).inject(0) {|sum, x| sum + x} => 55
Stepping through this, the first time this is called, sum = 0, x = 1, so (sum + x) = 1. Then it passes this to the next element and so sum = 1, x = 2, (sum + x) = 3. Next sum = 3, x = 3, (sum + x) = 6. sum = 6, x = 4, (sum + x) = 10. Etc etc.
That's just the first step of this problem. If you want to learn the language in this way, you should approach each part of the problem and learn what is appropriate to learn for that part, rather than tackling the entire problem.
REFACTORED SOLUTION (though not efficient at all)
def factors(n)
(1..n).select{|x| n % x == 0}
end
def triangle(n)
(n * (n + 1)) / 2
end
n = 2
until factors(triangle(n)).size >= 500
puts n
n += 1
end
puts triangle(n)
It looks like you are coming from writing Ocaml, or another functional language. In Ruby, you would want to use more def to define your methods. Ruby is about staying clean. But that might also be a personal preference.
And rather than a loop do you could while (faction_count(traingle_number(n)) < 500) do but for some that might be too much for one line.

Resources