Ruby code for Simple Tree Matching showing error - ruby

I wrote a code for Simple Tree Matching for matching two DOM nodes. It is showing some error. I am not able to figure it out.
I am posting the code and the error trace.
def tree_match(node1,node2)
# ------- Implementation of Simple Tree matching algorithm.
if node1.name!=node2.name
puts 'in base case'
return 0
elsif node1.children.empty? or node2.children.empty?
return 0
else
dp = Array.new(node1.children.size) {Array.new(node2.children.size,0)}
i=1
j=0
j = j+1
node1.children.each do |child1|
node2.children.each do |child2|
Line 180th. main error is in the code segment below.
dp[i][j] = [dp[i-1][j],dp[i][j-1],(dp[i-1][j-1] + tree_match(child1,child2))].max
j = j + 1
end
i = i+1
end
return 1+dp[dp[0].size-1][dp[1].size-1]
end
end
ERROR:
in block (2 levels) in tree_match': undefined method[]' for nil:NilClass (NoMethodError)
from /usr/local/lib/ruby/gems/2.0.0/gems/nokogiri-1.6.6.2/lib/nokogiri/xml/node_set.rb:187:in block in each'
from /usr/local/lib/ruby/gems/2.0.0/gems/nokogiri-1.6.6.2/lib/nokogiri/xml/node_set.rb:186:inupto'
from /usr/local/lib/ruby/gems/2.0.0/gems/nokogiri-1.6.6.2/lib/nokogiri/xml/node_set.rb:186:in each'
from /<>/DomTreeParser.rb:179:inblock in tree_match'
from /usr/local/lib/ruby/gems/2.0.0/gems/nokogiri-1.6.6.2/lib/nokogiri/xml/node_set.rb:187:in block in each'
from /usr/local/lib/ruby/gems/2.0.0/gems/nokogiri-1.6.6.2/lib/nokogiri/xml/node_set.rb:186:inupto'
from /usr/local/lib/ruby/gems/2.0.0/gems/nokogiri-1.6.6.2/lib/nokogiri/xml/node_set.rb:186:in each'
from /<path>/DomTreeParser.rb:178:intree_match'
from /<>/DomTreeParser.rb:180:in block (2 levels) in tree_match'
from /usr/local/lib/ruby/gems/2.0.0/gems/nokogiri-1.6.6.2/lib/nokogiri/xml/node_set.rb:187:inblock in each'
from /usr/local/lib/ruby/gems/2.0.0/gems/nokogiri-1.6.6.2/lib/nokogiri/xml/node_set.rb:186:in upto'
from /usr/local/lib/ruby/gems/2.0.0/gems/nokogiri-1.6.6.2/lib/nokogiri/xml/node_set.rb:186:ineach'
from /<>/DomTreeParser.rb:179:in block in tree_match'
from /usr/local/lib/ruby/gems/2.0.0/gems/nokogiri-1.6.6.2/lib/nokogiri/xml/node_set.rb:187:inblock in each'
from /usr/local/lib/ruby/gems/2.0.0/gems/nokogiri-1.6.6.2/lib/nokogiri/xml/node_set.rb:186:in upto'
from /usr/local/lib/ruby/gems/2.0.0/gems/nokogiri-1.6.6.2/lib/nokogiri/xml/node_set.rb:186:ineach'
from /<>/DomTreeParser.rb:178:in tree_match'
from testDriver.rb:11:in'
EDIT: nil class error is on line 180. I have marked it.

You move past the boundaries of the array you created. Here
dp = Array.new(node1.children.size) {Array.new(node2.children.size,0)}
you initailize an array with node1.children.size, then iterate over each of them (node1.children) and try to assign
dp[i][j] = ... in each step.
Since you start with i, j = 1, 1, here's what happens on the last step of the loop:
dp[i] returns nil because i == node1.children.size and indexes are numerated starting from 0. That's why you get
undefined method[] for nil:NilClass
when you call dp[i][j]
Can't really suggest a best course here since I don't see the entire code, but initializing a bigger Array should do the trick. Oh, and be sure to check out each_with_index

Related

Why am I getting a "no method" error for a simple matrix add operation in Ruby? Many essentially identical lines in my other programs

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.

Recieving desired output but error message tags along

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('-')

How to fix a wrong number of arguments error (0 for 1)

The code below is giving me an ArgumentError and I can't figure out the problem.
The challenge comes from the Advent of Code, day 3.
require 'set'
x, y = 0
visited = Set.new
def move(dir)
case move
when ">"
x += 1
when "<"
x -= 1
when "^"
y += 1
when "v"
y -= 1
end
end
def visit(x,y)
unless visited.include?([x,y])
visited << [x,y]
end
end
a_file = File.open("day3a_directions.txt", "r")
a_file.each_line("\n") do |line|
line.each_char do |dir|
move(dir)
visit(x,y)
end
end
puts visited.length
This is the error I'm getting:
day3a.rb:6:in `move': wrong number of arguments (0 for 1) (ArgumentError)
from tmp.rb:7:in `move'
from tmp.rb:28:in `block (2 levels) in <main>'
from tmp.rb:27:in `each_char'
from tmp.rb:27:in `block in <main>'
from tmp.rb:26:in `each_line'
from tmp.rb:26:in `<main>'
You have a typo in your code in the definition of move, here:
def move(dir)
case move
when ">"
...
Should be:
def move(dir)
case dir
when ">"
...
Also, I might add that you're circumventing the whole point of Set by checking if an element exists before adding it. This is built into the class already.

Ruby - nils appearing in array

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.

ruby quicksort error

below is my quicksort implementation in ruby(using the first element as pivot). But there's an error I cannot figure out why
def quicksort(items)
return items if items.nil? or items.length <= 1
first=items[0]
left,right=parti(items,0,items.length)
quicksort(left) + [first] + quicksort(right)
end
def parti(s,l,r)
p=s[l]
i=l+1
(l+1..r).each do |x|
if s[x] < p
s[x],s[i] = s[i],s[x]
i+=1
end
end
s[l],s[i-1] = s[i-1],s[l]
return [s[0..i-2],s[i..r]]
end
The error:
putarray.rb:38:in `block in parti': undefined method `<' for nil:NilClass (NoM
hodError)
from inputarray.rb:37:in `each'
from inputarray.rb:37:in `parti'
from inputarray.rb:22:in `quicksort'
from inputarray.rb:47:in `<main>'
it says in
if s[x] < p
s[x] is NilClass.
UPDATE: It turns out
left,right=parti(items,0,items.length) should be
left,right=parti(items,0,items.length-1)
But after this change, an error
inputarray.rb:37: stack level too deep (SystemStackError)
point to
(l+1..r).each do |x|
Didn't find any good explanation on the internet.
Keep in mind that in Ruby, array index starts from 0, not 1.
Therefore items.length will return the value of 1 greater than the maximum index in the array.
Try right=parti(items,0,items.length-1) instead of right=parti(items,0,items.length).

Resources