Extending (Monkey Patching) a Binary Search for Array Class and Syntactic Sugar - ruby

I've been studying a few searching algorithms and my last problem comes down to binary searching. I watched a few youtube videos to understand the concept and then tried to solve the problem, but keep getting an endless loop error. I've looked through stack overflow, and reddit, and wherever Google would lead me, but I can't quite find a solution that fits my method of coding. Also, please excuse the term 'monkey patching', it's been brought to my attention that the technical term is called 'extending' so the fault lies on my instructors for teaching it to us as 'monkey patching'.
Here's my code:
class Array
def my_bsearch(target)
return nil if self.empty?
middle_idx = self.length/2
left = self.take(middle_idx)
right = self.drop(middle_idx + 1)
return middle_idx if self[middle_idx] == target
until self[middle_idx] == target || self.nil? == nil
if self[middle_idx] < target
right.my_bsearch(target)
elsif self[middle_idx] > target
left.my_bsearch(target)
end
end
end
end
I have a solution, but I don't want to just memorize it-- and I'm having trouble understanding it; as I'm trying to translate it, learn from it, and implement what I'm missing into my own code.
class Array
def my_bsearch(target)
return nil if size == 0
mid = size/2
case self[mid] <=> target
when 0
return mid
when 1
return self.take(mid).my_bsearch(target)
else
search_res = self.drop(mid+1).my_bsearch(target)
search_res.nil? ? nil : mid + 1 + search_res
end
end
end
I guess I understand case/when despite not use to using it. I've tried following it with debugger, but I think I'm hung up on what's going on in the ELSE section. The syntactic sugar, while making this obviously more concise than my logic, isn't straight-forward/clean to someone of my ruby literacy level. So, yeah, my ignorance is most of the problem I guess.
Is there someone who is a little more literate, and patient, able to help me break this down into something I can understand a bit better so I can learn from this?

First, take and drop have sufficiently similar interfaces that you don't actually want your + 1 for drop. It will disregard one element in the array if you do.
Next, self.nil? will always be false (and never nil) for instances of this class. In fact, .nil? is a method exactly to avoid having to ever compare against nil with ==.
You want self.empty?. Furthermore, with the exception of setters, in Ruby messages are sent to self by default. In other words, the only time self. is a useful prefix is when the message ends in = and operates as an lvalue, as in self.instance_var = 'a constant', since without the self., the tokens instance_var = would be interpreted as a local variable rather than an instance variable setting. That's not the case here, so empty? will suffice just as well as self.empty?

So I figured it out, and I decided to answer my own post in hopes to help someone else out if they run into this issue.
So, if I have an Array and the target is the middle_element, then it will report middle_element_idx. That's fine. What if the target is less than middle_element? It recursively searches the left-side of the original Array. When it finds it, it reports the left_side_idx. There's no problem with that because elements in an array are sequentially counted left to right. So, it starts at 0 and goes up.
But what if the target is on the right side of the middle element?
Well, searching for the right side is easy. Relatively the same logic as searching left. Done recursively. And it will return a target_idx if it's found on that right side --however that's the target's idx as it was found in the right-side array! So, you need to take that returned target_idx and add 1 to it and the original middle_element_idx. See below:
def my_bsearch(target)
return nil if self.empty?
middle_idx = self.length/2
left = self.take(middle_idx)
right = self.drop(middle_idx + 1)
if self[middle_idx] == target
return middle_idx
elsif self[middle_idx] > target
return left.my_bsearch(target)
else
searched_right_side = 1 + right.my_bsearch(target)
return nil if searched_right_side.nil? == true
return searched_right_side + middle_idx
end
end
end
Notice how many more lines this solution is? The spaceship operator used in conjunction with case/when and a ternary method will reduce the number of lines significantly.
Based on suggestions/feedback from Tim, I updated it to:
def my_bsearch(target)
return nil if empty?
middle_idx = self.length/2
left = self.take(middle_idx)
right = self.drop(middle_idx)
if self[middle_idx] == target
return middle_idx
elsif self[middle_idx] > target
return left.my_bsearch(target)
else
searched_right_side = right.my_bsearch(target)
return nil if searched_right_side.nil?
return searched_right_side + middle_idx
end
end
end

Related

Julia: define method for custom type

I apologize if this has been answered before, I didn't find a straight answer for this among my search results.
I'm going through "Learn Julia The Hardway" and I can't really find where's the difference in my code vs the example in the book. Whenever I run it I get the following error:
TypeError: in Type{...} expression, expected UnionAll, got a value of type typeof(+)
Here's the code:
struct LSD
pounds::Int
shillings::Int
pence::Int
function LSD(l,s,d)
if l<0 || s<0 || d<0
error("No negative numbers please, we're british")
end
if d>12 error("That's too many pence") end
if s>20 error("That's too many shillings") end
new(l,s,d)
end
end
import Base.+
function +{LSD}(a::LSD, b::LSD)
pence_s = a.pence + b.pence
shillings_s = a.shillings + b.shillings
pounds_s = a.pounds + b.pounds
pences_subtotal = pence_s + shillings_s*12 + pounds_s*240
(pounds, balance) = divrem(pences_subtotal,240)
(shillings, pence) = divrem(balance,12)
LSD(pounds, shillings, pence)
end
Another quick question, I haven't got yet to the functions chapter, but it catches my attention that there is no "return" at the end of the functions, I'm guessing that if it isn't stated a function will return the last evaluated value, am I right?
This appears to be using very old Julia syntax (I think from version 0.6). I think you just want function +(a::LSD, b::LSD)

problems implementing negamax for tic-tac-toe in ruby

I'm banging my head against the wall trying to implement negamax for tic-tac-toe
def negamax(board_obj, mark, depth)
if board_obj.game_over?
return value(board_obj)
else
max = -1.0/0 # negative infinity
if mark == #mark
next_mark = #opponent_mark
else
next_mark = #mark
end
board_obj.empty_squares.each do |square|
board_obj[square] = mark
x = -negamax(board_obj, next_mark, depth + 1)
board_obj[square] = ' '
if x > max
max = x
#scores << x
#best_move = square if depth == 1
end
end
return max
end
end
# determines value of final board state
def value(board_obj)
if board_obj.mark_win?(#mark)
return 1
elsif board_obj.mark_win?(#opponent_mark)
return -1
else
return 0
end
end
the rest of the code is here: https://github.com/dave-maldonado/tic-tac-doh/blob/AI/tic-tac-doh.rb
It does produce a result but the AI is easily beat so I know something's wrong, any help
is appreciated!
The problem is that value needs to be relative to the mark in the current execution of negamax rather than always relative to the computer. If you pass in the mark argument to value from negamax with the following modified definition for value, you'll get the right results:
def value(board_obj, mark)
if board_obj.mark_win?(mark)
return 1
elsif board_obj.mark_win?(mark == 'X' ? 'O' : 'X')
return -1
else
return 0
end
end
That is, the first two lines of the negamax body need to be:
if board_obj.game_over?
return value(board_obj, mark)
That said, this overall program leaves an awful lot to be desired relative to Ruby, good design principles, etc (no offense intended). Now that you have it running, you might want to head over to the Code Review SE for some feedback. :-) And while it's too late to use TDD ;-), it would also be a good one to put "under test".
Also, please understand that per one of the other comments, this is not a kind of question that you'll typically get an answer to here at SO. I don't even know if this question will survive the review process without getting deleted. I worked on it for a variety of personal reasons.
Update: Looking at your reference implementation, you'll note that the negamax code includes the expression sign[color]*Analysis(b). It's that sign[color] that you were missing, effectively.

Unexpected $end after while loop and nested if

I have this program that I am working on that is supposed to find the sum of the first 1000 prime numbers. Currently all I am concerned with is making sure that the program is finding the first 1000 prime numbers, I will add the functionality for adding them later. Here is what I have:
#!/usr/bin/ruby
def prime(num)
is_prime = true
for i in 2..Math.sqrt(num)
if (num % i) == 0
is_prime = false
else
is_prime = true
end
end
return is_prime
end
i = 2
number_of_primes = 0
while number_of_primes < 1000
prime = prime(i)
if prime == true
number_of_primes++
end
i++
end
When i try to run the program I get the following feedback:
sumOfPrimes.rb:32: syntax error, unexpected keyword_end
sumOfPrimes.rb:34: syntax error, unexpected keyword_end
what gives? Any direction is appreciated.
Ruby doesn't have ++ operator, you need to do += 1
number_of_primes += 1
Unasked for, but a few pieces of advice if you're interested:
One of the cool things about Ruby is that question marks are legal in method names. As such you'll often find that 'predicate' methods (methods that test something and return true or false) end with a question mark, like this: odd?. Your prime method is a perfect candidate for this, so we can rename it prime?.
You use a local variable, is_prime, to hold whether you have found a factor of the number you're testing yet - this is the kind of thing you'd expect to do in an imperative language such as java or C - but Ruby has all sorts of cool features from functional programming that you will gain great power and expressiveness by learning. If you haven't come across them before, you may need to google what a block is and how the syntax works, but for this purpose you can just think of it as a way to get some code run on every item of a collection. It can be used with a variety of cool methods, and one of them is perfectly suited to your purpose: none?, which returns true if no items in the collection it is called on, when passed to the code block you give, return true. So your prime? method can be rewritten like this:
def prime? num
(2..Math.sqrt(num)).none? { |x| num % x == 0 }
end
Apart from being shorter, the advantage of not needing to use local variables like is_prime is that you give yourself fewer opportunities to introduce bugs - if for example you think the contents of is_prime is one thing but it's actually another. It's also, if you look carefully, a lot closer to the actual mathematical definition of a prime number. So by cutting out the unnecessary code you can get closer to exposing the 'meaning' of what you're writing.
As far as getting the first 1000 primes goes, infinite streams are a really cool way to do this but are probably a bit complex to explain here - definitely google if you're interested as they really are amazing! But just out of interest, here's a simple way you could do it using just recursion and no local variables (remember local variables are the devil!):
def first_n_primes(i = 2, primes = [], n)
if primes.count == n then primes
elsif prime? i then first_n_primes(i + 1, primes + [i], n)
else first_n_primes(i + 1, primes, n)
end
end
And as far as summing them up goes all I'll say is have a search for a ruby method called inject - also called reduce. It might be a bit brain-bending at first if you haven't come across the concept before but it's well worth learning! Very cool and very powerful.
Have fun!

Copying objects in an array before removing it

The code below loops through an array factor_list and checks if each of them contains a specified variable. If so, remove them from the array, multiply them together, and sum out the final result with respect to the variable. After all the operations, add the factor back to the array.
temp_factor = nil
factor_list.each{|factor|
if factor._variables.include?(variable)
if temp_factor == nil
temp_factor = factor
else
temp_factor = multiply(temp_factor, factor)
end
factor_list.delete(factor)
end
}
temp_factor = sumOut(temp_factor, variable)
factor_list << temp_factor
The problem is, temp_factor is always nil in every iteration, even if it has been set in a previous loop. I thought the main problem was because of the deletion in the array, so I removed the deletion for testing, and that solved the problem (but my array is full of trash of course). So I came to the conclusion that my temp_factor was a shallow copy of the object, and so its referencing object is gone with the original one. I then tried doing a deep copy using the marshal trick, but it didn't help.
That was all I have got, as I was unable to solve the problem. Can anyone help me identify the mechanism behind all these myths?
It's cool that you guys give very nice advice about rewriting the code to avoid problems, and they really help! But I'm still wondering what caused the above problem? It would be nice if I can learn that bit of information!
College years with C++ and STL had taught me hard: never ever modify a collection that you're iterating at the moment. So, instead of deleting items in place, why don't you try to build a new array?
temp_factor = nil
new_factors = factor_list.map do |factor|
if factor._variables.include?(variable)
if temp_factor == nil
temp_factor = factor
else
temp_factor = multiply(temp_factor, factor)
end
nil
else
factor
end
end.compact
temp_factor = sumOut(temp_factor, variable)
factor_list = new_factors + [temp_factor]
I'm really not sure I understand what you're trying to achieve with the code, so it might help if you could give more of a description of what you want the end result to be and why, but based on what I think you're trying to do, I think you can get much greater clarity by splitting it into a few steps:
# first, split the array into an array that matches and one that doesn't
matched, factors = factor_list.partition { |f| f._variables.include? variable }
# then call multiply with each element of the matched list on the result of
# calling it on the previous elements
temp_factor = matched.reduce { |acc, f| multiply(acc, f) }
temp_factor = sumOut(temp_factor, variable)
factor_list = factors << temp_factor
I would also reconsider having so many temp variables that are frequently reassigned, as it becomes very difficult to keep track of what their values are at any point - and so very easy to introduce bugs.

Can't convert nil into string--Ruby Secret Santa

I wrote a Secret Santa program (ala Ruby Quiz...ish), but occasionally when the program runs, I get an error.
Stats: If there's 10 names in the pot, the error comes up about 5% of the time. If there's 100 names in the pot, it's less than 1%. This is on a trial of 1000 times in bash. I've determined that the gift arrays are coming up nil at some point, but I'm not sure why or how to avoid it.
Providing code...
0.upto($lname.length-1).each do |i|
j = rand($giftlname.length) # should be less each time.
while $giftlname[j] == $lname[i] # redo random if it picks same person
if $lname[i] == $lname.last # if random gives same output again, means person is left with himself; needs to switch with someone
$giftfname[j], $fname[i] = $giftfname[i], $fname[j]
$giftlname[j], $lname[i] = $giftlname[i], $lname[j]
$giftemail[j], $email[i] = $giftemail[i], $email[j]
else
j = rand($giftlname.length)
end
end
$santas.push('Santa ' + $fname[i] + ' ' + $lname[i] + ' sends gift to ' + $giftfname[j] + ' ' + $giftlname[j] + ' at ' + '<' + $giftemail[j] + '>.') #Error here, something is sometimes nil
$giftfname.delete_at(j)
$giftlname.delete_at(j)
$giftemail.delete_at(j)
end
Thanks SO!
I think your problem is right here:
$giftfname[j], $fname[i] = $giftfname[i], $fname[j]
Your i values range between zero to the last index in $fname (inclusive) and, presumably, your $giftfname starts off as a clone of $fname (or at least another array with the same length). But, as you spin through the each, you're shrinking $giftfname so $giftfname[i] will be nil and the swap operation above will put nil into $giftfname[j] (which is supposed to be a useful entry of $giftfname). Similar issues apply to $giftlname and $giftemail.
I'd recommend using one array with three element objects (first name, last name, email) instead of your three parallel arrays. There's also a shuffle method on Array that might be of use to you:
Start with an array of people.
Make copy of that array.
Shuffle the copy until it is different at every index from that original array.
Then zip the together to get your final list of giver/receiver pairs.
Figured it out and used the retry statement. the if statement now looks like this (all other variables have been edited to be non-global as well)
if lname[i] == lname.last
santas = Array.new
giftfname = fname.clone
giftlname = lname.clone
giftemail = email.clone
retry
That, aside from a few other edits, created the solution I needed without breaking apart the code too much again. Will definitely try out mu's solution as well, but I'm just glad I have this running error-free for now.

Resources