I was helping a friend out with a ruby assignment, and I ran into some peculiar behavior. We've refactored it to use a Hash and increment the counts directly through that, so this code is obsolete, but for my own sanity, I need to know why nil values show up in the packets array. The strangest thing is that it doesn't always happen, it only happens on some executions.
I should note that the purpose of the code is essentially to tally the number of times in a row that the random value is below p.
count = 0
p = 0.1
packets = []
counts = []
10000.times do
if rand.round(1) <= p
count += 1
elsif count > 0
packets << count
count = 0
end
end
packets.each do |train|
counts[train] = counts.fetch(train, 0) + train
end
counts.each_with_index do |value, index|
puts "Train Length: #{index} Count: #{value}"
end
The packets array should only ever contain numerical values, but it winds up with multiple nil's. What could be causing this?
Are you certain that your packets array is getting nils? There's basically no way that could happen with the code you posted. Dump out the packets array to be sure.
However, I do observe that the code sometimes fails with the following error:
NoMethodError: undefined method `+' for nil:NilClass
from (irb):16:in `block in irb_binding'
from (irb):15:in `each'
from (irb):15
from /usr/bin/irb:12:in `<main>'
from the line
counts[train] = counts.fetch(train, 0) + train
This error means that counts.fetch(train, 0) was nil (if train was nil, you'd get a coercion error instead). This can happen if, for example, you set counts[3] before counts[2] is set, and later access counts[2] (because Ruby will fill the array elements you "skipped over" with nils).
If you are actually getting nils in packets, then you may have demons in your Ruby.
Related
Here's the error statement:
EvoWithout.rb:53:in `block (2 levels) in ': undefined method `+' for nil:NilClass (NoMethodError)
Here's line 53:
if behavior[i,0] > Thrsh && s == 0 then animal[i,0]+= 5 end
Here's the relevant code:
situation= Matrix[ [1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0],
[1,0,0,0,0,1,1,1,1,1,1,0,0,0,0,1],
[1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1],
[1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1],
[1,0,0,1,1,0,1,0,1,0,1,0,1,0,0,1] ]
# Build brain with $Behavmax rows of 0,1's
brain = Matrix.build(10,16) { 1 }
for i in (0..$Behavmax)
for j in (0..$Stimmax)
if rand(4) < 1.1 then brain[i,j] = 0 end
end # j
end #i
stimulus=Matrix.column_vector([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])
behavior=Matrix.row_vector([0,0,0,0,0,0,0,0,0,0])
animal=Matrix.row_vector([20,20,20,20,20,20,20,20,20,20]) # to hold value of fitness
# BEGIN MAIN PROGRAM
# Noise=20
# Go through once presenting 1 situation after another
for s in (0..4)
for j in (0..$Stimmax)
stimulus[j,0] = situation[s,j]
end # for j
# GENERATE BEHAVIOR
behavior=brain*stimulus
for i in (0..$Behavmax) #fire iff stimulus pattern matches detector
if behavior[i,0] > Thrsh && s == 0 then animal[i,0]+= 5 end
#if behavior[i,0] > Thrsh && s != 0 then print "Behavior#{i}=#{behavior[i,0]} and s=#{s} " end
end # for i
puts
end # for s
An important skill to learn is to read error messages and warnings. In your title, you ask:
Why am I getting a “no method” error for a simple matrix add operation in Ruby?
But, that's not what the error message is saying!
You don't get a NoMethodError for a matrix add operation (Matrix#+). If you were, the error message would say something like:
EvoWithout.rb:53:in `block (2 levels) in ': undefined method `+' for animal:Matrix (NoMethodError)
Note that the error message would say (bold emphasis mine) "undefined method `+' for animal:Matrix" (which would be wrong, because Matrix#+ exists). However, that's not what your error message is saying. Your error message says (bold emphasis mine):
undefined method `+' for nil:NilClass
Which is correct, because NilClass does, in fact, not have a + method, and neither do its superclasses Object, Kernel, and BasicObject.
So, you are looking in the wrong place: your problem is not with the matrix add operation, your problem is that your matrix index operation returns nil.
And the reason for that is rather simple: you animal matrix contains only a single row, but you are iterating over $Behavmax + 1 rows. So, as soon as $Behavmax is greater than zero, you will index into the second row of animal, which doesn't exist. Therefore, it will return nil, and your addition will fail.
Remember that a ω= b for any operator ω and arbitrary expressions a and b is equivalent to a = a ω b with a only evaluated once, so
animal[i,0]+= 5
is roughly equivalent to:
__temp__ = animal[i, 0]
animal[i, 0] = __temp__ + 5
And if i is anything other than 0, __temp__ will be nil, because there is only one row in animal.
I am passing all of the tests for this kata yet still receiving some errors. The codewars website does not allow me to see all the test cases, so with my inexperienced eyes it is hard to see the issue. Any explanation for what I am seeing is greatly appreciated. My method is to parse for nil, split digits into an array, and evaluate the first one for even/odd and place it into the new array with/out "-" accordingly, and removing each first element as I iterate through.
Dashatize:
Given a number, return a string with dash'-'marks before and after each odd integer, but do not begin or end the string with a dash mark.
Ex:
dashatize(274) -> '2-7-4'
dashatize(6815) -> '68-1-5'
def dashatize(num)
if num.nil?
"nil"
else
arr2 = []
arr = num.digits.reverse
arr2 << arr[0]
arr.shift
until arr == [] do
if arr[0].even? && arr2[-1].to_i.even?
arr2 << arr[0].to_s
arr.shift
else
arr2 << "-"
arr2 << arr[0].to_s
arr.shift
end
end
arr2.join
end
end
I pass all tests but still fail the kata due to this:
#<Math::DomainError: out of domain> main.rb:10:in `digits'
main.rb:10:in `dashatize' main.rb:39:in `block (2 levels) in <main>'
/runner/frameworks/ruby/cw-2.rb:180:in `wrap_error'
/runner/frameworks/ruby/cw-2.rb:72:in `it'
/runner/frameworks/ruby/cw-2.rb:206:in `it' main.rb:36:in `block in <main>'
/runner/frameworks/ruby/cw-2.rb:55:in `block in describe'
/runner/frameworks/ruby/cw-2.rb:46:in `measure'
/runner/frameworks/ruby/cw-2.rb:51:in `describe'
/runner/frameworks/ruby/cw-2.rb:202:in `describe' main.rb:29:in
`<main>'
From the docs:
Math::DomainError Raised when a mathematical function is evaluated
outside of its domain of definition.
You're calling the #digits function on the input, which is probably negative for some example in the test cases and you get the error mentioned above.
So again, doing this:
-1.digits
Will give you an error like you got:
out of domain
():1:in `digits'
():1:in `<main>'
You have to use something other than #digits or make it positive first or find some other solution.
On a side note, here' my approach to the problem:
def dashatize(number)
number.to_s
.gsub(/[13579]/, '-\\0-')
.gsub('--','-')
.delete_suffix('-')
.delete_prefix('-')
end
dashatize(68145)
#=>"68-1-4-5"
dashatize(6815)
#=>"68-1-5"
dashatize(274)
#=> "2-7-4"
I guess #Viktor already caught the reason: maybe the test case uses negative numbers.
You can fix your code changing your line arr = num.digits.reverse using Integer#abs to:
arr = num.abs.digits.reverse
Side note, a shorter version using the array of digits (Array#slice_when):
num.abs.digits.reverse.slice_when { |a, b| a.odd? || b.odd? }.map(&:join).join('-')
I am new to ruby and I was trying to iterate over a 2d array. I made a mistake in my code. From that mistake, I noticed some unexpected output.
s = [["ham", "swiss"], ["turkey", "cheddar"], ["roast beef", "gruyere"]]
i = 0;
s.each{
|array| so = array[i] # pin
puts so[i]
}
Due to #pin, if i = 0, output is h t r. i = 1 gives w h r. i > 1 gives an error:
C:/Ruby.rb in `block in <main>': undefined method `[]' for nil:NilClass (NoMethodError)
from C:/Ruby.rb:3:in `each'
from C:/Ruby.rb:3:in `<main>'
If I use |array| so = array # pin, then my code does not produce strange output. I'll just fix the remaining stuff to make my code iterate for all values that 'i' can have.
Please explain this.
PS: Working code is here
s = [["ham", "swiss"], ["turkey", "cheddar"], ["roast beef", "gruyere"]]
s.each{
|array| so = array
array.each{
|str| puts str
}
}
For each type of sandwich, when i is 0, so is the 1st element, which is the meat. so[0] is the first letter of the meat.
When i is 1, which is the 2nd element, which is the cheese. so[1] is the second letter of the cheese.
When i is 3, there is no third component to the sandwich. so so is nil. so[2] is asking for the nil[2].
nil is a class, like everything in ruby. But nil does not implement the [] method, as arrays and other classes that implement the Enumerable module do.
Since nil does not support the [] method, then you get the undefined method error.
Even operations that are built into other languages, like +, [], and == are methods that can be overridden in Ruby.
To understand exactly what's happening, try this bit of code:
class NilClass
def [] (i)
nil
end
end
Executing that will open up the existing NilClass, and add a method called []. Then you can do nil[1] and it will return nil. Changing an existing class like this is known as monkey patching in the Ruby world.
When you ask for so[2] you are actually asking for the third element, and if it doesn't exist, you'll get an error.
I recommend structuring your blocks like so:
s.each do |pair|
puts pair
end
Note the do/end instead of {} and the placement of the iteration variable inline with the each. Also note that this is equivalent to your "working code" so you don't need the extra iterator in this case.
So I've been trying to create a method that would display an array of prime numbers for any Integer that you insert as an argument, but I've been getting the weirdest error. Can anyone explain what this error means and how to solve my problem w/o using any gems? I've tried looking on Stack Overflow and haven't found a solution. Thanks!
def prime_factors(num, output = [])
factor = (2..num-1).find(0){|divisor| num % divisor == 0} #returns nil if find fails.
output << factor if factor != 0
prime_factors(num/factor, output)
end
prime_factors(5)
The error I've been receiving is:
`find': undefined method `call' for 0:Fixnum (NoMethodError)
in `prime_factors'
in `<main>'
See the docs for Enumerable#find (which you call in the first line of your function: http://ruby-doc.org/core-1.9.3/Enumerable.html#method-i-find
The parameter of find is called if no object of the Enumerable ((2..num-1) in your case) matches (that is, the given block always returned false). So find tries to find a Number for which num % divisor is equal to 0, but doesn't find one. Then, it calls the function supplied as parameter. You pass 0 for that parameter. So it tries to invoke 0 as a function (which is done by executing the call Method: 0.call). But 0 of class Fixnum has no such method (it is not a function but a number). That generates the error message.
The error you are getting is because of find(0) in the below line
factor = (2..num-1).find(0)
since find method is unable to trigger call for "0 i.e fixnum" as argument.
I don't know why you are doing that.
Instead, you can try this logic
(2..num-1).each do |i|
is_prime = true
(2..Math.sqrt(i)).each { |j| is_prime = false if i % j == 0 }
output << i if is_prime
end
What i'm trying to do is create a method that can be given an array as an argument. The array should have some numbers in it. The method will return the number of times the array includes each number inside of it. I understand that there are probably many ways to do this, but I'd appreciate it if folks could help me understand why my way is not working rather than just advising me to do something completely different.
So I start by trying this method out
def score (dice)
dice.each do |die|
x = /(die)/.match(dice.to_s).length
end
x
end
and calling it with score ([5])expecting to get an output of 1. However, I get
NoMethodError: undefined method `length' for nil:NilClass
from t2.rb:22:in `block in score'
from t2.rb:21:in `each'
from t2.rb:21:in `score'
from (irb):2
from /home/macs/.rvm/rubies/ruby-2.0.0-p247/bin/irb:13:in `<main>'
I have also tried changing the match statement slightly (getting rid of the to_s) so it is
def score (dice)
dice.each do |die|
x = /(die)/.match(dice).length
end
x
end
and calling it with score ([5]) I get
TypeError: no implicit conversion of Array into String
from t2.rb:22:in `match'
from t2.rb:22:in `block in score'
from t2.rb:21:in `each'
from t2.rb:21:in `score'
from (irb):2
from /home/macs/.rvm/rubies/ruby-2.0.0-p247/bin/irb:13:in `<main>'
Really not sure how I'm supposed to accomplish this matching.
In this line
/(die)/.match(dice.to_s).length
the method match returns nil if the argument you are passing doesn't match the regular expression, which leads to this error
nil.length
# => NoMethodError: undefined method `length' for nil:NilClass
The method will return the number of times the array includes each
number inside of it.
You can try this
a = [1,1,1,2,2,1,3]
a.uniq.map { |x| a.count(x) }
# => [4, 2, 1]
a.uniq.map { |x| {x => a.count(x)} }
# => [{1=>4}, {2=>2}, {3=>1}]
If you want to count the occurence of each elements in the array, then you can do something like this
def score (dice)
count_hash = {}
dice.uniq.each do |die|
count_hash[die] = dice.count(die)
end
count_hash
end
I'd appreciate it if folks could help me understand why my way is not working ...
/(die)/ creates a Regexp, a pattern that can be matched against a string. Your pattern matches and captures die.
Regexp#match returns a MatchData object if there was a match:
/(die)/.match('a string with die') #=> #<MatchData "die" 1:"die">
# here's the match: ^^^
or nil if there was no match:
/(die)/.match('a string with dice') #=> nil
You are not working with string but with an array of integers. You convert this array to a string using Array#to_s:
dice = [5]
dice.to_s #=> "[5]"
This string doesn't contain die and therefore match returns nil:
/(die)/.match("[5]") #=> nil
Calling nil.length then raises the NoMethodError.
Passing the array "as-is" doesn't work either, because match expects a string:
/(die)/.match([5]) #=> TypeError: no implicit conversion of Array into String
Using a Regexp is not going to work here, you'll have to approach this problem in another way.
This is probably the most rubyish way to solve the problem:
a = [1,1,1,2,2,1,3]
p Hash[a.group_by{|x|x}.map{|key, val| [key,val.size]}]
#=> {1=>4, 2=>2, 3=>1}
An example that might help you implement your logic
a = [2,3,2,8,3]
a.uniq.each {|i| print i, "=>", a.to_s.scan(/#{i}/).length, " times \n" } #=> this works but ugly.
a.uniq.each {|i| print i, "=>", a.count(i), " times \n" } #=> borrowed from one of the answers.
2=>2 times
3=>2 times
8=>1 times
You're getting errors because in both cases you are trying to match a string (5) with the wrong thing.
This tries to match die with the entire array dice converted to a string:
dice.each do |die|
x = /(die)/.match(dice.to_s).length
end
This tries to match die with the dice array itself:
dice.each do |die|
x = /(die)/.match(dice).length
end