Can I reuse conditions in nested if statements? - ruby

Is it possible to reuse a condition from a parent if statement?
Example:
if a == b || a == c
if a == b
#do thing
elsif a == c
#do the other thing
end
#in addition to this thing
end
Can the initial a == b or a == c be referenced in the nested statements without manually retyping them?

As pointed in the comment in ruby, the process of storing a variable inside returns the value of the variable so you can do this:
a = 3
b = 4
c = 3
if cond1 = a == b || cond2 = a == c then
if cond1 then
puts "a==b"
elsif cond2
puts "a==c"
end
puts "do this"
end
the result
irb(main):082:0> a==b
do this
=> true
i

I suggest the following.
case a
when b
...
common_code
when c
...
common_code
end
def common_code
...
end

Perhaps you can use a flag.
if a == b
flag = true
# do thing
elsif a == c
flag = true
# do the other thing
else
flag = false
end
if flag
# in addition to this thing
end
or
flag =
case a
when b
# do thing
true
when c
# do the other thing
true
else
false
end
if flag
# in addition to this thing
end

Related

Question on how to filter x || y and not x && y

I am having trouble using || ("or").
This is the first time I select using the "or" feature and I have been trying to select the words that are greater than 6 characters long OR start with an "e". I tried everything but I keep getting just one feature or an "and". This is the code so far
def strange_words(words)
selected_words = []
i = 0
while i < words.length
word = words[i]
if word.length < 6
selected_words << word
end
i += 1
end
return selected_words
end
print strange_words(["taco", "eggs", "we", "eatihhg", "for", "dinner"])
puts
print strange_words(["keep", "coding"])
Using the || operator is the same as writing multiple if statements. Let's use a silly example to demonstrate it. Say you wanted to determine if a word started with the letter 'e'. Well there are a few forms of 'e'. There is the lowercase e and the upppercase E. You want to check for both forms so you could do something like this:
def starts_with_e?(string)
result = false
if string[0] == 'e'
result = true
end
if string[0] == 'E'
result = true
end
result
end
Notice however that you're doing the same actions after checking for the condition. This means you could simplify this code using the OR/|| operator, like such:
def starts_with_e?(string)
result = false
if string[0] == 'e' || string[0] == 'E'
result = true
end
end
For your specific question, you can do the following:
def strange_words(words)
words.select { |word| word.length < 6 || word[0] == 'e' }
end
When you run with your example, it gives you this output:
> strange_words(["taco", "eggs", "we", "eatihhg", "for", "dinner"])
=> ["taco", "eggs", "we", "eatihhg", "for"]
This is still not good code. You'll want to protect the methods from bad input.

ternary operator based on if else

If i have following if else statement
if a.present? && b.value == 'N'
b = test
elsif a.present? && b.value == 'Y'
b = guest
end
I can write ternary operation for this
b = (a.present? && b.value == 'N') ? "test" : "guest"
but in this ternary oprator i am not looking for condition b.value == 'Y' and it could be something else 'd' or 'e'.
How do i update ternary operator so it verifies both conditions in if and elsif?
For something like this you might want to use a simple look-up table to eliminate some of the logic:
EQUIVALENT = {
'Y' => 'guest',
'N' => 'test'
}
if (a.present?)
b = EQUIVALENT[b.value] || b
end
The || b part may not be necessary if non-mapped b values are ignored.
b = case b.value
when 'N' then test
when 'Y' then guest
end if a.present?
This is the only DRY answer here so far.
You can use a ternary operator. It doesn't mean you should do it, though:
a.present? && (b.value == 'N' ? b = 'test' : b.value == 'Y' && b = 'guest')
Here's a small test:
class Object
def present?
true
end
end
class NilClass
def present?
false
end
end
a = true
class B
attr_accessor :value
end
b = B.new
b.value = 'Y'
a.present? && (b.value == 'N' ? b = 'test' : b.value == 'Y' && b = 'guest')
p b
# "guest"
I would not insist on the ternary operator but extract the common a.present? test in an outer if and then write the rest of the code using if modifiers:
if a.present?
b = test if b.value == 'N'
b = guest if b.value == 'Y'
end
To me, it seems much easier to read this way.

Implementing operator precedence in my calculator interpreter

As part of learning Ruby am trying to implement a basic interpreter which reads input and do basic arithmetic calculations. So far basic arithmetic operations are working but having problem in operator precedence. Which is not handled yet. This is the code. Am at a beginner level. Any mistakes in this code are due to my lack of knowledge. How this code can be modified to handle operator precedence.
Sample output
2+2+2 = 6 #correct
10+10/2 = 10 # incorrect as in irb answer must be 15
Github Repo of this interpreter
=begin
Basic calculator Interpreter
can add, substract, multiply , divide with any number of operands at a time
Drawback : Lacks operator precedence
=end
class Interpreter
attr_accessor :input
def initialize
#input = gets.chomp
end
def intepret
first_operand = []
f = []
operator = '+'
array = Array.new
lc = 0
#input.split.join.split("").each_with_index.map do |i, index|
if i.is_number?
first_operand.push(i)
if index == #input.length-1
array.push(first_operand.join("").to_i)
end
elsif i.is_plus?
f = first_operand
first_operand = nil
first_operand = []
array.push(f.join("").to_i)
array.push("+")
elsif i.is_minus?
f = first_operand
first_operand = nil
first_operand = []
operator = '-'
array.push(f.join("").to_i)
array.push("-")
elsif i.is_multi?
f = first_operand
first_operand = nil
first_operand = []
operator = '*'
array.push(f.join("").to_i)
array.push("*")
elsif i.is_divide?
f = first_operand
first_operand = nil
first_operand = []
operator = '/'
array.push(f.join("").to_i)
array.push("/")
else
puts "Illegal input exiting.."
exit
end
lc = lc+1
end
#apply the appropriate operation on the inputs based on the operand
#puts "=======TOKENS======"
#puts array.inspect
result = 0
array.each_with_index.map do |x, key|
result = x if key == 0
if x == '+'
if key == 0
result = add(result, array[key+1])
else
result = add(result, array [key+1])
end
elsif x == '-'
if key == 0
result = minus(result, array[key+1])
else
result = minus(result, array [key+1])
end
elsif x == '*'
if key == 0
result = multi(result, array[key+1])
else
result = multi(result, array [key+1])
end
elsif x == '/'
begin
if key == 0
result = divide(result, array[key+1])
else
result = divide(result, array [key+1])
end
rescue
puts "Zero Divsion error"
exit
end
end
end
puts "Result is: "+result.to_s
end
def print_token(type, value)
puts type + ' '+ value
end
def add(f,s)
return f.to_i + s.to_i
end
def minus(f,s)
return f.to_i - s.to_i
end
def multi(f,s)
return f.to_i * s.to_i
end
def divide(f,s)
return f.to_i / s.to_i
end
end
# Override the string class, to directly use methods like obj.is_number? rather than is_number?(obj)
class String
def is_number?
true if Float(self) rescue false
end
def is_plus?
true if self == '+' rescue false
end
def is_minus?
true if self == '-' rescue false
end
def is_multi?
true if self == '*' rescue false
end
def is_divide?
true if self == '/' rescue false
end
end
#continue accepting inputs until exit CTRL + D
while true
print 'pck>:'
i_obj = Interpreter.new
i_obj.intepret
end
First, process the input using the Shunting-yard algorithm. This should give a list of tokens in Reverse Polish notation (RPN). Then you can evaluate the RPN expression.

Why can't a variable initialized in an `if` condition be used in the if's block?

So it is common to initialize a variable in an if condition, and then use that variable inside the if's block.
if a = foo()
puts a
end
However when I initialize a variable and use it in the same if's block, that var will not be considered initialized at that time. For example:
def good?(item)
puts "item is #{item.inspect}"
true
end
if b = 52 && good?(b)
puts "b is #{b.inspect}"
end
Run the above and the result would be
item is nil
b is true
Why is this the case? What kind of keyword is related to this Ruby behavior that I search for and study about it?
The precedence of && is higher than =, so
if b = 52 && good?(b)
is equivalent to:
if b = (52 && good?(b))
Reference: Operator Precedence.
You're assigning to b the result of 52 && good?(b). b is still nil when it is passed to good?.
Parenthesis are the key.
def good?(item)
puts "item is #{item.inspect}"
true
end
if (b = 52) && good?(b)
puts "b is #{b.inspect}"
end
Result:
item is 52
b is 52

What is the best way to determine if a variable is a number in Ruby? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Test if string is a number in Ruby on Rails
Currently I have this (awful) code:
def is_num(num_given)
begin
num_given.to_i
worked = true
rescue
worked = false
ensure
return worked
end
end
Which I refactored to this:
def is_num(num_given)
num_given.to_i.is_a?(Numeric) rescue false
end
This still just doesn't feel right to me, is there a better way to do this?
Both of these implementations work fine for my purposes, I am just looking for some code euphoria.
something.is_a?(Numeric) is the way to go. Referring to your latter example, there's no need to call to_i on the input.
Note that something.is_a?(Numeric) will not work if you're looking to see if a string is a number...
Here's another solution. It's not very Ruby-like, but that's intentional (e.g., while is faster than str.chars.each in this case).
# is a character between 0 and 9? (based on C's isdigit())
def digit?(c)
o = c.ord
o >= 48 && o <= 57 # '0'.ord, '9'.ord
end
# is a string numeric (i.e., represented as an integer or decimal)?
def numeric?(str)
str = str.to_s unless str.is_a?(String)
l = str.length
i = 0
while i < l
c = str[i]
if c == '.' || c == '-'
i += 1
next
end
return false if !digit?(c)
i += 1
end
true
end
Here are the unit tests. Let me know if I missed a case. For other answerers, just change the subject block to your function.
if $0 == __FILE__
require 'minitest/autorun'
describe :digit? do
%w(- + : ? ! / \ ! # $ ^ & *).each do |c|
it "flunks #{c}" do
digit?(c).must_equal false
end
end
%w(0 1 2 3 4 5 6 7 8 9).each do |c|
it "passes #{c}" do
digit?(c).must_equal true
end
end
end
describe :numeric? do
subject { :numeric? }
%w(0 1 9 10 18 123.4567 -1234).each do |str|
it "passes #{str}" do
method(subject).call(str).must_equal true
end
end
%w(-asdf 123.zzz blah).each do |str|
it "flunks #{str}" do
method(subject).call(str).must_equal false
end
end
[-1.03, 123, 200_000].each do |num|
it "passes #{num}" do
method(subject).call(num).must_equal true
end
end
end
end
The functions you listed won't work:
is_num("a") #=> true
The problem is that they don't raise an error for invalid input. What you want is Integer, which will raise an error which you can rescue:
def is_num(num_given)
!!Integer(num_given) rescue false
end
This works:
irb(main):025:0> is_num("a")
=> false
irb(main):026:0> is_num(5)
=> true
irb(main):027:0> is_num((1..2))
=> false
irb(main):028:0> is_num("3")
=> true
(There may be a more natural way to do this, though.)
You can always use a simple regex:
def is_num(num_given)
num_given =~ /\d+(\.\d+)?/
end

Resources