When I run this, there is a something wrong in the sum+=n statement, I don't know how to fix it - ruby

class Test
def multiples
(1..1000).each do |n|
if n % 3 == 0 || n % 5 == 0
sum += n
end
puts "The sum of multiples of 3 or 5 below 1000 is #{sum}"
end
end
end
test = Test.new
test.multiples
Error:
test.rb:9:in `block in multiples': undefined method `+' for nil:NilClass (NoMethodError)
The sum of multiples of 3 or 5 below 1000 is
from test.rb:7:in `each'
from test.rb:7:in `multiples'
from test.rb:19:in `<top (required)>'
from -e:1:in `load'
from -e:1:in `<main>'

You have not initialised sum, adding sum = 0 should fix this:
class Test
def multiples
sum = 0
(1..1000).each do |n|
if n % 3 == 0 || n % 5 == 0
sum += n
end
puts "The sum of multiples of 3 or 5 below 1000 is #{sum}"
end
end
end
test = Test.new
test.multiples

I remember when I started out in Ruby, I didn't really observe the error messages much, but you might find value in learning how to read those messages. You have:
test.rb:9:in `block in multiples': undefined method `+' for nil:NilClass (NoMethodError)
Just to dissect the obvious a little here. You get the following parts:
test.rb - Your file name
9 - The line number where things break down.
block in multiples - There's a block with an issue
inside 'multiples'
undefined method '+' - Remember that a + sign
is actually just a method name in Ruby and different methods are
available for different variable types. This is saying there's no
method by the name of + for the variable type you're calling it on.
nil - This is the variable type you just tried to call the +
method on.
By element 5 of that error message, you should be thinking "Why is sum currently nil?" And then look around your code and realize you've never declared it. Ruby doesn't know how to add something to nil, so you should immediately think about instantiating/declaring the variable 'sum' somewhere outside of the loop that it's adding things to itself.
Hopefully understanding error messages and why you had an error helps. Because if you don't understand and think through an error message, you're going to hit a road block again. It will probably be around the same time you try to create an array by shoveling something into it, instead of instantiating it first, and wonder why you get a similar error message.
Hope this helps!

Related

Ruby Unidentified Method for nil No Method Error

I'm attempting to run this code:
def get_linedict(filename,source)
`dwarfdump -l #{source} > dwarfline.txt`
linefile = File.new("dwarfline.txt","r")
match = false
linefile.readlines.each do |line|
puts line
if /uri:/ =~ line
file = line.match(/.*\/(.*)"/)[1]
if file == filename
match = true
end
puts file
puts match
end
end
And when I do I get the following error:
assn4.rb:12:in `block in get_linedict': undefined method `[]' for nil:NilClass (NoMethodError)
from assn4.rb:9:in `each'
from assn4.rb:9:in `get_linedict'
from assn4.rb:126:in `block in <main>'
from assn4.rb:80:in `each'
from assn4.rb:80:in `<main>'
If I change my each loop to only print the lines it's reading, it works fine. As I understand it, the error I'm getting comes from something being nil which shouldn't be, but if that error is coming from the each loop, why am I able to print out the file?
I guess you first have to ask if line.match gets something, before calling an element of the desired array.
line.match(/.*\/(.*)"/)[1]
When you call line.match(/.*\/(.*)"/) the result is nil. Then you attempt to access nil as an Array. That is when you get undefined method []' for nil:NilClass.
And as for this part of your question
but if that error is coming from the each loop, why am I able to print out the file?
The each loop is causing your code to fail and halt when the error occurs. Since you are attempting to print file after the errors, you are actually not printing out file on that iteration of the loop.
Be aware that line might not be nil. Your regular expression is probably just not covering all the cases you think it is, so one of the match calls is failing and returning nil.

Errors in my ruby script

I am getting an error I don't understand. It runs how I intended but throws an error at the end. I am still new to ruby but I know it has something to do with my for loops.
This is my code
nums = Array.new(24){Array.new(80){'X'}}
system('cls')
for i in 0..24
for j in 0..79
print nums[i][j]
end
end
And this is the error messages
K:/Ruby 2/RubyInvaders.rb:5:in block (2 levels) in <main>': undefined method `[]' for nil:NilClass (NoMethodError)
from K:/Ruby 2/RubyInvaders.rb:4:in each'
from K:/Ruby 2/RubyInvaders.rb:4:in block in <main>'
from K:/Ruby 2/RubyInvaders.rb:3:in each'
from K:/Ruby 2/RubyInvaders.rb:3:in <main>'
Its ok to offer better ways to do this but I would also like to understand why I am getting this error
You are creating an array with 24 elements and then a loop with 25 iterations. When you try to print the 25th iteration of that loop, the array position doesn't exist. If you change for i in 0..24 to for i in 0..23, the error should be addressed:
nums = Array.new(24){Array.new(80){'X'}}
system('cls')
for i in 0..23
for j in 0..79
print nums[i][j]
end
end
To amplify on Jon's comment, ruby ranges created with the ... operator are exclusive and won't use the highest specified value (as opposed to the .. operator, which is inclusive).
And--while your approach to looping is valid--it's not idiomatic for ruby. Something like this would be more common:
nums = Array.new(24){Array.new(80){'X'}}
system('cls')
(0..23).each do |i|
(0..79).each do |j|
print nums[i][j]
end
end
When i is 24, nums[i] is nil, and you are calling [] on it.

unexpected behaviour of ruby threading code snippet

I am trying to learn threading in ruby, i am executing following code:
Thread.abort_on_exception = true
threads = 5.times.map do |i|
Thread.new(i) do |j|
raise "Boom!" if j == 1
print "#{j}\n"
end
end
i=0
loop do
i+=1
puts "Waiting!!" if i == 10000
break if i == 10**4
end
threads.each do |th|
begin
th.join
rescue RuntimeError => e
puts e
end
end
puts "Done!!"
Sometimes it run perfectly without exception and shows output like:
Waiting!!
0
2
3
4
Boom!
Boom!
Done!!
and sometimes it quits with exception and shows output like:
0
2
3
4
threading.rb:5:in `block (2 levels) in <main>': Boom! (RuntimeError)
Now my questions are:
Why? it quits with RuntimeError while i have already rescued it.
How could there be two Boom! in output.
Development environment:
Windows 7 (x64)
Ruby 2.1.5
The exception is happening in a thread before the main thread has reached the block with the Thread#join call; specifically, within your loop. The problem is that, with exceptions bubbling up to the main thread, it can literally happen on any line of code; thus, you need to encapsulate error handling within a thread as well. Perhaps it can return an error value, but exceptions are fundamentally broken when using this concurrency model.
(To prove this to yourself, try setting $DEBUG = true at the top of your file; this will show you where the exceptions really occur.)

ArgumentError in rspec

I want to write some tree data structure in ruby. The class file:
class Tree
attr_accessor :node, :left, :right
def initialize(node, left=nil, right=nil)
self.node=node
self.left=left
self.right=right
end
end
The rspec file:
require 'init.rb'
describe Tree do
it "should be created" do
t2=Tree.new(2)
t1=Tree.new(1)
t=Tree.new(3,t1,t2)
t.should_not be nil
t.left.node should eql 1
t.right.node should eql 2
end
end
Rspec keeps complaining:
1) Tree should be created
Failure/Error: t.left.node should eql 1
ArgumentError:
wrong number of arguments (0 for 1)
# ./app/tree.rb:3:in `initialize'
# ./spec/tree_spec.rb:9:in `block (2 levels) in <top (required)>'
Why?? I move the spec code into the class file and it works out. What is wrong?
Believe it or not, the problem is two missing dots in your rspec. These lines:
t.left.node should eql 1
t.right.node should eql 2
should be this:
t.left.node.should eql 1
t.right.node.should eql 2
Insert that period before should, and your spec should pass.
Here's what's going on. The should method works on any value, but if you call it bare, like this:
should == "hello"
it will operate on the subject of your test. What's the subject? Well, you can set the subject to whatever you want using the subject method, but if you don't, rspec will assume the subject is an instance of whatever class is being described. It sees this at the top of your spec:
describe Tree
and tries to create a subject like this:
Tree.new
which blows up, since your initialize won't work without any arguments; it needs at least one. The result is a pretty cryptic error if you didn't intend to write a should with an implicit subject.

Ruby. Mocking in RSpec

I have a problem with mocking. I have class DistanceMatrix and I would
like to indicate which method form_matrix was called in if/else
statement. I need to use mocha and RSpec. Any ideas?
class DistanceMatrix
def initialize(*args)
if args[0].class == String
form_matrix(get_data_from_yaml(args[0], args[1]))
elsif args[0].class == Array || args[0] == nil
form_matrix(get_data_from_db(args[0]))
end
end
def form_matrix(...)
...
end
end
it tried:
describe DistanceMatrix, "when mocking ..." do
it "should do call form_matrix" do
DistanceMatrix.any_instance.expects(:form_matrix).with([1]).once
DistanceMatrix.any_instance.expects(:get_data_from_yaml).with("file_name.yml").once.returns([1])
DistanceMatrix.new("file_name.yml")
end
end
but got error:
Failures:
1) DistanceMatrix when mocking ... should do call form_matrix
Failure/Error: DistanceMatrix.new("file_name.yml")
unexpected invocation: #<AnyInstance:DistanceMatrix>.get_data_from_yaml('file_name.yml', nil)
unsatisfied expectations:
- expected exactly once, not yet invoked: #<AnyInstance:DistanceMatrix>.get_data_from_yaml('file_name.yml')
- expected exactly once, not yet invoked: #<AnyInstance:DistanceMatrix>.form_matrix([1])
satisfied expectations:
- allowed any number of times, already invoked once: #<DistanceMatrix:0x9e48b40>.get_optimal_route(any_parameters)
- allowed any number of times, already invoked once: #<Database::Distances:0x9d59798>.load_distances(any_parameters)
# ./distance_matrix.rb:18:in `initialize'
# ./tsp_algorithm_spec.rb:253:in `new'
# ./tsp_algorithm_spec.rb:253:in `block (2 levels) in <top (required)>'
Finished in 0.25979 seconds
I found that in RSpec we should use not .expects() but .should_receive(), so I tried:
describe DistanceMatrix, "when mocking ..." do
it "should do call form_matrix" do
DistanceMatrix.any_instance.should_receive(:form_matrix).with([1])
DistanceMatrix.any_instance.should_receive(:get_data_from_yaml).with("file_name.yml").and_return([1])
DistanceMatrix.new("file_name.yml")
end
end
but got new failure:
Failures:
1) DistanceMatrix when mocking ... should do call form_matrix
Failure/Error: DistanceMatrix.any_instance.should_receive(:form_matrix).with([1])
(#<Mocha::ClassMethods::AnyInstance:0x96356b0>).form_matrix([1])
expected: 1 time
received: 0 times
# ./tsp_algorithm_spec.rb:251:in `block (2 levels) in <top (required)>'
Finished in 0.26741 seconds
I only have experience with using Mocha and not RSpec, but looking at the Mocha failure message, the key parts are these :-
unexpected invocation: #<AnyInstance:DistanceMatrix>.get_data_from_yaml('file_name.yml', nil)
unsatisfied expectations:
- expected exactly once, not yet invoked: #<AnyInstance:DistanceMatrix>.get_data_from_yaml('file_name.yml')
If you look at the ends of these lines, you will notice that get_data_from_yaml is not being called with the expected parameters. It is being called with ('filename.yml', nil) and not ('filename.yml') as expected.
This is happening because when you call DistanceMatrix.new("file_name.yml") in your test with only one argument and then inside DistanceMatrix#initialize DistanceMatrix#get_data_from_yaml is being called with (args[0], args[1]) and since args is a single element array, args[1] will be nil.
Maybe this isn't how you expected Ruby to work, but the following demonstrates this behaviour :-
def foo(*args)
puts "args[0]=#{args[0].inspect}; args[1]=#{args[1].inspect}"
end
foo("string") # => args[0]="string"; args[1]=nil
DistanceMatrix.any_instance.expects(:form_matrix).with("String") # => supply the correct string param
or
DistanceMatrix.any_instance.expects(:form_matrix).with([]) # => supply the correct array param
I'm not sure what your get_data_from_db and get_data_from_yaml methods are doing, but you should be able to control those inputs as well to verify the correct arguments are being supplied to form_matrix.
EDITED
You'll have to use DistanceMatrix.any_instance instead of mocking on an instance variable because you're trying to mock something in the initializer. Also, in case its unclear, you'll need to actually make the appropriate method call after you set up the mock in the lines above, e.g.
DistanceMatrix.new("SomeString")
EDITED
it "should do call #form_matrix with proper arguments" do
DistanceMatrix.any_instance.expects(:form_matrix).with([1])
DistanceMatrix.any_instance.expects(:get_data_from_yaml).with("foo").returns([1])
DistanceMatrix.new("foo")
end

Resources