I am making a basic hangman game that will need to keep retracing until the game is over. I'm trying to figure out the correct ruby way to go back to these lines. Here is a rough pseudocode example:
some_function
end
another_function
end
if x > 5 go back to some_function
if x < 5 go back to another_function
Functions are how you run some code somewhere else and return back to where you started. Loops are how you run the same code again and again until some condition is met.
Here is some incomplete code to demonstrate.
def some_function(x)
puts "x is #{x} some function"
end
def another_function(x)
puts "x is #{x} in another function"
end
def something_else(x)
puts "x is #{x} in something else"
end
# I've left are_we_done undefined, it's the condition for
# stopping the loop. I've also left get_x undefined.
while !are_we_done
x = get_x
if x > 5
some_function(x)
elsif x < 5
another_function(x)
else # x is 5
something_else(x)
end
Teaching these fundamentals is beyond a Stack Overflow answer. I would suggest going through a tutorial like Learn Ruby The Hard Way to learn the fundamentals of loops and functions.
Related
I have this method for a tic tac tow game:
def start(token)
if token == "X"
puts "#{#player1}: Please enter a number for your X to go"
elsif token == "O"
puts "#{#player2}: Please enter a number for your O to go"
end
player_input = STDIN.gets.chomp
locate_player_input(token, player_input)
end
I'm only trying to test to see if the correct thing is puts'd to the terminal. I have this test:
describe "#start" do
context "X player's turns" do
it "prints proper messege" do
expect(STDOUT).to receive(:puts).with("Harry: Please enter a number for your X to go")
game.start("X")
end
end
end
But I have a feeling the game.start("X") line is what is not making this work. How can I write the test to just check if the puts statement is correctly outputted?
I think I figured it out. Since my function was first puts-ing something and then calling the next method to be run, I needed another expect statement. My passing test is as such:
describe "#start" do
context "X player's turns" do
it "prints proper messege" do
expect(STDOUT).to receive(:puts).with("Harry: Please enter a number for your X to go")
expect(game).to receive(:get_input)
game.start("X")
end
end
end
I'm not sure if this is correct, but the test did pass.
In ruby, recursion is pretty straight forward. Everything before calling the method itself works as expected. However, strange thing happen after calling the method itself.
def AdditivePersistence(num, t = 0)
p "when begin, num is #{num}"
return t if num <= 9
t += 1
num = num.to_s.split("").inject(0) { |sum, x| sum += x.to_i }
p "after inject, num is #{num}"
AdditivePersistence(num, t)
p "-----------"
p "t = #{t}"
end
AdditivePersistence(56)
The result looks like this.
"when begin, num is 56"
"after inject, num is 11"
"when begin, num is 11"
"after inject, num is 2"
"when begin, num is 2"
"-----------"
"t = 2"
"-----------"
"t = 1"
This shows the code p '---------' after calling the method itself gets executed after the recursion is done for however many times the recursion happened.
My question is shouldn't p '---------' either be not executed (just like code after return), or get executed every time the method is called?
edited:
Added p "t = #{t}". This result makes me even more confused. Why does t = 2 the first time? t started from 0, and gets incremented in every recursion.
First of all, in ruby, you shouldn't use camel case, it would be better to rename the method to additive_persistence.
So you bother about
"-----------"
"-----------"
this output? It is normal behavior. The pointer is being returned to previous AdditivePersistence(num, t) call and execution continues to p "-----------"
The function is called a total of three times. The last call of the function results in an early return, but the first two calls run all the way to the end and print dashes before they return. This is normal, and if you just think a little bit more about how your program works you should see there is no problem.
I indented the lines of output from your program to indicate how deep the call stack is when the output was produced:
"when begin, num is 56"
"after inject, num is 11"
"when begin, num is 11"
"after inject, num is 2"
"when begin, num is 2"
"-----------"
"-----------"
No, of course program flow returns to where it should, and continues in the calling method. Recursive method invocation is just like any other method invocation. The flow of execution temporarily leaves the method and returns to the next statement. This is the same for every language. When a method calls itself, that doesn't behave any differently than when a method calls another method.
Consider:
def method_1
puts "a"
method_2
puts "b"
end
def method_2
end
Do you think this program should not print "b" just because it invokes method_2 and flow of control temporarily leaves method_1? No, obviously not. When method_2 finishes, program flow returns to method_1 and the next statement (puts "b") executes.
Perhaps you're expecting some kind of loop behavior, where the recusrive call jumps to the top, but that's not at all what happens:
def AdditivePersistence(num, t = 0)
while true
p "when begin, num is #{num}"
break if num <= 9
t += 1
num = num.to_s.split("").inject(0) { |sum, x| sum += x.to_i }
p "after inject, num is #{num}"
next
# Flow will never reach this point
p "-----------"
end
t
end
AdditivePersistence(56)
OK. So I found my answer. Thanks everyone for helping.
I found the answer from this short video that explains how recursive function works in C++, which I believe is a similar process. This is for future reference for anyone looking for this question (turned out searching for "recursive function" instead of being specific to ruby will lead me to lots of answer).
https://www.youtube.com/watch?v=k0bb7UYy0pY
Because of how the recursive call stack works, it will start unwinding after the call stack reaches the base case. That's why in my code, p "---------" is called twice, and why t = 2 first, then t = 1 in the output.
Correct me if I am not understanding this correctly.
I am trying to write a small program that goes through an array's values outputting each individual value. When it reaches 15 it stops and outputs "too big".
Why is my logic below wrong, it makes sense to me..
x = [10,11,12,13,14,15,16,17,18]
def counting
for x[y]
while y < 15
puts y
else
puts "too big"
end
puts counting
I'm just learning sorry if this is a really simple solution.
That's nothing at all like Ruby syntax. You want a .each and a simple if statement:
x.each do |y|
if y < 15
puts y
else
puts "too big"
break
end
end
It appears though you are trying to use Ruby like you would a c-style programming language. It's possible and viable, albeit not recommended.
Code Blocks
Ruby has structure known as code blocks. Code blocks are sort of like anonymous functions.
You can read more about code blocks here.
x = [10,11,12,13,14,15,16,17,18]
# This is a code block.
x.each do |y| # The y between the '|'s is the parameter caught by the code block
if y < 15
puts y
else
puts "Too big."
break # Break out of the loop
end
end
If you want a one liner:
x.each {|y| y < 15 ? puts(y) : puts("too big") || break }
If you insist using while, it can be done as following:
i = 0
while i do
x[i] < 15 ? puts(x[i]) : puts("too big") || break
i+=1
end
I think the cleanest way to do this using while would be:
def counting(x)
i = 0
while x[i] < 15
puts x[i]
i += 1
end
puts 'too big'
end
counting([10,11,12,13,14,15,16,17,18])
Why is my logic below wrong, it makes sense to me..
Given that you program isn't even syntactically legal, it's impossible to tell what its semantics would be if it were syntactically legal, let alone why those semantics were wrong. However, here's an idiomatic Ruby solution (you will almost never use nor see while in any Ruby code, and you will certainly never see for):
puts x.take_while {|n| n < 15 }
puts 'too big'
I prefer writing in point-free style, however in this case the only way to do that is to make use of the symmetry of the condition and invert the order of operands, which will lead to awkward logic:
x.take_while(&15.method(:>))
Ao, in this case I would rather avoid point-free style, because it no longer matches the original specification literally. Instead of "take all numbers less than 15", we now have "take all numbers such that 15 is greater than those numbers", which reads awkward.
So I'm aware of the Math.sqrt(number) method in Ruby, but as part of learning the language I thought it'd be a good idea to make some simple functions. I could be going about finding the square root wrong from a fundamental level, but here's what I did:
def negative?(number)
number < 0
end
#start = 2
def sqrt(number)
if negative? number
puts "Can't do negative"
else
if #start**2 < number
#start += 0.01
sqrt(number)
else
puts "Square root of #{number} is #{#start}!"
end
end
end
def start_sqrt
print "Input text: "
number = Integer(gets.strip)
sqrt(number)
end
begin
start_sqrt
rescue ArgumentError
puts "Need a positive number!"
end
This works fine for smaller numbers, but when I get to entering larger numbers I need to change the value of #start if I want to get an answer, otherwise it says "stack too deep". Is there something wrong I'm doing in the code, or is Ruby doing everything fine and I just am asking it to find the square root of a number in a resource-intensive way? I guess this is maybe even less of a programming question and more of a math question, cause I know I could just do:
def sqrt(number)
if negative? number
puts "Can't do negative"
else
root = number**0.5
puts "Square root of #{number} is #{root}!"
end
end
It looks like Ruby is working as expected. You are asking it to find the square root of a number in a resource-intensive way (recursion).
In fact, you have created a stack overflow error. :)
In Perl, there is an ability to break an outer cycle like this:
AAA: for my $stuff (#otherstuff) {
for my $foo (#bar) {
last AAA if (somethingbad());
}
}
(syntax may be wrong), which uses a loop label to break the outer loop from inside the inner loop. Is there anything similar in Ruby?
Consider throw/catch. Normally the outside loop in the below code will run five times, but with throw you can change it to whatever you like, breaking it in the process. Consider this perfectly valid ruby code:
catch (:done) do
5.times { |i|
5.times { |j|
puts "#{i} #{j}"
throw :done if i + j > 5
}
}
end
What you want is non-local control-flow, which Ruby has several options for doing:
Continuations,
Exceptions, and
throw/catch
Continuations
Pros:
Continuations are the standard mechanism for non-local control-flow. In fact, you can build any non-local control-flow (subroutines, procedures, functions, methods, coroutines, state machines, generators, conditions, exceptions) on top of them: they are pretty much the nicer twin of GOTO.
Cons:
Continuations are not a mandatory part of the Ruby Language Specification, which means that some implementations (XRuby, JRuby, Ruby.NET, IronRuby) don't implement them. So, you can't rely on them.
Exceptions
Pros:
There is a paper that proves mathematically that Exceptions can be more powerful than Continuations. IOW: they can do everything that continuations can do, and more, so you can use them as a replacement for continuations.
Exceptions are universally available.
Cons:
They are called "exceptions" which makes people think that they are "only for exceptional circumstances". This means three things: somebody reading your code might not understand it, the implementation might not be optimized for it (and, yes, exceptions are godawful slow in almost any Ruby implementation) and worst of all, you will get sick of all those people constantly, mindlessly babbling "exceptions are only for exceptional circumstances", as soon as they glance at your code. (Of course, they won't even try to understand what you are doing.)
throw/catch
This is (roughly) what it would look like:
catch :aaa do
stuff.each do |otherstuff|
foo.each do |bar|
throw :aaa if somethingbad
end
end
end
Pros:
The same as exceptions.
In Ruby 1.9, using exceptions for control-flow is actually part of the language specification! Loops, enumerators, iterators and such all use a StopIteration exception for termination.
Cons:
The Ruby community hates them even more than using exceptions for control-flow.
No, there isn't.
Your options are:
put the loop in a method and use return to break from the outer loop
set or return a flag from the inner loop and then check that flag in the outer loop and break from it when the flag is set (which is kind of cumbersome)
use throw/catch to break out of the loop
while c1
while c2
do_break=true
end
next if do_break
end
or "break if do_break" depending on what you want
Perhaps this is what you want? (not tested)
stuff.find do |otherstuff|
foo.find do
somethingbad() && AAA
end
end
The find method keeps looping until the block returns a non null value or the end of the list is hit.
Wrapping an internal method around the loops could do the trick
Example:
test = [1,2,3]
test.each do |num|
def internalHelper
for i in 0..3
for j in 0..3
puts "this should happen only 3 times"
if true
return
end
end
end
end
internalHelper
end
Here you can do a check inside any of the for loops and return from the internal method once a condition is met.
I know I will regret this in the morning but simply using a while loop could do the trick.
x=0
until x==10
x+=1
y=0
until y==10
y+=1
if y==5 && x==3
x,y=10,10
end
end
break if x==10
puts x
end
The if y==5 && x==3 is only an example of an expression turning true.
You may consider adding a flag, which is set inside the inner loop, for controlling the outer loop.
'next' the outer loop
for i in (1 .. 5)
next_outer_loop = false
for j in (1 .. 5)
if j > i
next_outer_loop = true if j % 2 == 0
break
end
puts "i: #{i}, j: #{j}"
end
print "i: #{i} "
if next_outer_loop
puts "with 'next'"
next
end
puts "withOUT 'next'"
end
'break' the outer loop
for i in (1 .. 5)
break_outer_loop = false
for j in (1 .. 5)
if j > i
break_outer_loop = true if i > 3
break
end
puts "i: #{i}, j: #{j}"
end
break if break_outer_loop
puts "i: #{i}"
end