I'm writing a recursive toString method in a binary tree class in Ruby. The algorithm uses two methods as shown below. I have a local variable called result, which is to hold the concatenation of all the values in the tree. For some odd reason, when I concatenate result in the toString algorithm, the root of the tree changes.
def toString
currentNode = #root
result = "<"
if nil != currentNode
result << (getString currentNode, result)
end
result = result + ">"
return result
end
def getString(currentNode, result)
if currentNode != nil
result = currentNode.value.toString
if nil != currentNode.left
result << "," # Through debugging, I have noticed that it starts here!!!!
result.concat (self.getString currentNode.left, result)
end
if nil != currentNode.right
result << ","
result.concat (self.getString currentNode.right, result)
end
end
return result
end
So, where I have noted in the comment above, the value at the root of the tree begins to change. Instead of concatenating with result, it concatenates with the root, which changes the tree, instead of simply traversing and concatenating the values. I have used << and concat. Same result. Could someone please help shed some light on this? I am fairly new to Ruby. Thank you. :)
Contrary to other languages (like java or c#) Strings in ruby are not immutable (by default). This means that if currentNode.value.toString returns the actual string value of the value (and not a copy of it) calling << on it will change the value itself:
class Dummy
attr_accessor :value
end
dummy = Dummy.new
dummy.value = 'test'
result = dummy.value
result << 'something else'
# => "testsomething else"
dummy.value
# => "testsomething else" # !!!!
To avoid that you need to make sure you return a duplicate of the value, and not the value itself.
dummy.value = 'test'
result = dummy.value.dup
result << 'something else'
# => "testsomething else"
dummy.value
# => "test" # :-)
Related
I was trying to implement a ruby binary search tree, creating, adding and printing node is fine, the problem rose when i was implementing #delete
(i will post the code below) the structure for this tree is nested binary nodes (why? I am no nothing about ruby pointers)
class BinaryNode
attr_accessor :value
attr_accessor :left_node
attr_accessor :right_node
def initialize (value = nil, left_node = nil, right_node = nil)
#value = value
#left_node = left_node
#right_node = right_node
end
end
the left and right nodes will be another binary node, and it goes on
so when i want to insert a node (which is fine), i use a temp binary node to traverse the tree and when i reach my goal (if no duplicates encountered) I simply make the temp's child (left or right according to comparison), but why this works, i mean i copied the binary node, AND modified the COPIED one, but works,
here is the #insert if you need more insight
def insert (value, node = #root)
if value == node.value
return nil
elsif value > node.value
if node.right_node == nil
node.right_node = BinaryNode.new(value)
else
insert value, node.right_node
end
else
if node.left_node == nil
node.left_node = BinaryTree.new(value)
else
insert value, node.left_node
end
end
end
now when i apply the same logic for deleting node (currently stuck with leaves, have not yet explored and tested other cases) it fail, here is the code if my statement is not sufficient
def delete (value)
temp = #root
sup_node = temp
while temp.value != value
puts "currently at #{temp.value}"
if temp.value > value
temp = temp.left_node
puts "going left"
elsif temp.value < value
temp = temp.right_node
puts "going right"
end
target_node = temp
puts "target_node: #{target_node.value}"
end
if target_node.right_node == nil
puts "right node is nil"
if target_node.left_node == nil
puts "left node is nil"
puts "deleting node"
target_node = nil
else
temp_node = target_node.left_node
target_node.left_node = nil
target_node = temp_node
end
else
target_node_right = target_node.right_node
last_left_node = target_node_right
while last_left_node.left_node != nil
last_left_node = last_left_node.left_node
end
if last_left_node.right_node == nil
target_node.value = last_left_node.value
last_left_node = nil
else
last_left_parent_node = target_node_right
while last_left_parent_node.left_node != last_left_node
last_left_parent_node == last_left_parent_node.left_node
end
#some chaos going on here
last_left_parent_node.right_node = last_left_node.right
last_left_parent_node.left_node = nil
target_node.value = last_left_node.value
last_left_node = nil
end
end
end
My main question why an approach works fine in one situation but break in another, and how ruby can track copied data, and modify original, I am not interested in the binary tree algorithms it self (any problem probably will be easily searchable)
Thanks in advance
by the way sorry for being long, if you want the whole code (although I think what i copied is sufficient) you can find it on github
newbie ruby question
code challenge: string collapse: remove all adjacent duplicates in a given string. Note: removal of adjacent duplicates can create new adjacent duplicates, you must remove these as well.
I'm pretty sure I solved this with simple recursion but when I walk through this step by step in a debugger the code doesn't terminate when it reaches the return line and begins adding then removing letters to the string until finally terminating.
for example
zzzxaaxy => zxaaxy => zxxy => zy => zxxy => zy => zxxy =>
^ code hits return line here and should stop but instead continues
def same_char_collapse(str)
ar = str.split('')
ar.map.each_with_index do |char1, idx|
char2 = ar[idx+1]
if char1 == char2
ar.delete_at(idx)
ar.delete_at(idx)
same_char_collapse(ar.join(''))
end
end
return ar.join('')
end
The return keyword does not mean "terminate instantly and return my value", it means "return to the method that called me."
So when you call your method and it returns "zxxy" instead of "zy", it is because it is just returning the result of ar.join() in the first call to same_char_collapse.
def same_char_collapse(str)
ar = str.split('')
ar.map.each_with_index do |char1, idx|
char2 = ar[idx+1]
if char1 == char2
ar.delete_at(idx)
ar.delete_at(idx)
# same_char_collapse(ar.join('')) doesn't change the value we are returning.
# you could delete this line and the method would still return the same thing 'zxxy'
same_char_collapse(ar.join(''))
end
end
# ar's value is zxxy from the loop since the same_char_collapse(ar.join('')) line
# doesn't change ar. only the ar.delete_at(idx) lines modified ar.
return ar.join('')
end
same_char_collapse('zzzxaaxy') # first call to same_char_collapse
Essentially, if you want to fix your solution, you should make sure you are using the value produced in the recursive call.
thanks for the help and after lots of trial and error I eventually worked
out a solution
def dupes?(word)
word.each_char.with_index do |char, idx|
if char == word[idx+1]
return true
end
end
return false
end
def removeDupe(word)
ar = word.split('')
ar.map.with_index do |char,idx|
if char == ar[idx+1]
ar.delete_at(idx)
ar.delete_at(idx)
end
end
return ar.join('')
end
def same_char_collapse(str)
if dupes?(str)
same_char_collapse(removeDupe(str))
else
return str
end
end
I created a method to count a substring 'e' in a string passed as an argument. If there isn't a substring 'e' in the string, it should return "There is no \"e\"." I am trying to achieve this:
How many times 'e' is in a string.
If given string doesn't contain any "e", return "There is no "e"."
if given string is empty, return empty string.
if given string is nil, return nil.
This is my code:
def find_e(s)
if !s.include?("e")
"There is no \"e\"."
elsif s.empty?
""
else s.nil?
nil
end
s.count("e").to_s
end
find_e("Bnjamin")
It skips the if statement and it still uses the method count. Why is this?
To achieve what you want you could move your string.count to the else statement in your if, because actually you're making your method return the quantity of e passed in the count method over your string, but what happens inside the if isn't being used:
def find_e(s)
if s.nil?
nil
elsif s.empty?
''
elsif !s.include?("e")
"There is no \"e\"."
else
s.count("e").to_s
end
end
p find_e("Bnjamin") # => "There is no \"e\"."
p find_e("Benjamin") # => "1"
p find_e(nil) # => nil
p find_e('') # => ""
And also your validations must be in order, first check nil values, then empty values, and then the rest, if you don't then you'll get some undefined method ___ for nil:NilClass errors.
You might have a hard time using the method you wrote. In the next method, you'll need a new case statement to test if find_e returned nil, an empty string, a string with a number or "no e".
This method would be a bit more consistent:
def count_e(string_or_nil)
count = string_or_nil.to_s.count("e")
if count == 0
"There is no \"e\"."
else
count
end
end
puts count_e("Covfefe")
# 2
puts count_e("Bnjamin")
# There is no "e".
puts count_e("")
# There is no "e".
puts count_e(nil)
# There is no "e".
But really, if there's no e in the input, just returning 0 would be the most logical behaviour.
You need to put your count method in a branch of the if/else statement, or else it will be evaluated last every time. Without an explicit return statement Ruby will return the last statement, so putting the method outside the if/else branch on the last line guarantees it will always be hit. Also, nil can be converted to an empty string by calling #to_s, so you can remove one of your branches by converting s.to_s, calling empty? and returning s
def find_e(s)
if s.to_s.empty?
s
elsif !s.include?("e")
"There is no \"e\"."
else
s.count("e").to_s
end
end
If you just return 0 whether you get nil, an empty string, or a string without e, you can make it one line
def find_e(s)
s.to_s.count("e").to_s
end
If it were me I'd probably return an Integer, which can always be converted to a String later. puts and "#{}" will implicitly call to_s for you anway. Then you can use that integer return in your presentation logic.
def count_e(input)
input.to_s.count("e")
end
def check_for_e(input)
count = count_e(input)
count > 0 ? count.to_s : "There's no \"e\"."
end
check_for_e("Covfefe") # => "2"
check_for_e("Bnjamin") # => "There's no \"e\"."
check_for_e(nil) # => "There's no \"e\"."
check_for_e("") # => "There's no \"e\"."
In Ruby, methods return the last statement in their body. Your method's last statement is always s.count("e").to_s, since that lies outside of the if statements.
This function is supposed to take a string and return the characters in reverse order.
def reverse(string)
reversedString = "";
i = string.length - 1
while i >= 0
reversedString = reversedString + string[i]
i -= 1
end
puts reversedString
end
however all the tests return false:
puts(
'reverse("abc") == "cba": ' + (reverse("abc") == "cba").to_s
)
puts(
'reverse("a") == "a": ' + (reverse("a") == "a").to_s
)
puts(
'reverse("") == "": ' + (reverse("") == "").to_s
)
Does anyone see what the problem is?
Try to use the default String class reverse method like this:
"Hello World".reverse
"Hello World".reverse!
Check Ruby's String class API at https://ruby-doc.org/core-2.4.0/String.html
If you want to make your custom method, you could use a map like this:
string = String.new
"Hello World".chars.each { | c | string.prepend c }
The problem is your function isn't returning its result, it's printing it. It needs to return reversedString.
As a rule of thumb, functions should return their result. Another function should format and print it.
def reverse(string)
reversedString = "";
i = string.length - 1
while i >= 0
reversedString = reversedString + string[i]
i -= 1
end
return reversedString
end
Note: This was probably an exercise, but Ruby already has String#reverse.
It's good that you're writing tests, but the way you're writing them it's hard to tell what went wrong. Look into a Ruby testing framework like MiniTest.
require "minitest/autorun"
class TestReverse < Minitest::Test
def test_reverse
assert_equal "cba", reverse("abc")
assert_equal "a", reverse("a")
assert_equal "", reverse("")
end
end
That would have told you that your function is returning nil.
1) Failure:
TestReverse#test_reverse [test.rb:16]:
Expected: "cba"
Actual: nil
To make this more Ruby-like yet avoid using the built-in String#reverse method you'd do this:
def reverse(string)
string.chars.reverse.join('')
end
Remember that in Ruby the result of the last operation is automatically the return value of the method. In your case the last operation is puts which always returns nil, eating your value. You want to pass it through.
Try to design methods with a simple mandate, that is, this function should focus on doing one job and one job only: reversing a string. Displaying it is beyond that mandate, so that's a job for another method, like perhaps the caller.
To avoid calling any sort of reverse method at all:
def reverse(string)
result = ''
length = string.length
length.times do |i|
result << string[length - 1 - i]
end
result
end
You can often avoid for almost completely and while frequently if you use things like times or ranges (0..n) to iterate over.
puts prints and returns nil, so the whole method returns nil. If, for debugging reasons , you want to inspect what your method is returning, use p which returns it's argument (reversedString in this case).
def reverse(string)
reversedString = ""
i = string.length - 1
while i >= 0
reversedString = reversedString + string[i]
i -= 1
end
p reversedString # !!!
end
And all 3 tests return true
If I was going to do this, I'd probably take advantage of an array:
ary = 'foo bar baz'.chars
reversed_ary = []
ary.size.times do
reversed_ary << ary.pop
end
reversed_ary.join # => "zab rab oof"
pop removes the last character from the array and returns it, so basically it's walking backwards through ary, nibbling at the end and pushing each character onto the end of reversed_ary, effectively reversing the array.
Alternately it could be done using a string:
ary = 'foo bar baz'.chars
reversed_str = ''
ary.size.times do
reversed_str << ary.pop
end
reversed_str # => "zab rab oof"
or:
reversed_str += ary.pop
I just saw that #tadman did a similar thing with the string. His would run more quickly but this is more readable, at least to my eyes.
I am in the process of learning Ruby and as practice I am making a linked list class. I am in the process of writing the delete method for a doubly linked list. My question is, if I represent the list by its head node, how do I delete the head? It seems like Ruby wont allow you to assign to the self variable, so I can't change the reference of the caller to be the next node. One solution is that I can copy the key from the next node and swap references, but in general, is there a way in Ruby to change the reference of the caller?
class LinkedListNode
attr_accessor :next, :previous, :key
def initialize(key=nil, next_node=nil, previous=nil)
#next = next_node
#previous = previous
#key = key
end
def append(key=nil)
newnode = LinkedListNode.new(key)
seeker = self
while seeker.next != nil
seeker = seeker.next
end
newnode.previous = seeker
seeker.next = newnode
end
def delete(key=nil)
seeker = self
while seeker.key != key
return if seeker.next == nil
seeker = seeker.next
end
if seeker.previous != nil
if seeker.next != nil
seeker.previous.next = seeker.next
seeker.next.previous = seeker.previous
else
seeker.previous.next = nil
end
else
return self = self.next
end
return seeker = nil
end
def print
seeker = self
string = ""
while 1
if seeker.next == nil
string += seeker.key.to_s
break
else
string += seeker.key.to_s + " -> "
end
seeker = seeker.next
end
puts string
end
end
if __FILE__ == $0
ll = LinkedListNode.new(1)
ll.append(2)
ll.append(3)
ll.append(4)
ll.append(5)
ll.print
ll.delete(5)
ll.print
ll.delete(1)
ll.print
end
You can't change the which object is being pointed to by the caller (i.e. modify self), but you can manipulate the object in any way you want, as you've already thought through. The short answer is that it can't be done. You can come up with other ways to model it, but I think you're already on the right track.
You need to conceptualize a linked list differently. A LinkedListNode is a component of a LinkedList, not a LinkedList itself. Operations such as append, delete, and print should go in your LinkedList class, not your LinkedListNode class. Try starting with something like
class LinkedList
# This one-liner defines a LinkedList::Node with associated constructor
# and accessors for the three tags provided. Any tags omitted during
# construction will be initialized to nil.
Node = Struct.new(:key, :previous, :next)
attr_reader :head, :tail
def initialize
# start with no Nodes in the list
#head = #tail = nil
end
def append(key)
# Make the LinkedList tail a new node that stores the key,
# points to the prior tail as its previous reference, and
# has no next.
#tail = Node.new(key, #tail)
if #tail.previous # the prior tail was not nil
#tail.previous.next = #tail # make the prior tail point to the new one
else # if there wasn't any tail before the list was empty
#head = #tail # so the new tail Node is also the head
end
end
# ...
end