ruby method looping without loop - ruby

hi I am having a problem with a method i have written. When the method has finished executing it runs its first 2 lines again even though there is no loop. can anyone tell me why?
here is the code
def print(students)
if sort_by?("letter") then students = letter_sort(students) end
if sort_by?("cohort") then students = cohort_sort(students) end
print_header
i = 0
until i = students.length do
student_name = get_student_name(students[i][:name])
puts "#{i+1}. #{student_name} (#{students[i][:cohort]} cohort)"
i = i + 1
end
end
The two if statements at the top are the ones being executed. Here is the sort_by? method I've written as well.
def sort_by?(string)
puts "would you like to sort by #{string}?"
puts "Enter y/n:"
input = gets.chomp.downcase
while input != "n" or input != "y"
if input == "n"
return false
elsif input == "y"
return true
end
end
end
Any help at all would be appreciated.

Related

Ruby - How to Execute something and then Break inside IF block?

EDIT: Someone pointed out that I needed to break correctly so I am editing the question
Scenario:
Please see following code:
print "UserID: "
uid = $stdin.gets.chomp
print "Password: "
pwd = $stdin.gets.chomp
usr_inp = "#{uid};#{pwd}"
login_status = -1
# login_info.txt - "#{userid};#{password}" - format
File.open(File.join(File.dirname(__FILE__), 'login_info.txt'), "r") do |f|
f.each_line do |line|
puts line
if (line.chomp == usr_inp)
login_status = 1
elsif (line.chomp != usr_inp && line.include?(uid)) #case a person inputs invalid password
login_status = 0
elsif (line.chomp != usr_inp && !(line.include?(uid))) #case a person inputs an invalid id
login_status = 2
end
end
end
if (login_status == 1)
puts "\nLogged in successfully: #{uid}"
elsif (login_status == 2)
puts "\nSorry, that Employee does not exist."
elsif (login_status == 0)
puts "\nLogin failed.\nPlease check credentials."
end
Problem:
break if (condition) exists in Ruby. But I don't waht that.
I want to do something like:
if (condition x)
(do something)
break
elsif (condition y)
(do something else)
break
else
(whatever)
end
Maybe I am not understanding how ruby code works. Whenever I try to put the break as I want to use it, it associates with the next elsif.
Please help.
It depends on what you need and where you need it.
A script like this:
condition = 1
case condition
when 1
puts 'one'
break
when 2
puts 'two'
else
puts 'Other %s' % condition
end
puts 'end'
has a syntax error. break leaves a loop and there is no loop.
But with a loop, this works:
[1,2,3].each{|condition|
case condition
when 1
puts 'one'
break
when 2
puts 'two'
else
puts 'Other %s' % condition
end
puts 'end'
}
puts 'very end'
The output is:
one
very end
You see, the loop is stopped.
If you want to continue the loop with the next element, you need next (sorry, I'm just not aware what break is doing really in Java - it's been a long time since my last Java program):
[1,2,3].each{|condition|
case condition
when 1
puts 'one'
next
when 2
puts 'two'
else
puts 'Other %s' % condition
end
puts 'end %s' % condition
}
puts 'very end'
The result:
one
two
end 2
Other 3
end 3
very end
When you are not inside a loop (like in your code snippet), you may use exit (leave the program) or return (leave a method).

Return few lines back if condition is true

Let's say that I have this simple if-elsif-else block of code.
def
# some code...
input = get.chomp
if input == 1
puts "foo"
elsif input == 2
puts "bar"
else
# exit
end
# some more code...
end
How do I tell program to go back and ask for input again for cases 1 and 2 and continue with code within this method if else is triggered? I do not want to go back to start of the method, instead I just want to back to input variable declaration.
def
# some code...
loop do
input = get.chomp
if input == 1
puts "foo"
break
elsif input == 2
puts "bar"
break
end
end
# some more code...
end
Note: Your two if/elsif conditions will never be satisfied.
# main procedure
# defined here so other functions could be declared after
# the main procedure is called at the bottom
def main
loop do
puts "Insert a number"
input = gets.chomp.to_i
if isValidInput input
puts case input
when 1
"foo"
when 2
"bar"
end
break
end
end #loop
puts "Other code would execute here"
end
# Validity Checker
# makes sure your input meets your condition
def isValidInput(input)
if [1,2].include? input
return true
end
return false
end
main

Ruby code efficiency

Is there a way to make this code shorter and simpler?
loop do
if possibleSet.split(" ").map(&:to_i).any? {|e| (e<0 || e>12)}
print "Please enter valid numbers (between 1 and 12): "
possibleSet = gets
errorinput = false
else
errorinput = true
end
break if errorinput
end
Refactored a bit :)
loop do
print "Please enter valid numbers (between 1 and 12): "
possibleSet = gets.chomp
break unless possibleSet.split(" ").map(&:to_i).any? {|e| (e<0 || e>12)}
end
The code below will check input for correctness:
input = loop do
print "Please enter valid numbers (between 1 and 12): "
# ⇓⇓⇓ as many spaces as user wants
input = gets.chomp.split(/\s+/).map(&:to_i) rescue []
break input unless input.empty? || input.any? { |i| !(0..12).include? i }
end
This parses the user input in an array (not exactly the same behavior, but I hope it is cleaner and you can work from there)
set = []
until set.all? {|i| (1..11).include?(i) } && !set.empty? do
set = gets.split(' ').map(&:to_i)
end

Ruby: function/method positions

I have this factorial app that's supposed to go infinite if answer is always "y".
def continue?
answer = gets
if answer.downcase == "y"
main
elsif answer.downcase == "n"
exit
else
"This means n to me. Follow the rules next time. Bye."
end
end
def main
p "Enter any Integer"
out = gets
num = out.to_i
def factorial(num)
sum = num
(num-1).times {sum = sum * (num - 1); num = num-1}
sum
end
p factorial(num)
p "Do you want another number"
continue?
end
main
At first, #continue? was at the end of the app, but then when I called continue in #main I'd get an error for non-existing Method. So, I moved #continue? to the top but now I can't call #main because of the same method error again. I can put #continue? inside #main method but I don't think it will do a lot. Is there a better way for handling this kind of situation?
If my code is off or my practice is not the best please let me know. And I'd use #inject for factorial but I was working with ruby 1.8.5 so I had to do what I could.
First of all, calling main from another function is weird because main should only be called once when the program starts.
Second, if you do it this way you're going to run out of memory because your callstack is going to keep growing (main, continue, main continue, ...)
Why don't you make continue? return a true or false value. Then in main you can write
begin
p "Enter any Integer"
out = gets
num = out.to_i
def factorial(num)
sum = num
(num-1).times {sum = sum * (num - 1); num = num-1}
sum
end
p factorial(num)
p "Do you want another number"
end while continue?
You could put the condition in a while loop instead of calling the function every time. Also, take care with gets method, you should strip the input.
def continue?
answer = gets.strip
if answer.downcase == "y"
true
elsif answer.downcase == "n"
false
else
p "This means n to me. Follow the rules next time. Bye."
false
end
end
def main
begin
p "Enter any Integer"
out = gets
num = out.to_i
def factorial(num)
sum = num
(num-1).times {sum = sum * (num - 1); num = num-1}
sum
end
p factorial(num)
p "Do you want another number"
end while continue?
end
main
You've got a couple of problems. First, when you do answer = gets what you're getting isn't just a letter, it's a letter followed by a linefeed, e.g. 'y\n'. The solution is to use str#chomp. Also, you're not actually showing anything when a letter other than 'y' or 'n' is entered. Here's the fixed method:
def continue?
answer = gets.chomp
if answer.downcase == "y"
main
elsif answer.downcase == "n"
exit
else
puts "This means n to me. Follow the rules next time. Bye."
end
end

Is there a way to 'cd ..' up a nested "if" statement tree?

I'm curious if there's a way to have the program go back up the if statement stack?
Ideally, the program would return to line 2 and prompt the user for the input variable, then continue to evaluate like it did the first time. Think of it like a cursor in a text editor, I just want to move it from either of those two comments back up to line 2. The two places of interest are commented out below:
while true
input = gets.chomp
if input != input.upcase
puts "HUH?! SPEAK UP, SONNY!"
elsif input == 'BYE'
puts "HUH?! SPEAK UP, SONNY!"
input = gets.chomp
if input == 'BYE'
puts "HUH?! SPEAK UP, SONNY!"
input = gets.chomp
if input == 'BYE'
puts "GOOD BYE!";
break
else
# return to top-level if statement
end
else
# return to top-level if statement
end
else
random_year = rand(1930..1950)
puts "NO, NOT SINCE #{random_year}!"
end
end
In the code you show, you don't need to do anything to make the flow of execution go back to line 2. Just omit the else clauses in the two places you marked. The flow of execution will drop down to the bottom of the while loop, then loop back to the top, then go back to line 2.
You need to use a while statement to set a condition flag and check it, which will loop back to the while statement if you don't change the flag:
flag = 0
while flag1 == 0
if var = "string"
then ...statements...
flag1 = 1 ; this allows us to break out of this while loop
else ...statements...
end
end
If flag1 is not 0 at the end of the while statement, the while statement will loop back. For two such conditions, you need to nest the while loops. You might have to re-order your statements to make multiple while loops work this way.
You can avoid this level of neasted ifs with:
byecount = 0
while byecount < 3
input = gets.chomp
if input == "BYE"
byecount += 1
next
else
byecount = 0
end
if input != input.upcase
puts "HUH?! SPEAK UP, SONNY!"
else
puts "NO, NOT SINCE #{rand(1930..1950)}!"
end
end
puts "GOOD BYE!"
Or you can write a catch..throw flow structure. (Really.. if you need to use it, something is wrong with your design)
catch :exitloop do
while ...
if ...
if ...
if ...
throw :exitloop
end
end
end
end
end
Here's how I'd write a similar exercise:
BYE = 'BYE'
HUH = "HUH?! SPEAK UP, SONNY!"
loop do
input = gets.chomp
if input != input.upcase
puts HUH
next
end
if input != BYE
random_year = rand(1930..1950)
puts "NO, NOT SINCE #{random_year}!"
next
end
puts HUH
input = gets.chomp
if input == BYE
puts HUH
input = gets.chomp
if input == BYE
puts "GOOD BYE!";
break
end
end
end
I used loop instead of while. Matz, the main man for Ruby, recommends loop. See "Is there a “do … while” loop in Ruby?" for further discussion about it.

Resources