Ruby undefined method `each' - ruby

i can't figure out what's wrong with my code, can you help me please?
this is constructor of my class:
def initialize(hash_table_size)
#size = hash_table_size
#table = Array.new(hash_table_size) { LinkedList.new }
end
this is method in that class:
def to_a
arr = Array.new
#table.each { |list| list.each { |o| arr << o } }
arr
end
this is my "each" method in LinkedList class:
def each
if #length > 0
item = #head
begin
yield item.object
item = item.next
end until item.nil?
end
end
and this is what i get from unittest:
1) Error:
test_initial_size_3(HashSetTest):
NoMethodError: undefined method `each' for 3:Fixnum
C:/Users/Ajax/My Documents/Aptana Studio 3 Workspace/alg_du1_jan_svec/hash_set.rb:34:in `block in to_a'
C:/Users/Ajax/My Documents/Aptana Studio 3 Workspace/alg_du1_jan_svec/hash_set.rb:34:in `each'
C:/Users/Ajax/My Documents/Aptana Studio 3 Workspace/alg_du1_jan_svec/hash_set.rb:34:in `to_a'
C:/Users/Ajax/My Documents/Aptana Studio 3 Workspace/alg_du1_jan_svec/hash_set_test.rb:14:in `test_initial_size_3'
1 tests, 3 assertions, 0 failures, 1 errors, 0 skips

It means that LinkedList.new in the method initialize is returning 3, which becomes an element of #table, and is substituted into the block variable list of the method to_a.

Related

Instance variables in Ruby; why do I have to (sometimes) specify `self`?

Why do increment_v1 and_v3 work, but increment_v2 and _v4 don't (v2 returns the correct value, but doesn't change the #counter, v4 fails with "NoMethodError (undefined method `+' for nil:NilClass)")
class MyClass
attr_accessor :counter
def initialize
#counter = 0
end
def increment_v1
#counter = counter + 1
end
def increment_v2
counter = #counter + 1
end
def increment_v3
#counter += 1
end
def increment_v4
counter += 1
end
end
I expect all of these methods to have the same outcome (increase the #counter value and return the increased number). It has the same error if I replace attr_accessor with attr_reader and attr_writer. I feel like I may be misunderstanding something about the attr_* methods.
Here is what it looks like in the console:
2.6.3 :026 > a = MyClass.new
=> #<MyClass:0x00000000018d7240 #counter=0>
2.6.3 :027 > a.increment_v1
=> 1
2.6.3 :028 > a
=> #<MyClass:0x00000000018d7240 #counter=1>
2.6.3 :029 > a.increment_v2
=> 2
2.6.3 :030 > a
=> #<MyClass:0x00000000018d7240 #counter=1>
2.6.3 :031 > a.increment_v3
=> 2
2.6.3 :032 > a
=> #<MyClass:0x00000000018d7240 #counter=2>
2.6.3 :033 > a.increment_v4
Traceback (most recent call last):
5: from /home/guin/.rvm/rubies/ruby-2.6.3/bin/irb:23:in `<main>'
4: from /home/guin/.rvm/rubies/ruby-2.6.3/bin/irb:23:in `load'
3: from /home/guin/.rvm/rubies/ruby-2.6.3/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
2: from (irb):33
1: from (irb):23:in `increment_v4'
NoMethodError (undefined method `+' for nil:NilClass)
Running a.counter += 1 from outside the class works as I expect. Do I have to specify self.counter += 1 when I am inside the class? Why? It even works if with self.counter = counter + 1. What is going on?
As you have shown, you can always access an attribute directly using the instance variable (#counter). However your issue here is relating to the getter/setter methods generated by attr_accessor.
The getter method does not require self unless you have a local variable with the same name. Setter methods are different. You always need to use self with setters.
For example:
def test_method
# directly set instance var. this will always work
#counter = 1
# define local variable with same name.
# this does not call the setter because you don't use self
counter = 0
puts counter
# prints 0
# The getter method is never called because you have a local variable
# with the same name.
puts self.counter
# prints 1
# you can force the getter to be called by using self
end
I think the idiomatic way to write your method would be:
def increment_v5
self.counter += 1
end
However you could also write it like this:
def increment_v6
self.counter = counter + 1
# \ calls getter
end
and there are many other ways to write it.

A simple Calculator DSL in Ruby

I'm implementing a Calculator DSL in Ruby. The code is given below. It gives me an error stating that '+' in total = total + number is not defined. What could be the error? Also, could there be any problem in the initialize function that is causing it?
class Calculator
attr_accessor :total
def initialize(&block)
self.total = 0
instance_eval(&block)
return total
end
def add(number)
total = total + number
end
def subtract(number)
total -= number
end
def multiply(number)
total *= number
end
def divide(number)
total /= number
end
end
h = Calculator.new do
add 3
add 5
end
The error message is -
calculator_dsl.rb:10:in `add': undefined method `+' for nil:NilClass (NoMethodError)
from calculator_dsl.rb:27:in `block in <main>'
from calculator_dsl.rb:5:in `instance_eval'
from calculator_dsl.rb:5:in `initialize'
from calculator_dsl.rb:26:in `new'
from calculator_dsl.rb:26:in `<main>'
Short answer: name clash (local variable vs method)
Long answer:
def add(number)
puts defined?(total)
total = (puts defined?(total); total + number)
end
This code outputs
method
local-variable
NoMethodError: undefined method `+' for nil:NilClass
Right before this line
total = total + number
a new local variable total is created which shadows the method from outer scope. It is also set to nil which explains the error you're getting.
To avoid creation of new local var, use self
self.total = total + number
# or
self.total += number

Ruby - How to populate a 2d array?

I have this rspec test:
it 'has a populated chessboard' do
expect(ChessBoard.new.populate_new_board).to eq [
['pawn','pawn','pawn','pawn','pawn','pawn','pawn','pawn'],
['pawn','pawn','pawn','pawn','pawn','pawn','pawn','pawn'],
['pawn','pawn','pawn','pawn','pawn','pawn','pawn','pawn'],
['pawn','pawn','pawn','pawn','pawn','pawn','pawn','pawn'],
['pawn','pawn','pawn','pawn','pawn','pawn','pawn','pawn'],
['pawn','pawn','pawn','pawn','pawn','pawn','pawn','pawn'],
['pawn','pawn','pawn','pawn','pawn','pawn','pawn','pawn'],
['pawn','pawn','pawn','pawn','pawn','pawn','pawn','pawn']]
end
For this code:
class ChessBoard
def initialize
#board=Array.new(7){Array.new(7)}
end
def populate_new_board
(0..7).each do |row|
(0..7).each do |cell|
#board[row][cell]='pawn'
end
end
#board
end
end
but I'm getting:
1) least number of moves from x to y has a populated chessboard
Failure/Error: expect(ChessBoard.new.populate_new_board).to eq [
NoMethodError:
undefined method `[]=' for nil:NilClass
# ./code.rb:10:in `block (2 levels) in populate_new_board'
# ./code.rb:9:in `each'
# ./code.rb:9:in `block in populate_new_board'
# ./code.rb:8:in `each'
# ./code.rb:8:in `populate_new_board'
# ./code_spec.rb:12:in `block (2 levels) in <top (required)>'
how can I fix this?
btw pawns in every space is not the final result but it's what I want for this test right now (then I can modify it further).
class ChessBoard
def populate_new_board
#board = [['pawn'] * 7] * 7
end
end
Change it to:
def populate_new_board
(0...7).each do |row|
(0...7).each do |cell|
#board[row][cell]='pawn'
end
end
#board
end
As it was denoted that you did a mistake in range, but a strongly advice you to reduce usage of index ranges. You can use :each, and :map methods instead:
class ChessBoard
def initialize
#board = Array.new( 7 ){ Array.new( 7 ) }
end
def populate_new_board
#board.each {| row | row.map! {| _ | 'pawn' } }
end
end
But I'd use more the simple code:
class ChessBoard
def populate_new_board
#board = Array.new( 7 ){Array.new( 7 ) { 'pawn' } }
end
end

Instance variable access in subclasses

I am hoping that someone could shed some light on the error that I am receiving below. I define an instance variable in the parent class Node and want to access and modify it in the subclass AddSubNode, whenever I try to access #code I receive this error:
'code': undefined method `<<' for nil:NilClass (NoMethodError)
I must be misunderstanding Ruby's inheritance model, but I thought that I could do this.
class Node
attr_accessor :code
def initialize
#code = []
end
end
class AddSubNode < Node
def initialize op, l, r
#op = op
#l = l
#r = r
end
def code
#code << 1 # error: `code': undefined method `<<' for nil:NilClass (NoMethodError)
#code
end
def to_s
"#{#l} #{#op} #{#right}"
end
end
You need to call the super initializer in the initializer of the subclass.
class AddSubNode < Node
def initialize op, l, r
super()
#op = op
#l = l
#r = r
end
...
edit: forgot parenthesis
When you redefine the initialize method in the subclass, you overwrite the original. Hence the instance variable #code is never initialized, and you code throws an error when you call #code << 1.
Calling super() from the initialize method in your subclass (effectively calling it's parent) or utilizing #code << 1 unless #code.nil? are a few ways to address the error.
Here I just tried to give you some visualization to test such scenarios.
class Node
attr_accessor :code
def initialize
#code = []
end
end
class AddSubNode < Node
def initialize op, l, r
#op = op
#l = l
#r = r
end
def code
#code << 1 # error: `code': undefined method `<<' for nil:NilClass (NoMethodError)
#code
end
end
ob = AddSubNode.new(1,2,3)
p ob.instance_variables #=> [:#op, :#l, :#r]
p ob.instance_variable_defined?(:#code) #=> false
p ob.instance_variable_set :#code,[12] #=> [12]
p ob.instance_variable_defined?(:#code) #=> true
p ob.instance_variable_get :#code #=> [12]
p ob.instance_variables #=> [:#op, :#l, :#r, :#code]
p ob.code #=> [12, 1]

Recursive lambdas in Ruby

I have the following code which correctly generates all possible trees of size num:
class Tree
attr_accessor :left, :right
def initialize left = nil, right = nil
#left = left
#right = right
end
# Don't ever specify any arguments, it will make me very angry.
# Tilt your head 90 degrees to the side to see the tree when viewing.
def print level = 0
#right.pretty_print(level + 1) if #right
puts (' ' * level) + to_s
#left.pretty_print(level + 1) if #left
end
def self.generate num
trees = []
generate_subtrees(num) { |tree| trees << tree } if num > 0
trees
end
private
def self.generate_subtrees num, &block
if num == 0
yield nil
else
(1..num).each do |root_position|
generate_subtrees(root_position - 1) do |left|
generate_subtrees(num - root_position) do |right|
yield Tree.new nil, left, right
end
end
end
end
end
end
I’m trying to (for the sake of it) “condense” this into one method, utilizing lambda recursion. My current attempt (of several iterations) is below:
def self.generate num
trees = []
gen = ->(num, &block) do
if num == 0
yield nil # L61
else
(1..num).each do |root_position| # L63
gen.call(root_position - 1) do |left| # L64
gen.call(num - root_position) do |right|
block.call { Tree.new nil, left, right }
end
end
end
end
end
gen.call(num) { |tree| trees << tree } # L73
trees
end
This results in the error (referenced lines noted above):
LocalJumpError: no block given (yield)
from tree.rb:61:in `block in generate'
from tree.rb:64:in `call'
from tree.rb:64:in `block (2 levels) in generate'
from tree.rb:63:in `each'
from tree.rb:63:in `block in generate'
from tree.rb:73:in `call'
from tree.rb:73:in `generate'
from (irb):4
from /Users/amarshall/.rbenv/versions/1.9.2-p290/bin/irb:12:in `<main>'
What am I doing wrong? Alternative solutions to this mostly academic problem are also welcome.
The yield keyword does not work from inside a lambda. The alternative is to use &block, in the same way that you are already doing on line 64 and 65:
gen = ->(num, &block) do
if num == 0
block.call(nil)
else
# ...
end
gen.call(num) { |tree| trees << tree }

Resources