This question already has answers here:
Shortcut to make case/switch return a value
(4 answers)
Closed 6 years ago.
I have this code
(1..50).each do |num|
case num
when num % 4 == 0, num % 6 == 0
puts 'Cluck'
when num % 4 == 0
puts 'Cluck Cluck'
when num % 5 == 0
puts 'Cluck Cluck Cluck'
else
puts num
end
end
For some odd reason, instead of putting cluck cluck on the fourth line or cluck on the 24th line, it's just putting a list of 1 through 100. I can't figure out what's wrong with the switch statement. The first when using the comma or && doesn't change anything either (which I don't believe it should).
Problems
case a when b
case a
when b
tests if a is equal to b.
In your case, a is a number (num) and b is a boolean (num % 4 == 0) so this never happens.
when b,c
Another problem is that
case
when b,c
tests if b or c.
If you want to check that num is divisible by 24, you need b and c.
Solution
Remove num from case and use logical and (&&) :
(1..100).each do |num|
case
when num % 4 == 0 && num % 6 == 0
## or just :
# when num % 24 == 0
puts 'Cluck'
when num % 4 == 0
puts 'Cluck Cluck'
when num % 5 == 0
puts 'Cluck Cluck Cluck'
else
puts num
end
end
Oh well, Eric's answer is right on the money. I'd just add this as a reference -
It doesn’t end there though you can use a case statement without giving it a value to match against, which allows a case statement to mimic the behavior of an if statement, e.g.:
print "Enter a string: "
some_string = gets.chomp
case
when some_string.match(/\d/)
puts 'String has numbers'
when some_string.match(/[a-zA-Z]/)
puts 'String has letters'
else
puts 'String has no numbers or letters'
end
I read over this guide quick, and it seems to be that you're trying to use the case as an if statement, but you supplied a value to match against.
Because you gave num as the first argument of case, it's expecting to match against it. The problem is, your conditions evaluate to boolean values, and num is a number, so they'll never match, and the else condition will always be run.
Remove the num from the start of the case.
Related
I'm trying to get the numbers in numbers to print out.
But when the if-expression comes before the numbers are appended, it doesn't seem to print. Only if I put the if-expression after, it will. Why is this?
numbers = []
while numbers.size < 5
if numbers.size == 5
puts numbers
break
end
numbers << rand(0..99)
end
This happens because you have numbers.size < 5.
As soon as numbers.size is 5, the while is no longer true. You can use <= (or numbers.size < 6) if you want it to ONLY print when it is exactly 5.
numbers = []
while numbers.size <= 5
if numbers.size == 5
puts numbers
break
end
numbers << rand(0..99)
end
Or you could write this in a more ruby style:
numbers = []
5.times do numbers << rand(0..99) end
puts numbers
1.Case 1: If statement placed before appending
The reason is because the loop will not execute when numbers.size == 5 because the condition you've set is while numbers.size < 5.
2. If statement is placed after appending
Placing the if statement after appending the numbers list will work because you're still within the body of the loop i.e. you're still executing the loop for which the condition you set was true.
Your code doesn't work because the while loop exits before the if condition is true.
What's more, the if block is unnecessary and makes your code confusing and inefficient.
Lemme refactor it for ya...
Generates an array of 5 random integers between 0 and 99 and then prints it
numbers = []
while numbers.size < 5
numbers << rand(0..99)
end
puts numbers
This question already has answers here:
How to test if a string is basically an integer in quotes using Ruby
(19 answers)
Closed 6 years ago.
I have this code where I am entering input for sides of a triangle. Depending on the values, it will print it the triangle is equilateral, isoceles, or scalene. It's executing for number values, but how do I specify that the input should only be integers? For example, if I type in "w" , it should say invalid or error, but in this case, it executes. How do I solve this?
Basically, I am looking for a way to write that if a string were to be inputted, it should show up as an error (then I would write a print statement saying it is invalid). So could I put that into another if statement? (before the ones mentioned below)
Example Code:
puts "Enter the triangle length"
x = gets.chomp
puts "Enter the triangle width"
y = gets.chomp
puts "Enter the triangle height"
z = gets.chomp
if x == y and y == z
puts "This triangle is equilateral"
else if
x==y or y == z or x==z
puts "This triangle is isoceles"
else if
x!=y and y!=z and x!=z
puts "this triangle is scalene"
end
end
end
If you are dealing with integers, you can check this with ruby.
Note, this is not as robust as regex, but it covers most cases.
if (input != '0') && (input.to_i.to_s != input.strip)
# input is not a number
else
# input is a number
end
strip is there if you want to accept input with leading or trailing whitespace, otherwise you can leave it off.
While all the other answers are probably more or less robust, I would go with another one. Since you have a triangle sides lengths, they are to be greater than zero, right? Then one might use the side effect of String#to_i method: for everything that is not converting to integer it returns zero. Therefore:
x = gets.chomp.to_i
y = gets.chomp.to_i
z = gets.chomp.to_i
raise "Wrong input" unless x > 0 && y > 0 && z > 0
# ...
You can do something like this:
x = x.to_i
puts "Please enter an integer" if x == 0
Why?
Because:
"ABC".to_i # returns 0
You may be better off calling strip instead of chomp
gets.strip.to_i
An example:
## ruby user_age.rb
# input variables
name = ""
years = 0
MONTHS_PER_YEAR = 12 # a constant
# output variable
months = 0
# processing
print "What is your name? "
name = gets.strip
print "How many years old are you? "
years = gets.strip.to_i
puts "please enter an integer" if years == 0
months = years * MONTHS_PER_YEAR
puts "#{name}, at #{years} years old, "\
"you are #{months} months old."
There are several ways of doing it. If you allow for a leading sign,
x =~ /^[+-]?\d+$/
would be a possibility.
You will also have to think whether or not you allow surrounding or embedding spaces (for instance, a space between the sign and the first digit).
I assume that any string value that, when converted to a float, equals an integer is to be accepted and the integer value is to be returned. Moreover, I assume integers can be entered with the "xen" (or "xEn") notation, where x is an integer or float and n is an integer.
def integer(str)
x = Float(str) rescue nil
return nil if x==nil || x%1 > 0
x.to_i
end
integer("3") #=> 3
integer("-3") #=> -3
integer("3.0") #=> 3
integer("3.1") #=> nil
integer("9lives") #=> nil
integer("3e2") #=> 300
integer("-3.1e4") #=> -31000
integer("-30e-1") #=> -3
integer("3.0e-1") #=> nil
You could use Integer() to check if a string contains an integer:
Integer('1234')
#=> 1234
Integer('foo')
#=> ArgumentError: invalid value for Integer()
This could be combined with a retry:
begin
number = Integer(gets) rescue retry
end
The next statement is used to skip a part of the loop and continue with the next iteration of the loop. It can be used in combination with for and while statements.
I have seen people using next if there is complicated piece of code after some condition is being evaluated i.e
next if #state!=:some_state
# some long complicated code
Now here I have played with the next in my IRB as below :
n = 1
loop do
n = n + 1
next unless n == 10
print "Good"
break
end
# Good=> nil
The above one understood. Nicely clear.
n = 1
#=> 1
loop do
print "#{n}"
n = n + 1
next puts "hi" unless n == 5
p "good"
break
end
#1hi
#2hi
#3hi
#4"good"
#=> nil
In the above code, couldn't understand about which order the lines puts "hi" and unless n == 5 executed. Which executed first?
The below one leads to the infinite Loop.
n = 1
#=> 1
loop do
print "#{n}"
n = n + 1
next puts "hi"; 2 + 3 unless n == 5
p "good"
break
end
whereas this one is good:
n = 1
#=> 1
loop do
print "#{n}"
n = n + 1
next puts "hi", 2 + 3 unless n == 5
p "good"
break
end
#1hi
#5
#2hi
#5
#3hi
#5
#4"good"
#=> nil
Please help me here to understand - how does this one resolve that forever loop ?
A semicolon is evaled as a line break so:
next puts "hi"; 2 + 3 unless n == 5
would be equivalent to:
next puts "hi"
2 + 3 unless n == 5
Therefore, next will always be called and you'll have an infinite loop.
The comma is evaled as passing a set of arguments (which is interpreted as an array by puts method signature), so:
next puts "hi", 2 + 3 unless n == 5
is equivalent to:
next puts("hi", 2 + 3) unless n == 5
Regarding the execution order of puts and unless - consider the following:
unless n == 5
next puts "hi"
end
In this example, it is obvious that unless is evaluated first, then if the condition passed is evaluated to false the next puts "hi" statement is executed. Well:
next puts "hi" unless n == 5
is shorthand for exactly the same thing. So the unless modifier will always be evaluated first. Naturally, if you insert a semicolon in the middle, it would cause this to be evaluated differently, since the semicolon is evaluated as a line break.
The conditional has to execute first, otherwise, how would it even know whether to evaluate any expressions involving the statement?
As to your second question, the value of the next expression is ignored for loops, the value in the expression is only useful to return a value from a block. So, the only thing that makes your loop end is the break.
So I am writing a Ruby program for school that changes the value of a boolean to true if a certain value is either 1 or 3, and to false if it is 0 or 2. Since I come from a Java background, I thought that this code should work: if n == 1 || n == 3
But it does not. So my question here is is it possible to use an "Or" expression thingy in If blocks in Ruby? I know that my current situation could be solved easily by just something like the following:
if n == 0
t_o_f = false
elsif n == 1
t_o_f = true
Et Cetera. But I want to know if I can use an Or in If blocks for the future.
Yes, any expression can be used in an if condition, including those using the || (logical or) operator.
As with Java, Ruby's || operator short-circuits. That is, if the left side is true, the right side is not evaluated.
Idiomatic ruby uses postfix if for one-liners:
puts "Yes" if n == 4 || n == 5
Avoid postfix if the line is long, however, and break it into multiple lines:
if n == 4 || n == 5
puts "This is a big long string to be output....................."
end
That's because postfix if can get visually lost at the end of a long line.
You can have a one-liner with prefix if, but it's not commonly seen:
if n == 4 || n == 5 then puts "foo" end
or, perhaps:
if n == 4 || n == 5 ; puts "foo" ; end
This is really a multi-line if condensed into one line; the semicolons act as line breaks.
When testing for multiple conditions, it can sometimes be valuable to use Array#include?:
puts "foo" if [4, 5].include?(n)
The value of this for only two conditions is not that great. For three or more, it reads well.
The or operator works, and you can write the keyword too:
if n==1 or n==2
end
http://www.tutorialspoint.com/ruby/ruby_operators.htm
And you could also write what you want this way:
x = (n%2) ? true : false
In addition to #Wayne Conrad: if there is little or no 'logic' deciding if n is true or false, then this is also a good option:
result = case n
when 1, 3 then true
when 0, 2 then false
end
I have two succeeding function with the same condition and I wonder what is the best way to write this? I know the one way to do this is using if (condition) ... end, but I'm wondering if I can do it in one-line similar to bash, '[$n == $value] echo "$n" && break'.
n = 0
loop do
puts n if n == value # puts and break is having the same condition, but
break if n == value # can we do it in one line?
n += 1
end
Because n is truthy, you can use the 'and' joiner. It reads really nicely:
n = 0
loop do
puts n and break if n == value
n += 1
end
--edit--
As pointed out in comments, that won't actually work because puts returns nil, which isn't truthy. My bad. You can use 'or' instead, but that doesn't read nicely. So I'd say just group the statements with parenthesis.
n = 0
loop do
(puts n; break) if n == value
n += 1
end
You could also change the puts method to return the value it prints, and that would work with 'and', but that's probably not the smartest idea :)
I'm guessing your actual code is different to what you've pasted, so if the first method in your chain returns something, you can use 'and'.
One easy way is to just parenthesize the statements:
ruby-1.9.1-p378 > 0.upto(5) do |n|
ruby-1.9.1-p378 > (puts n; break;) if n == 3
ruby-1.9.1-p378 ?> puts ">>#{n}<<"
ruby-1.9.1-p378 ?> end
>>0<<
>>1<<
>>2<<
3
If it's a bit much to put in parentheses, a begin-end will do the trick:
0.upto(5) do |n|
begin
puts "I found a matching n!"
puts n
puts "And if you multiply it by 10, it is #{10*n}"
break;
end if n == 3
puts "((#{n}))"
end
Output:
((0))
((1))
((2))
I found a matching n!
3
And if you multiply it by 10, it is 30
proc { puts n; break; }.() if n == 3
One of the golden rules of Ruby is that if you are writing a loop, you are probably doing it wrong. In this particular case, all that your loop is doing is to find an element in a collection. In Ruby, there already is a method for finding an element in a collection: Enumerable#find. There is no need to write your own.
So, the code gets simplified to:
puts (0...1.0/0).find {|n| n == value }
Now that we have a nice declarative formulation of the problem, it is easy to see that (assuming sane equality semantics and sane semantics of value.to_s), this is exactly the same as:
puts value
So, the whole loop was completely unnecessary to begin with.