My friends and I are working on some basic Ruby exercises to get a feel for the language, and we've run into an interesting behavior that we're yet unable to understand. Basically, we're creating a tree data type where there's just one class, node, which contains exactly one value and an array of zero or more nodes. We're using rspec's autospec test runner. At one point we started writing tests to disallow infinite recursion (a circular tree structure).
Here's our test:
it "breaks on a circular reference, which we will fix later" do
tree1 = Node.new 1
tree2 = Node.new 1
tree2.add_child tree1
tree1.add_child tree2
(tree1 == tree2).should be_false
end
Here's the Node class:
class Node
attr_accessor :value
attr_reader :nodes
def initialize initial_value = nil
#value = initial_value
#nodes = []
end
def add_child child
#nodes.push child
#nodes.sort! { |node1, node2| node1.value <=> node2.value }
end
def == node
return (#value == node.value) && (#nodes == node.nodes)
end
end
We expect the last line of the test to result in an infinite recursion until the stack overflows, because it should continually compare the child nodes with each other and never find a leaf node. (We're under the impression that the == operator on an array will iterate over the array and call == on each child, based on the array page of RubyDoc.) But if we throw a puts into the == method to see how often it's called, we discover that it's called exactly three times and then the test passes.
What are we missing?
Edit: Note that if we replace be_false in the test with be_true then the test fails. So it definitely thinks the arrays are not equal, it's just not recursing over them (aside from the three distinct calls to ==).
If you click on the method name of the RubyDoc you linked to, you will see the source (in C) of the Array#== method:
{
// [...]
if (RARRAY(ary1)->len != RARRAY(ary2)->len) return Qfalse;
if (rb_inspecting_p(ary1)) return Qfalse;
return rb_protect_inspect(recursive_equal, ary1, ary2);
}
This implementation (specifically the "recursive_equal") suggests that Array#== already implements the infinite recursion protection you're after.
Related
just having a bit of confusion while recursively traversing through BST, the type of traversal is inorder. here is the code. if a block is given the method yields the node to the block. Right now i am lost with the arrays. Whats confusing is, When its done going to the leftmost node. its gonna add it to the array and lets say that the leftmost node does not have a right child, when the leftmost node returns control to the parent node/method, the leftmost nodes value is still present in the array while i never returned the array to the parent. This obviously only happens with a node that does not have a child because of the return statement i put at the end of the method 'return if node.right_node == nil && node.left_node == nil' which causes the present frame to return so it shouldnt return the modified array right, but when the control is returned the array is modified and infact has the added element.
def inorder(node=root,array=Array.new)
inorder(node.left_node,array) if has_left(node)
if block_given?
yield(node.value)
else
array << node.value
end
inorder(node.right_node,array) if has_right(node)
return if node.right_node == nil && node.left_node == nil
return array if !block_given?
end
I'm implementing a binary search tree in ruby, and I'm attempting to define a function to remove an value from the tree. The Tree has a #root value that points to a Node object. which is defined as such:
class Node
attr_reader :value
attr_accessor :left, :right
def initialize value=nil
#value = value
#left = nil
#right = nil
end
# Add new value as child node to self, left or ride
# allows for duplicates, to L side of tree
def insert new_value
if new_value <= #value
#left.nil? ? #left = Node.new(new_value) : #left.insert(new_value)
elsif new_value > #value
#right.nil? ? #right = Node.new(new_value) : #right.insert(new_value)
end
end
end
Thus, the nodes all hold references to their L & R children. Everything works on the tree, inserting, traversing, performing a breadth-first-search (level-order-traverse) to return a Node value, if found.
The problem I'm having is when trying to remove a Node object. I can't figure out how to set the actual OBJECT to nil, or to its child, for example, rather than reassigning a POINTER/variable to nil and the object still existing.
Tree has two functions that are supposed to do this (you can assume that breadth_first_search correctly returns the appropriate found node, as does smallest_r_child_of)
def remove value
node = breadth_first_search(value)
return false if node.nil?
remove_node(node)
end
def remove_node node
if node.left.nil? && node.right.nil?
node = nil
elsif !node.left.nil? && node.right.nil?
node = node.left
elsif node.left.nil? && !node.right.nil?
node = node.right
else
node = smallest_r_child_of(node)
end
return true
end
I thought that by passing in the actual node object to remove_node, that I could call node = ____ and the like to reassign the actual object to something else, but all it does, as far as I can tell, is reset the node argument variable/pointer, while not actually reassigning my data at all.
Does anyone have any tips/suggestions on how to accomplish what I'm trying to do?
You cannot "set an object to nil". An object can never change its class, it either is an instance of Node or it is an instance of NilClass, it cannot at one point in time be an instance of Node and at another point in time be an instance of NilClass.
Likewise, an object cannot change its identity, object #4711 will always be object #4711, however, nil is a singleton, so there is only one nil and it has the same identity during the entire lifetime of the system.
What you can do is to bind the variable which references the object to nil. You are doing the opposite operation inside your insert method.
I'm trying to develop a new cop (based on these guidelines) and am banging my head trying to get the correct node pattern.
I want the cop to register an offense when X.some_method is called without providing a block.
i.e X.some_method is an offense, but X.some_method { blah } is not.
I got the correct pattern for identifying X.some_method, which is '(send (const nil? :X) :some_method ...'.
But not sure how to create a pattern for "is not given a block"?
Apparently, in the parsed AST, when a node is given a block, that node is then represented as the first child of that block.
i.e
[92] pry(RuboCop)> node # `X.some_method(something) { do_something }`
=> s(:block,
s(:send,
s(:const, nil, :X), :some_method,
s(:send, nil, :something)),
s(:args),
s(:send, nil, :do_something))
And we can check that using the Rubocop::AST instances.
Here is the full implementation (includes an option for multiple method names):
MSG = 'Avoid using `X.%<method>s` without providing a block.'
def_node_matcher :x_method, '(send (const nil? :X) ${:some_method :another_method} ...)'
def on_send(node)
x_method(node) do |method_name|
return if !method_name || first_child_of_block?(node)
add_offense(node, location: :selector, message: format(MSG, method: method_name))
end
end
private
# checks if the given node's parent is a block, and the given node is its first child,
# which would mean that the block is supplied to the given node (i.e `node { block }`)
def first_child_of_block?(node)
return false unless (parent = node.parent)
return false unless parent.type == :block
parent.children.first == node
end
I am attempting to search a simple tree to find a target value. My instructions are to use recursion. I understand that recursion means (in part) that the method is called from within itself. In that sense the following code succeeds. However, I also understand (I think) that recursion requires setting up an inquiry that cannot be resolved until the base case is reached, at which time the answer is passed back in the chain and each unresolved inquiry can be resolved. My code does not really do that (I think). I need a higher power to show me (and hopefully others) the way.
class Tree
attr_accessor :payload, :children
def initialize(payload, children)
#payload = payload
#children = children
end
def self.test (node, target)
if node.payload == target
print "Bingo!"
else
print node.payload
end
node.children.each do |child|
Tree.test(child, target)
end
end
end
# The "Leafs" of a tree, elements that have no children
deep_fifth_node = Tree.new(5, [])
eleventh_node = Tree.new(11, [])
fourth_node = Tree.new(4, [])
# The "Branches" of the tree
ninth_node = Tree.new(9, [fourth_node])
sixth_node = Tree.new(6, [deep_fifth_node, eleventh_node])
seventh_node = Tree.new(7, [sixth_node])
shallow_fifth_node = Tree.new(5, [ninth_node])
# The "Trunk" of the tree
trunk = Tree.new(2, [seventh_node, shallow_fifth_node])
Tree.test(trunk, 5)
Assuming your function calls itself (and yours appears to), you're using recursion. See https://softwareengineering.stackexchange.com/questions/25052/in-plain-english-what-is-recursion for a good discussion on recursion.
To prevent infinite recursion, you'll want a base case that makes sure that you do eventually unwind everything completely, rather than simply continuing to call your function forever, and quite often that is when you're fully complete with whatever computation you're running, but it doesn't necessarily mean you couldn't find what you're looking for (in the case of a search) before you reach the base case.
Yes, your function is using recursion: It calls itself.
Here is a proposal for your method:
def find_nodes_with_payload?(target, result = [])
if payload == target
result << self
end
children.each do |child|
child.find_nodes_with_payload?(target, result)
end
accu
end
The method will collect all the nodes in a (sub)-tree containing a specific target value. Does solve your problem?
I modified your original code in following ways:
Made it an instance method and removed the 'node' argument
Renamed it from 'test' to something more descriptive, IMHO
added an array which collects all the nodes that contain the payload
changed the logic to: do nothing but recursively call the method again for all the children if the condition does not match
You can make it simple.
def search(tree, target)
trav = ->(node) { node.val == target ? puts("Bingo") : node.leaves.each(&trav) }
trav[tree]
end
Tree creation can also be simplified.
Node = Struct.new(:val, :leaves)
add = ->(tree) { tree ? tree.map {|k,v| Node.new(k, add[v])} : [] }
So we can recursively create tree and find value.
sample_tree = {2 => {7 => {6 => {4=>{}, 11=>{}}}, 5=> {9 => {}, 4=>{}}}}
tree = add[sample_tree]
search(tree.first, 5)
I am working on a compiler written in Ruby and I am currently at the semantic analysis stage (type checking). I have an AST that I need to visit in two ways: pre-order and post-order, I was wondering what the best way to do this is in Ruby. I know that passing a block to each is essentially the Visitor Pattern, but since I need to visit in two ways(pre, post) and Ruby doesn't support method overloading, I am not sure how to approach this.
(Note: I am trying to have the Node objects control how they are visited, so my Visitor isn't bloated)
Here is what I am thinking about trying:
Two accept methods for each Node class accept_pre and accept_post that call the corresponding accept_pre and accept_post methods of other Nodes
class Node
def initialize(a, b, c)
#a, #b, #c = a, b, c
end
def accept_pre(visitor)
#a.accept_pre visitor
#b.accept_pre visitor
#c.accept_pre visitor
vistor.visit_node(self)
end
def accept_post(visitor)
visitor.visit_node(self)
#c.accept_post visitor
#b.accept_post visitor
#a.accept_post visitor
end
end
Is there a better way to do this? Can it be done with .each, even though I need two orderings?
Any help would be appreciated.
You could fold the two orderings into one accept using an traversal option arg. You can certainly use each over the node's members to dispatch the children's accepts.
class Node
def initialize(a, b, c)
#a, #b, #c = a, b, c
end
def accept(visitor, traversal=:pre)
visitor.visit(self) if traversal == :pre
order = traversal == :pre ? :each : :reverse_each
[#a,#b,#c].send(order) { |e| e.accept(visitor, traversal) }
visitor.visit(self) if traversal == :post
end
end