about handling args in block

S

salamond

Hi, guys.

I'm writing a Node class for a Binary Tree.
Coding comes below.

There's a method remove_leaf.
Now I want to do it this way:
=3Dbegin
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0[@left_node, @right_node].ea=
ch do |node|
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if node.is_l=
eaf?
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0node =3D nil
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0end
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0end
=3Dend

But it doesn't work.
Is there a way to do it ?

You can ignore class Tree. I just post it here to make a little sense.

---------- Forwarded message ----------
From: salamond <[email protected]>
Date: Wed, Oct 13, 2010 at 9:12 AM
Subject: ruby-talk-ask
To: (e-mail address removed)


class Node
=A0attr_accessor :value, :left_node, :right_node

=A0def initialize(value=3Dnil, left_node=3Dnil, right_node=3Dnil)
=A0 =A0@value =3D value
=A0 =A0@left_node =3D left_node
=A0 =A0@right_node =3D right_node
=A0end

=A0 =A0 =A0 =A0def to_s
=A0 =A0 =A0 =A0 =A0 =A0 =A0 [email protected]_s
=A0 =A0 =A0 =A0end

=A0 =A0 =A0 =A0def is_leaf?
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0return @left_node=3D=3Dnil && @right_node=3D=
=3Dnil
=A0 =A0 =A0 =A0end

=A0 =A0 =A0 =A0def remove_leaf
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if is_leaf?
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0return
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0else
=3Dbegin
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0[@left_node, @right_node].ea=
ch do |node|
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if node.is_l=
eaf?
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0node =3D nil
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0end
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0end
=3Dend
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if @left_node.is_leaf?
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0@left_node =
=3D nil
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0end
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if @right_node.is_leaf?
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0@right_node =
=3D nil
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0end
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0end
=A0 =A0 =A0 =A0end

end


require 'test/unit'
require 'node'

class NodeTest < Test::Unit::TestCase
=A0 =A0 =A0 =A0def setup
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0@empty =3D Node.new()
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0@single =3D Node.new("haha")
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0@full =3D Node.new(1, Node.new(2), Node.new(=
4))
=A0 =A0 =A0 =A0end

=A0 =A0 =A0 =A0def test_default_init()
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0assert(@empty.value=3D=3Dnil)
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0assert(@empty.left_node=3D=3Dnil)
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0assert(@empty.right_node=3D=3Dnil)
=A0 =A0 =A0 =A0end

=A0 =A0 =A0 =A0def test_init_with_value()
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0assert(@full.left_node.value=3D=3D2)
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0assert(@full.right_node.value=3D=3D4)
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0assert(@full.value=3D=3D1)
=A0 =A0 =A0 =A0end

=A0 =A0 =A0 =A0def test_to_s()
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0assert(@full.to_s =3D=3D "1")
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0assert(@single.to_s =3D=3D "haha")
=A0 =A0 =A0 =A0end

=A0 =A0 =A0 =A0def test_is_leaf()
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0assert(@empty.is_leaf? =3D=3D true)
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0assert(@single.is_leaf? =3D=3D true)
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0assert(@full.is_leaf? =3D=3D false)
=A0 =A0 =A0 =A0end

=A0 =A0 =A0 =A0def test_remove_leaf()
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0assert(@full.is_leaf? =3D=3D false)
=A0 =A0 =A0 =A0 =A0 =A0 =A0 [email protected]_leaf()
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0assert(@full.is_leaf? =3D=3D true)
=A0 =A0 =A0 =A0end

end

require "node"

class Tree

attr_accessor :root, :nodes, :sorted_nodes

def initialize( root=3Dnil )
@root =3D root
@nodes =3D Array.new()
if @root!=3D nil
@nodes.push( @root )
end
@sorted_nodes =3D Array.new()
@targets =3D Array.new()
end

def insert( node )
@nodes.push( node )
@root =3D insert_at( @root, node )
end

# TODO: to prevent multiple travel in the same way, add state check
def travel(order =3D :inorder)
@sorted_nodes =3D Array.new()
travel_at( @root, order )
return @sorted_nodes
end

def to_s
@data.to_s
end

def cut_all_leaves()
@nodes.each do |node|
node.cut_leaf
end
end

def n_step_from_leaf( n )
(1..n).each do |times|
cut_all_leaves
end
nodes =3D Array.new()
travel().each do |node|
if node.is_leaf?
nodes.push(node)
end
end
return nodes
end

private
def insert_at(subroot, node)
if subroot =3D=3D nil
subroot =3D node
else
if subroot.value >=3D node.value
subroot.left_node =3D insert_at(subroot.left_node, node)
else
subroot.right_node =3D insert_at(subroot.right_node, node)
end
end
return subroot
end

def travel_at(subroot, order)
if subroot =3D=3D nil
return
end
case order
when :preorder
@sorted_nodes.push( subroot )
travel_at( subroot.left_node, order )
travel_at( subroot.right_node, order )
when :inorder
travel_at( subroot.left_node, order)
@sorted_nodes.push( subroot )
travel_at( subroot.right_node, order )
when :postorder
travel_at( subroot.left_node, order )
travel_at( subroot.right_node, order)
@sorted_nodes.push( subroot )
end
end


end
 
J

Jeremy Bopp

Hi, guys.

I'm writing a Node class for a Binary Tree.
Coding comes below.

There's a method remove_leaf.
Now I want to do it this way:
=begin
[@left_node, @right_node].each do |node|
if node.is_leaf?
node = nil
end
end
=end

But it doesn't work.
Is there a way to do it ?

In your example, node is just a reference to the same objects referenced
by @left_node and @right_node. Assigning nil to node only discards the
reference held by node. It looks like you're trying to do this too much
the C/C++ way, where you would have a pointer to a pointer that you
could then nullify.

The alternative code you're already using (where you unrolled this loop)
is probably the easiest and most straightforward way to deal with this
issue.

-Jeremy
 
R

Robert Klemme

Hi, guys.

I'm writing a Node class for a Binary Tree.
Coding comes below.

There's a method remove_leaf.
Now I want to do it this way:
=begin
[@left_node, @right_node].each do |node|
if node.is_leaf?
node = nil
end
end
=end

But it doesn't work.
Is there a way to do it ?

In your example, node is just a reference to the same objects referenced
by @left_node and @right_node. Assigning nil to node only discards the
reference held by node. It looks like you're trying to do this too much
the C/C++ way, where you would have a pointer to a pointer that you
could then nullify.

The alternative code you're already using (where you unrolled this loop)
is probably the easiest and most straightforward way to deal with this
issue.

OP, you can somewhat simplify the code

Node = Struct.new :value, :left, :right do

# We usually do not use prefix "is" which
# you might be used to in Java.
def leaf?
@left.nil? && @right.nil?
end

# Actually this will remove all leaves hence the
# changed name.
def remove_leaves
self.left = nil if left && left.leaf?
self.right = nil if right && right.leaf?
self
end

def to_s; value.to_s end

# Useful for recursive algorithms.
def visit(val = nil, &b)
val = @left.visit(val, &b) if @left
val = b[self, val]
val = @right.visit(val, &b) if @right
val
end
end

:)

Have fun!

Btw, you original implementation had a flaw because it would break if a
Node had only one child. For the other the test ".is_leaf?" would fail
because NilClass does not have that method.

Kind regards

robert
 
W

w_a_x_man

Node = Struct.new :value, :left, :right do

   # We usually do not use prefix "is" which
   # you might be used to in Java.
   def leaf?
     @left.nil? && @right.nil?
   end

Will that work? Or would it have to be

self.left.nil? && self.right.nil?
 
R

Robert Klemme

Will that work? Or would it have to be

=A0 =A0self.left.nil? && self.right.nil?

No, my code won't work. But this is sufficient:

left.nil? && right.nil?

It also needs to be

def visit(val =3D nil, &b)
val =3D left.visit(val, &b) if left
val =3D b[self, val]
val =3D right.visit(val, &b) if right
val
end

Thanks for catching this!

Kind regards

robert


--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
 
S

salamond

Thanks Jeremy, wax, and robert.

I wanner do this because this tree may not be a binary tree.
What if a node could link to a dozen nodes.

we have to something like this.
nodes.each do |node|
node =3D nil
end



Will that work? Or would it have to be

=A0 =A0self.left.nil? && self.right.nil?

No, my code won't work. =A0But this is sufficient:

left.nil? && right.nil?

It also needs to be

=A0def visit(val =3D nil, &b)
=A0 val =3D left.visit(val, &b) if left
=A0 val =3D b[self, val]
=A0 val =3D right.visit(val, &b) if right
=A0 val
=A0end

Thanks for catching this!

Kind regards

robert
 
J

Jesús Gabriel y Galán

Thanks Jeremy, wax, and robert.

I wanner do this because this tree may not be a binary tree.
What if a node could link to a dozen nodes.

In that case it's probable that you have another level of indirection,
and not have each child node directly as an instance variable. More
likely something like:

class Node
attr_reader :children

def initialize
@children = []
end

def add_children node
@children << children
end

def remove_leaves
@children.delete_if {|node| node.leaf?}
end
end

Missing some pieces, but you get the idea...

Jesus.
 
R

Robert Klemme

Please do not top post.

I wanner do this because this tree may not be a binary tree.
What if a node could link to a dozen nodes.

In that case you would have a single collection (e.g. an Array) which
stores child nodes! For that use Jesus's solution.
we have to something like this.
nodes.each do |node|
=A0node =3D nil
end

This won't work because you only assign to the block parameter and
leave "nodes" unchanged.

Cheers

robert

--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
 
S

salamond

Let's see if I get this.
While I'm passing @left_node, and @right_node into this block,
I'm passing the reference of these 2 objects into a block.
Inside block, I can use these 2 references to modify the objects,
but I can't change the reference themselves.

Now here comes an update, is there a way to have the class destroy itself?

like
class Node
#snip
def destroy
self =3D nil
end
end

[@left_node, @right_node].each do |node|
node.destroy
end

Is it possible to have something like this work?
When I try this, it said can't change the value of self.
 
R

Robert Klemme

Let's see if I get this.
While I'm passing @left_node, and @right_node into this block,
I'm passing the reference of these 2 objects into a block.
Inside block, I can use these 2 references to modify the objects,
but I can't change the reference themselves.
Exactly.

Now here comes an update, is there a way to have the class destroy itself=
?

No. Please note Ruby !=3D C++.
like
class Node
#snip
=A0 =A0def destroy
=A0 =A0 =A0 =A0self =3D nil
=A0 =A0end
end

[@left_node, @right_node].each do |node|
=A0 =A0node.destroy
end

Is it possible to have something like this work?
When I try this, it said can't change the value of self.

Which is the truth. Please read on object finalization in Ruby.

robert

--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
 
J

Jesús Gabriel y Galán

Let's see if I get this.
While I'm passing @left_node, and @right_node into this block,
I'm passing the reference of these 2 objects into a block.
Inside block, I can use these 2 references to modify the objects,
but I can't change the reference themselves.

Now here comes an update, is there a way to have the class destroy itself= ?

like
class Node
#snip
=A0 =A0def destroy
=A0 =A0 =A0 =A0self =3D nil
=A0 =A0end
end

[@left_node, @right_node].each do |node|
=A0 =A0node.destroy
end

Is it possible to have something like this work?
When I try this, it said can't change the value of self.

I think you are confused about how variables and objects work in Ruby.
Variables (local or instance) point to objects. You use a variable to
send messages to the object it references. You can have many variables
pointing to the same object, and an object doesn't know which
variables pointing to itself exist. So, @left_node and node are two
such variables that, in the first iteration of the loop, are pointing
to the same object. You can use the variable node to send messages to
the object, but that can never affect which is object is referenced by
@left, it can only modify the internal state of the object.

An object cannot destroy itself. The only way to destroy an object is
to remove all references to it, and eventually the garbage collector
will take care of it. So, what you need to do here is to change the
@left variable, making it point to nil, so that the original object is
not pointed to anymore.

If you have a collection of child nodes, then do as a I explained
before: delete from the array the chosen objects so they are not
pointed from the array anymore.

Jesus.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,061
Latest member
KetonaraKeto

Latest Threads

Top