Ruby undefined method `[]' for nil:NilClass Loop in a loop - ruby

Basically I'm looping through a matrix of arrays adding up the columns, but it stops right before the 2nd iteration of the 9.times loop. If I change y = 1 it will perform the action and stop at y = 2 and so on.
def new_feature(board)
x=0
y=0
vertical = []
while y < 9
9.times do
vertical << board[x][y]
x += 1
end
puts vertical.reduce(:+)
vertical = []
y += 1
end
end

You never reset x back to 0, so in the second while iteration your x starts with 9 where it finished last time, not 0. This makes board[9], which is presumably out of bounds and thus nil; and then nil[1] crashes your code.
Note that you can write 9.times do |x| ... end to have x count from 0 to 8 without any manual counting, since times passes the current iteration number into the block.
Also, a more Rubyish way to sum columns:
board.transpose.map { |row| row.reduce(&:+) }

Related

I don't understand this method

I'm a beginner in Ruby and I don't understand what this code is doing, could you explain it to me, please?
def a(n)
s = 0
for i in 0..n-1
s += i
end
s
end
def defines a method. Methods can be used to run the same code on different values. For example, lets say you wanted to get the square of a number:
def square(n)
n * n
end
Now I can do that with different values and I don't have to repeat n * n:
square(1) # => 1
square(2) # => 4
square(3) # => 9
= is an assignment.
s = 0 basically says, behind the name s, there is now a zero.
0..n-1 - constructs a range that holds all numbers between 0 and n - 1. For example:
puts (0..3).to_a
# 0
# 1
# 2
# 3
for assigns i each consecutive value of the range. It loops through all values. So first i is 0, then 1, then ... n - 1.
s += i is a shorthand for s = s + i. In other words, increments the existing value of s by i on each iteration.
The s at the end just says that the method (remember the thing we opened with def) will give you back the value of s. In other words - the sum we accumulated so far.
There is your programming lesson in 5 minutes.
This example isn't idiomatic Ruby code even if it is syntactically valid. Ruby hardly ever uses the for construct, iterators are more flexible. This might seem strange if you come from another language background where for is the backbone of many programs.
In any case, the program breaks down to this:
# Define a method called a which takes an argument n
def a(n)
# Assign 0 to the local variable s
s = 0
# For each value i in the range 0 through n minus one...
for i in 0..n-1
# ...add that value to s.
s += i
end
# The result of this method is s, the sum of those values.
s
end
The more Ruby way of expressing this is to use times:
def a(n)
s = 0
# Repeat this block n times, and in each iteration i will represent
# a value in the range 0 to n-1 in order.
n.times do |i|
s += i
end
s
end
That's just addressing the for issue. Already the code is more readable, mind you, where it's n.times do something. The do ... end block represents a chunk of code that's used for each iteration. Ruby blocks might be a little bewildering at first but understanding them is absolutely essential to being effective in Ruby.
Taking this one step further:
def a(n)
# For each element i in the range 0 to n-1...
(0..n-1).reduce |sum, i|
# ...add i to the sum and use that as the sum in the next round.
sum + i
end
end
The reduce method is one of the simple tools in Ruby that's quite potent if used effectively. It allows you to quickly spin through lists of things and compact them down to a single value, hence the name. It's also known as inject which is just an alias for the same thing.
You can also use short-hand for this:
def a(n)
# For each element in the range 0 to n-1, combine them with +
# and return that as the result of this method.
(0..n-1).reduce(&:+)
end
Where here &:+ is shorthand for { |a,b| a + b }, just as &:x would be short for { |a,b| a.x(b) }.
As you are a beginner in Ruby, let's start from the small slices.
0..n-1 => [0, n-1]. E.g. 0..3 => 0, 1, 2, 3 => [0, 3]
for i in 0.. n-1 => this is a for loop. i traverses [0, n-1].
s += i is same as s = s + i
So. Method a(n) initializes s = 0 then in the for loop i traverse [0, n - 1] and s = s + i
At the end of this method there is an s. Ruby omits key words return. so you can see it as return s
def a(n)
s = 0
for i in 0..n-1
s += i
end
s
end
is same as
def a(n)
s = 0
for i in 0..n-1
s = s + i
end
return s
end
a(4) = 0 + 1 + 2 + 3 = 6
Hope this is helpful.
The method a(n) calculates the sums of the first n natural numbers.
Example:
when n=4, then s = 0+1+2+3 = 6
Let's go symbol by symbol!
def a(n)
This is the start of a function definition, and you're defining the function a that takes a single parameter, n - all typical software stuff. Notably, you can define a function on other things, too:
foo = "foo"
def foo.bar
"bar"
end
foo.bar() # "bar"
"foo".bar # NoMethodError
Next line:
s = 0
In this line, you're both declaring the variable s, and setting it's initial value to 0. Also typical programming stuff.
Notably, the value of the entire expression; s = 0, is the value of s after the assignment:
s = 0
r = t = s += 1 # You can think: r = (t = (s += 1) )
# r and t are now 1
Next line:
for i in 0..n-1
This is starting a loop; specifically a for ... in ... loop. This one a little harder to unpack, but the entire statement is basically: "for each integer between 0 and n-1, assign that number to i and then do something". In fact, in Ruby, another way to write this line is:
(0..n-1).each do |i|
This line and your line are exactly the same.
For single line loops, you can use { and } instead of do and end:
(0..n-1).each{|i| s += i }
This line and your for loop are exactly the same.
(0..n-1) is a range. Ranges are super fun! You can use a lot of things to make up a range, particularly, time:
(Time.now..Time.new(2017, 1, 1)) # Now, until Jan 1st in 2017
You can also change the "step size", so that instead of every integer, it's, say, every 1/10:
(0..5).step(0.1).to_a # [0.0, 0.1, 0.2, ...]
Also, you can make the range exclude the last value:
(0..5).to_a # [0, 1, 2, 3, 4, 5]
(0...5).to_a # [0, 1, 2, 3, 4]
Next line!
s += i
Usually read aloud a "plus-equals". It's literally the same as: s = s + 1. AFAIK, almost every operator in Ruby can be paired up this way:
s = 5
s -= 2 # 3
s *= 4 # 12
s /= 2 # 6
s %= 4 # 2
# etc
Final lines (we'll take these as a group):
end
s
end
The "blocks" (groups of code) that are started by def and for need to be ended, that's what you're doing here.
But also!
Everything in Ruby has a value. Every expression has a value (including assignment, as you saw with line 2), and every block of code. The default value of a block is the value of the last expression in that block.
For your function, the last expression is simply s, and so the value of the expression is the value of s, after all is said and done. This is literally the same as:
return s
end
For the loop, it's weirder - it ends up being the evaluated range.
This example may make it clearer:
n = 5
s = 0
x = for i in (0..n-1)
s += i
end
# x is (0..4)
To recap, another way to write you function is:
def a(n)
s = 0
(0..n-1).each{ |i| s = s + i }
return s
end
Questions?

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.

Syntax error? Ruby bug? Issue with instance variable incrementing with += 1?

def isPrime?(num)
i = 2
#isFalse = 0
while i < num
divisible = ((num % i) == 0)
if divisible
#isFalse += 1
return false
end
i += 1
end
true
end
def primes(max)
startTime = Time.now
(2..max).each do |p|
puts "#{p} #{isPrime?(p)}"
end
endTime = Time.now
puts "--------------------------------------------------"
puts " FALSE values in range from (2 thru #{max}): #{#isFalse} \n TRUE values in range from (2 thru #{max}): #{max-1-#isFalse}"
puts "\nTotal time to calculate is #{endTime - startTime} seconds!"
end
primes(10)
isPrime? checks if a given number is a prime number.
primes loads a range of numbers and checks if each is a prime.
I want to know how many numbers are prime within the range and how many aren't.
I added #isFalse += 1 thinking I can increment each time false is returned so that I can determine how many numbers in the range are false and use this to subtract from max to get how many numbers are true.
Entire code is working correctly except #isFalse is not properly incrementing. Why is that? Thanks for the help.
--UPDATE--
My Output: added puts "About to increment #isFalse" before #isFalse += 1
2 true
3 true
About to increment #isFalse
4 false
5 true
About to increment #isFalse
6 false
7 true
About to increment #isFalse
8 false
About to increment #isFalse
9 false
About to increment #isFalse
10 false
--------------------------------------------------
FALSE values in range from (2 thru 10): 1
TRUE values in range from (2 thru 10): 8
Each time isPrime? is called, #isFalse is reset to 0. So the result of #isFalse being 1 is from the last time isPrime? was called (with num equal to 10, incrementing #isFalse to 1).
It seems that you are trying to find out the number of primes in that range using #isFalse. If that is the case, you should instead use the following modification of your code (although, I do not recommend checking for primes in this way, and the code is really inefficient):
Basically, I removed the instance variable #isFalse altogether, and checked whether a number is prime or not in your second method, instead. The code is much cleaner that way, and really does what you intend it to do.
The problem with your code is that #isFalse is being reset to 0 everytime your first method isPrime? was being called, and hence, does not properly reflect the number of primes in the given range.
def isPrime?(num)
i = 2
while i < num
divisible = ((num % i) == 0)
return false if divisible
i += 1
end
true
end
def primes(max)
startTime = Time.now
is_prime = 0
(2..max).each do |p|
if isPrime?(p)
is_prime += 1
puts p
end
endTime = Time.now
puts "--------------------------------------------------"
puts " FALSE values in range from (2 thru #{max}): #{is_prime} \n TRUE values in range from (2 thru #{max}): #{max-1-is_prime}"
puts "\nTotal time to calculate is #{endTime - startTime} seconds!"
end
primes(10)
As your question has been answered, I would like to suggest a more Ruby-like solution:
You want the method is_prime? to do nothing other than determine if num is prime:
def is_prime?(num)
(2...num).each {|i| return false if num % i == 0}
true
end
Do your counting of primes in the method nbr_primes.
def nbr_primes(max)
return false if max < 2
(2..max).reduce(0) {|tot, i| tot + (is_prime?(i) ? 1 : 0)}
end
p nbr_primes(20) # => 8
A few points:
I removed the references to time and the output formatting as they are not central to the question.
It's a Ruby convention to name methods with lower case letters and underscores.
(2...num), because it has three dots, is the sequence from 2 to (and including) num - 1. We could instead write (2..num-1) (two dots), which is favored by some.
(2..max).reduce(0) {|tot, i|...} iterates over i from 2 to and including max. tot, which is reduce's accumulator, is incremented by 1 for each number between 2 and max that is found to be prime. The expression returns the value of tot, which in turn is returned by the method nbr_primes. inject is a synonym for reduce.
In checking whether a number n is prime, you only have to check if its divisible by numbers up to Math.sqrt(n).to_i.
With require 'prime', you don't have to reinvent the wheel. For example, Prime.prime?(7) #=> true, and Prime.take_while {|p| p <= 10}.size #=> [2, 3, 5, 7].size + 1 # => 4.
#isFalse = 0 is inside your isPrime? method. Get it out of there!

IF condition is met, start loop at beginning (Ruby)

Using ruby 1.9.3, I need to do the following:
test_ids.each do |x|
if x == 1
y = x+1
elsif x == 2
y = x+2
elsif x == 3
(end loop here, start over with next test_id)
else
y = x+4
end
end
I just need the loop to continue with the next object in the array if a condition is met instead of continuing throughout the script. How is this possible?
EDIT:
The end goal is something like this:
case x
when 1
abort
when 2
...
else
...
end
puts x+2
So as you can see, x is being used after the if case statement. I need to not run the puts statement if x = 1. I don't want to abort the script and terminate it altogether, I just want to skip that array object and go to the next one. Any suggestions?
= assigns a value to x so your code only executes x = 1 inside the loop.
Instead you need a comparison using == in your if statements.
You could have avoided this using a case statement:
case x
when 1
...
when 2
...
when 3
...
else
...
end
In my experience using case tends to clarify code compared to multiple if/else/elsif.
You are looking for the next command.
test_ids.each do |x|
if x == 1
y = x+1
elsif x == 2
y = x+2
elsif x == 3
next
else
y = x+4
end
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

Resources