Ruby substitute for goto
Instead of goto just create functions for A, B and so on and use them
for example:
def A
# Lot of code
end
Now you can goto
A by just writing A
.
Also instead of using if/else you can use switch case, so that your code will look like
A
case variable
when something
B
D
when something else
B
C
D
else
C
D
end
How to have a goto statement / How to establish a loop?
Try this:
loop do # Edit: more Rubyesque than 'while true', see comments and other answers.
print "Command, please: "
command = gets.chomp
case command
when "0"
exit
when "1"
puts "Hello, world!"
end
end
Note:
case command
- you're checking the command variable, not a response
method on the case object.
when "0"
- what comes back will always be a string, so it will never match 0
- an integer.
end
- you've opened a case statement.
How to jump to a specific line of code
The gist of this is using the loop
approach where you can repeat something until you get a satisfactory answer:
def play_again?
loop do
print "Would you like to play again? Y/N"
again = gets.chomp.capitalize
case (again)
when 'N'
return false
when 'Y'
return true
else
puts "Huh? I don't know what that means."
end
end
end
Then you can incorporate this into your main program:
begin
run_game
end while play_again?
That will keep running the game until play_again?
returns false
, which happens only if you type "N".
How to implement goto and label methods in Ruby?
Here's an idea: pre "parse" your assembly code and then execute it. This consists of changing a few things. Here would be my implementation of the Memory class:
class Memory
attr_reader :table
def initialize
@table = { ax: 0, bx: 0, cx: 0, dx: 0 }
@program = []
@labels = {}
end
OPS = {
"mov" => lambda {|dest, src| @table[dest] = (src.is_a?(Symbol) ? @table[src] : src); nil},
"inc" => lambda {|dest, incval = 1| @table[dest] += incval; nil},
"jmp" => lambda {|lblname| @labels[lblname]}
#and so on
}
def method_missing(name, *args)
if(OPS.keys.include?(name.to_s))
@program << [name.to_s, args]
elsif(name.to_s == "label")
@labels[args.first] = @program.length
else
return name
end
end
def load_program(&block)
self.instance_exec(&block)
end
def run_program
pc = 0
until(pc == @program.length)
instruction = @program[pc]
retval = self.instance_exec(*instruction[1], &OPS[instruction[0]])
if(retval)
pc = retval
else
pc += 1
end
end
end
def asm(&block)
load_program(&block)
run_program
@table
end
end
Let's go over this step by step. Instead of having a method for each instruction, I use a hash of lambdas. Then, I use method_missing to do three things:
- If the method name is just a random symbol (not an instruction name), I just return the symbol. So now,
cx
is equivilent to:cx
. - If it's an instruction name, add it and the arguments to the program array
- If it's a
label
, add the index of the next instruction to the labels hash under that name.
The return value (if not nil) of an instruction lambda is used as the new value of the program counter (so that more jumps like jump if zero can be added).
Is there a a back to the top in Ruby?
No, Ruby does not have goto
. Try a loop instead:
loop {
input = gets.chomp
if input == "option one"
puts "you choose right"
break
else
puts "you choose wrong! DO IT AGAIN!"
end
}
Or, an alternative method (and arguably slightly more readable):
input = gets.chomp
until input == "option one"
puts "you choose wrong! DO IT AGAIN!"
input = gets.chomp
end
puts "you choose right"
Can you pass a next back to the function that called the current function?
next
is only valid in the direct context of a loop. Once you call into a method, you are no longer directly in that loop context. You cannot use next to short-circuit the outer loop like this.
You have a couple of options:
- Return statuses from your predicate functions (which is what you should do, from a predicate!) and short-circuit the loop based on those, or
- Use Ruby's
catch...throw
construct (which is NOT its raise/rescue exception handler, but is instead something like a block-scoped GOTO statement)
Option 1: Returning statuses. This is the most appropriate method, IMO. Predicate methods (those ending in ?) should conventionally return a boolean and be idempotent (that is, should have no side effects, such as logging a statement). They are conventionally used to ask a yes/no question. Deciding what to do based on that question should ideally be outside of their scope.
def card_handler
cards.each do |card|
#some non-relevant code is here on my end
if already_sent?
puts '\n Order info: \n id: #{id} \n Email already sent'
next
end
end
end
def already_sent?
case list_action
when 145
a_s_helper(p3_label)
when 145
a_s_helper(p2_label)
when 147
a_s_helper(p1_label)
end
end
def a_s_helper(label)
card::card_labels.include? label
end
This causes your helpers to return a true or false value to your loop, which can decide to log a message and go to the next iteration.
Option 2: catch...throw
def card_handler
cards.each do |card|
# Put all your code that should nomally run inside the catch block. If
# the message :email_sent is thrown, then Ruby will zip up the stack and
# resume execution at the end of the block. This will skip any unexecuted
# code in the block, essentially terminating the execution.
catch :email_sent do
already_sent?
end
end
end
def already_sent?
# ...
end
def a_s_helper(label)
# ...
throw :email_sent if card::card_labels.include? label
# ...
end
You may be tempted to use option 2, since it requires less careful control over method construction, but it is perilously close to exceptions as flow control which are widely considered an antipattern (it's essentially a slightly more fancy GOTO, which is notorious for making code difficult to read and debug). If you can simply return a status from your helpers and decide whether or not to continue the loop based on that, you should do so.
Ruby on Rails: Best way to handle repeated error checking without goto?
Callbacks
You should transfer many of these errors into your models, using Callbacks. These apply to errors that are relevant to actions that involve records in your database, i.e. checking whether a data input is appropriate.
Filters
Use before_filters and after_filters to check for errors, especially when you need to perform these checks on multiple controller actions. An example:
before_filter :check_errors
def example
regular code...
end
private
def check_errors
error checking...
end
Case statements
Use Case statements to improve your if
statements, particularly when you have multiple checks involved.
Prioritizing the above
Use callbacks in your models whenever you can and definitely whenever data saving/updating/validation is involved.
Use before_filters
whenever the code is to be reused across multiple actions (and in my opinion, always whenever you have involved error checking like this).
If you need these checks to occur only once, in this controller action alone, that do not involve records being changed, simply rewrite your code in a valid case statement (but my recommendation would still be to transfer to a before_filter).
Related Topics
Convert a .Doc or .Pdf to an Image and Display a Thumbnail in Ruby
Where's the Best Place to Define a Constant in a Ruby on Rails Application
In Rails, Display Time Between Two Dates in English
Default Task for Namespace in Rake
What Are the Paths That "Require" Looks Up by Default
How to Access the Rack Environment from Within Rails
Ruby - Lexical Scope VS Inheritance
What's the Difference Between Design Patterns and Design Principles
How to Randomly Sort (Scramble) an Array in Ruby
Parsing String to Add to Url-Encoded Url
Setting Elastic Search Limit to "Unlimited"
Adding an Action to an Existing Controller (Ruby on Rails)
Setting Up Private Github Access with Aws Elastic Beanstalk and Ruby Container
Rails Resque Workers Fail with Pgerror: Server Closed the Connection Unexpectedly
Magic First and Last Indicator in a Loop in Ruby/Rails
How to Serve Requests Concurrently with Rails 4
Changing Table Name at Query Run Time in a Rails Application