Attempting to learn Ruby as my first programming language. I was understanding everything fine to this point, but I am drawing a blank at this exercise and not sure where to even get started with this and could use some help.
9: Introducing || operator
Similar to the logical and operator &&, we have a logical or operator ||. The character | is located right above your enter key on the keyboard and is called a pipe. Similar to double ampersand, this one is also commonly referred to as double pipe. The || operator is used if any of the conditions can be true. For example - if number == 1 || number == 3 returns true if number is either 1 or 3. Simple right? Implement a method dinner_choice as per the instructions in the code editor.
This function should return:
"steak" if celebrity is "brad pitt" or "angelina jolie"
"italian" if celebrity is "ashton kutcher" or "demi moore"
"french" if celebrity is none of the above
This is my attempt.
def dinner_choice(celebrity)
return "steak" if celebrity == "brad pitt" || celebrity == "angelina jolie"
return "italian" if celebrity == "ashton kutcher" || celebrity == "demi moore"
else return "french"
end
end
You were close, this is what you want
def dinner_choice(celebrity)
return "steak" if celebrity == "brad pitt" || celebrity == "angelina jolie"
return "italian" if celebrity == "ashton kutcher" || celebrity == "demi moore"
return "french"
end
You don't want the last else at the end. You can simply use return "french" because if any of the statements above it match, it won't get to that line.
The general form you're looking for is to write statements like this:
def dinner_choice celebrity
return "steak" if celebrity == "Brad Pitt"
return "foo" if # add programming here
end
You'll check the celebrity variable to see if it's something your method knows how to handle, and give the result that goes with the input.
The explicit return tells the Ruby interpreter to stop evaluating the rest of the code in the method and give the result specified.
You could also experiment with case statements, but IMO they're ugly and hard to maintain in most situations.
Related
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
I have the following code:
until (#world.exists? decision || decision == '')
UiHandler.print_error(UiHandler::NO_TILE)
UiHandler.print_turn_message
decision = gets.chomp
end
which should allow the player to skip a turn by entering an empty line. But for some reason the until loop keeps running even when the condition is true
i.e. passing in '1 1' does work and stop the loop, since it exists in world, but passing nothing doesn't, even though puts (#world.exists? decision || decision == '') gives 'true'
What would cause an until loop not to stop even when the condition is met?
Fix is
(#world.exists?(decision) || decision == '')
Otherwise - #world.exists? decision || decision == '' is being treated as #world.exists?(decision || decision == ''), which is not correct expression, you intended to write.
As decision is a string object, which in Ruby is considered as truth value, decision || decision == '' (in the code written by you) will be evaluated as true too. This decision will be passed as a method argument to the method #world.exists? always.
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.
It's a bit confusing to me about what is the difference between these condition expressions below:
if( 1 == a) {
//something
}
and
if( a == 1 ) {
//something
}
I saw the above one in some scripts I have downloaded and I wonder what's the difference between them.
The former has been coined a Yoda Condition.
Using if(constant == variable) instead of if(variable == constant), like if(1 == a). Because it's like saying "if blue is the sky" or "if tall is the man".
The constant == variable syntax is often used to avoid mistyping == as =. It is, of course, often used without understanding also when you have constant == function_call_retuning_nothing_modifiable.
Other than that there's no difference, unless you have some weird operator override.
Many programming languages allow assignments like a = 1 to be used as expressions, making the following code syntactically valid (given that integers can be used in conditionals, such as in C or many scripting languages):
if (a = 1) {
// something
}
This is rarely desired, and can lead to unexpected behavior. If 1 == a is used, then this mistake cannot occur because 1 = a is not valid.
Well, I am not sure about the trick. Generally, we could say the equal sign is commutative. So, a = b implies b = a. However, when you have == or === this doesn't work in certain cases, for example when on the right side you have a range: 5 === (1..10) vs. (1..10) === 5.
I found this problem and I am not very sure if my approach approach is right:
"A binary tree can be encoded using
two functions l and r such that for a
node n, l(n) gives the left child of
n(or nil if there is none) and r(n)
gives the right child(or nil if there
is none).Let Test(l,r,x) be a simple
recursive algorithm for taking a
binary tree encoded by the l and r
functions together with the root node
x for the binary tree, and returns
"yes" if no node in the tree has
exactly one child. Give the pseudocode
for this algorithm."
I tried this:
test(l,r,x)
if((l(x)!=null && r(x)!=null) || (l(x)==null && r(x)==null))
return "yes"
else return "no"
if(test(l,r,l(x))=="yes") test (l,r,l(x)) else return "no"
if(test(l,r,r(x))=="yes") test (l,r,r(x)) else return "no"
return "yes"
Is it correct? If l and r are functions, why are they passed as normal parameters when the function is called?
Thank you in advance for your answers!
You have three basic conditions: both children are null, one child is null, or neither child is null. I'd test them in that order as well (because it simplifies the logic a bit):
if l(x) == null && r(x) == null
return true;
if l(x) == null || r(x) == null // only need inclusive OR, due to previous test.
return false;
return test(l, r, l(x)) && test(l, r, r(x))
As far as passing l and r as parameters goes, it all depends: some languages (e.g., functional languages) allow you to pass a function as a parameter. Others (e.g., C, C++, etc.) have you pass a pointer to a function instead -- but it's pretty much irrelevant. A few won't let you pass anything like a function, in which case you'd have to hardwire it in. In this case, you're not really gaining much (anything?) by passing l and r as parameters anyway. Passing a function as a parameter is useful primarily when/if the receiving function doesn't know what function it's going to receive a priori (which it does here).
the first thing you do is either return yes or no, so the last part is unreachable.
I would change it so that if you have one child, you return no, otherwise you return yes or no depending on if your children meet the criteria.
test(l,r,x)
if((l(x)!=null && r(x)==null) || (l(x)==null && r(x)!=null))
return "no"
if(l(x) == null && r(x) == null)
return "yes"
return test(l,r,l(x)) && test(l,r,r(x))
I don't think this is correct. The problem I see is in the first step:
if((l(x)!=null && r(x)!=null) || (l(x)==null && r(x)==null))
return "yes"
else return "no"
The problem is that you cannot determine the 'yes' for the entire tree at the first step. What you have to do is break it up into components:
if this node has both children
return the result of test(l,r,l(x)) && (test(l,r,r(x))
if this node has no children
return true
if this node has 1 child
return false
as per your last question ("If l and r are functions, why are they passed as normal parameters when the function is called?"), the answer is that they don't have to be passed as parameters. That's just the notation they chose when they said "A binary tree can be encoded using two functions l and r [...]"