FizzBuzz using ternary conditional operator - ruby

I've been reading up on conditional-style expressions in ruby. However I came across one I couldn't quite understand to define the classic FizzBuzz problem. I understand the FizzBuzz problem and even wrote my own before finding the following quick solution utilising the ternary operator. If someone can explain to me how this chain works to satisfy the FizzBuzz problem it would be very much appreciated :)
for i in 0...100
puts i%3==0 ? i%5==0 ? "FizzBuzz" : "Buzz" : i%5==0 ? "Fizz" : i
end

Some parentheses might help:
puts (i%3 == 0) ? ((i%5 == 0) ? "FizzBuzz" : "Buzz") : ((i%5 == 0) ? "Fizz" : i)
So, if i is divisible by 3, then it checks whether i is also divisible by 5. If so, it prints "FizzBuzz" otherwise just "Buzz". If i is not divisible by three, then it checks divisibility by 5 again and prints "Fizz" if so, otherwise just i.

Here is a description of the FizzBuzz problem as stated in this Jeff Atwood article.
Write a program that prints the
numbers from 1 to 100. But for
multiples of three print "Fizz"
instead of the number and for the
multiples of five print "Buzz". For
numbers which are multiples of both
three and five print "FizzBuzz".
A ternary operator is shorthand writing for an if-else statement. The general format is:
cond ? evaluate_if_cond_is_true : evaluate_if_cond_is_false
So if I write:
int isEven = (i % 2 == 0) ? 1 : 0;
Is equivalent to the following code:
if (i % 2 == 0) {
isEven = 1;
} else {
isEven = 0;
}
Where cond is i % 2 == 0, evaluate_if_cond_is_true is 1 and evaluate_if_cond_is_false is 0.
The nice thing about ternary operators is that they can be combined. This means that the statement to execute when either condition evaluates to true or false can be another ternary operator.
Let put the entire condition in a more readable fashion:
i%3==0 ?
i%5==0 ?
"FizzBuzz"
: "Buzz"
: i%5==0 ?
"Fizz"
: i
And mapping this to if-else statements is easy with the rules explained above:
if (i%3==0) {
if (i%5==0) {
"FizzBuzz"
} else {
"Buzz"
}
} else {
if (i%5==0) {
"Fizz"
} else {
i
}
}
This is not valid code but because the result of the ternary operator is inlined in the result expression it is used as input for the puts command.

For fun, here's another way:
puts (1..100).map {|i| (fb = [["Fizz"][i%3],["Buzz"][i%5]].compact.join).empty? ? i : fb}
And another:
(1..100).zip([nil,nil,"Fizz"]*34,[nil,nil,nil,nil,"Buzz"]*20).map {|a,b,c| b || c ? [b,c].join : a}

The ternary is a basic if-then structure.
The above is equivalent to...
if i%3 ==0
if i%5 == 0
"FizzBuzz"
else
"Buzz"
else
if i%5 == 0
"Fizz"
else
i
Or, using some parens...
puts i%3==0 ? ( i%5==0 ? "FizzBuzz" : "Buzz" ) : ( i%5==0 ? "Fizz" : i )

the flow is:
if (i%3 == 0) { // multiple of 3
if (i%5 == 0) { // multiple of 3 and 5
puts "FizzBuzz"
} else { // not multiple of 5, only of 3
puts "Buzz"
}
} else ( // not multiple of 3
if (i%5 == 0) { // multiple of 5, not of 3
puts "Fizz"
} else { // multiple of neither 5 nor 3
puts i
}
}

Just for fun. If you wanted to do it in C#. Here's a simple way. It basically starts with your for loop that will print numbers from 1 to 100. It then asks if your index "i" is divisible by 3 and 5 if true then print to the console "FizzBuzz". Else if your index "i" is divisible by 3 if true then print to the console "Fizz". Else if your index "i" is divisible by 5 if true then print to the console "Buzz". Else just print out "i" which is your integer. I added tabbing for better readability.
for(int i = 1; i <= 100; i++) {
string result = (i % 3 == 0 && i % 5 == 0) ?
"FizzBuzz" :
(i % 3 == 0) ?
"Fizz" :
(i % 5 == 0) ?
"Buzz" :
i.ToString();
Console.WriteLine(result);
}

Related

Having trouble with conditionals

I'm trying to define a method that returns "fizz" when the integer is divisible by 3, "buzz" when is divisible by 5 and "fuzzbuzz" when the number is divisible by both. I know 15 is divisible by both, however I don't know what I'm inputting wrong in my code. Thank you.
def fizzbuzz(int)
if int % 3 == 0
return "Fizz"
elsif int % 5 == 0
return "Buzz"
elsif int % 15 == 15
return "FizzBuzz"
else
end
end
So for conditionals, ruby evaluate the if, then the elsif, and finally the else in order. If anything condition evaluates to true, then the rest of the conditions are ignored.
def fizzbuzz(int)
if int % 3 == 0
return "Fizz"
elsif int % 5 == 0
return "Buzz"
elsif int % 15 == 15
return "FizzBuzz"
else
end
end
So let's look at a few examples based on your current code.
Say int = 3
We go to if int % 3 == 0, which is true, so the code return "Fizz" and nothing below that is evaluated.
Say int = 5,
int % 3 == 0 is false, we move onto the first elsif.
int % 5 == 0 is true, we return "Buzz"
Now let's look at 15,
int % 3 == 0 is true, so the code will return "Fizz" and ignores everything else, even if there are more conditions that will evaluate to be true after the if statement.
The order here is very important. You will want to check if int % 15 == 0 first, then move onto % 3 and % 5.

Ruby FizzBuzz using arrays, my logic seems right but it is getting an error

FizzBuzz, a classic problem, returns all the numbers up to N with a slight twist. If a number is divisible by 3, it is replaced with "fizz". If it's divisible by 5, it's replaced with "buzz". If it's divisible by both, it's replaced with "fizzbuzz"
I keep getting this Error message:
comparison of Fixnum with nil failed
Can someone explain this error message to me please? Also why is the code not working?
def fizz_buzz(n)
arr = (1..n).to_a
i = 0
while arr[i] < arr[n]
if i % 3 == 0 && i % 5 == 0
arr[i] = 'fizzbuzz'
elsif i % 3 == 0
arr[i] = 'fizz'
elsif i % 5 == 0
arr[i] = 'buzz'
else
arr[i] = i
i += 1
end
end
return arr
end
fizz_buzz(12)
Your conditions are just a bit off, give this a try:
def fizz_buzz(n)
arr = (1..n).to_a
i = 0
while i < n
if arr[i] % 3 == 0 && arr[i] % 5 == 0
arr[i] = 'fizzbuzz'
elsif arr[i] % 3 == 0
arr[i] = 'fizz'
elsif arr[i] % 5 == 0
arr[i] = 'buzz'
end
i+=1
end
return arr
end
Trying to access arr[n] puts you outside the bounds of the array which returns nil in Ruby.
You can update the code the ruby way, by using blocks and guards, I don't remember last time I used a while loop in ruby :)
Also, Array.new accepts a block as an argument which you can exploit to build your Array in a single step:
def fizz_buzz(n)
Array.new(n) do |index|
x = index + 1
case
when x % 3 == 0 && x % 5 == 0 then "fizzbuzz"
when x % 3 == 0 then "fizz"
when x % 5 == 0 then "buzz"
else x
end
end
end
Notice I used 1 as a base index and not 0, you can just remove x = index + 1 and replace x with index to have it working in a zero index base
A solution with a block instead of the while loop, and guards
def fizz_buzz(n)
arr = (1..n).to_a
0.upto(n - 1) do |i|
arr[i] = "fizzbuzz" and next if i % 3 == 0 && i % 5 == 0
arr[i] = "fizz" and next if i % 3 == 0
arr[i] = "buzz" if i % 5 == 0
end
arr
end
#brad-melanson beat me to the straight-forward answer to your question, so I'll share an answer which uses some common Ruby idioms (passing a range to the Array constructor and map), which simplify things, prevent you from having to do any iteration bookkeeping and prevent the possibility of off-by-one errors, out-of-bounds errors, etc.
def fizz_buzz(n)
Array(1..12).map do |n|
if n % 3 == 0 && n % 5 == 0
'fizzbuzz'
elsif n % 3 == 0
'fizz'
elsif n % 5 == 0
'buzz'
else
n
end
end
end
result = fizz_buzz 12
# result => [1, 2, "fizz", 4, "buzz", "fizz", 7, 8, "fizz", "buzz", 11, "fizz"]

Fizzbuzz switch statement

Long question but I think it is odd. I was playing around with ruby switch statements. Created a little fizzbuzz function to practice.
Initially created the code like this
def fizzbuzz(start_num, end_num)
fizzbuzz = []
(start_num..end_num).each do |x|
if x % 3 == 0 && x % 5 != 0
fizzbuzz << "fizz"
elsif x % 5 == 0 && x % 3 != 0
fizzbuzz << "buzz"
elsif (x % 3 == 0 && x % 5 == 0)
fizzbuzz << "fizzbuzz"
else
fizzbuzz << x.to_s
end
end
fizzbuzz
end
Works as expected. Then wanted to play with a switch statement. So I tried:
def fizzbuzz(start_num, end_num)
fizzbuzz = []
(start_num..end_num).each do |x|
case x
when x % 3 == 0
fizzbuzz << "fizz"
when x % 5 == 0
fizzbuzz << "buzz"
when (x % 3 == 0 && x % 5 == 0)
fizzbuzz << "fizzbuzz"
else
fizzbuzz << x.to_s
end
end
fizzbuzz
end
This time the code only prints out the number converted to a string. Then mistakenly I tried to add && to the end of every when statement like so
def fizzbuzz(start_num, end_num)
fizzbuzz = []
(start_num..end_num).each do |x|
case x
when x % 3 == 0 &&
fizzbuzz << "fizz"
when x % 5 == 0 &&
fizzbuzz << "buzz"
when (x % 3 == 0 && x % 5 == 0) &&
fizzbuzz << "fizzbuzz"
else
fizzbuzz << x.to_s
end
end
fizzbuzz
end
Interestingly this prints out the correct result. It is probably a trivial answer, but does anyone know why this is the case? It seems rather odd to me.
The when statements are doing a logical &&.
This has the side effect of concatenating your output when the condition is true.
The question you're actually asking, based on your comment, is what's going on with the when statements not seeming to work. The problem is that you wrote case x, which is evaluating x on-the-spot and comparing it to the when expressions.
Instead, use a "naked case", e.g.,
case
when (x % 3) == 0
# etc
Note also that this could be wrapped up a bit tighter, e.g.,
def fizzbuzz(start_num, end_num)
(start_num..end_num).collect do |x|
case
when (x % 3) == 0
"fizz"
when (x % 5) == 0
"buzz"
when (x % 3 == 0 && x % 5 == 0)
"fizzbuzz"
else
x.to_s
end
end
end
For the last piece of code, let's see one when condition in detail:
when x % 3 == 0 &&
fizzbuzz << "fizz"
Despite the indentation, it's equivalent to:
when x % 3 == 0 && fizzbuzz << "fizz"
Remember that && is short-circuit. The && expression returns its first argument if it is false. Otherwise, its second argument is evaluated and returned as the result.
So if x % 3 == 0 is false, then fizzbuzz << "fizz" is not executed. If x % 3 == 0 is true, fizzbuzz << "fizz" is executed. Exactly what is expected.

Lambdas and only 1 argument in case statements in Ruby

As I get it, you can use a proc/lambda inside, switch, for example:
is_even = ->(x) { x % 2 == 0 }
case number
when 0 then puts 'zero'
when is_even then puts 'even'
else puts 'odd'
end
As I understand it, and from the examples I see, is it that lambdas can be used in case statements ONLY IF they accept only 1 parameter, since you can't do a case statement with more than 1 argument (case a, b isn't possible, except if maybe these elements are enclosed into an array). So basically if you try to use a lambda with more than 1 parameter in a case statement the code is going to break. Correct me if I'm wrong.
It's because:
is_even = ->(x) { x % 2 == 0 }
is_even.lambda? #true
is "lambda" ( is_even = lambda(x) { x % 2 == 0 } is equivalent of above code)
There exist procs:
is_even = proc{|x| x % 2 == 0 } # Proc.new{|x| x % 2 == 0 }
is_even.lambda? #false
lambdas checks for number of arguments. procs on other hand doesn't check number of arguments.
ppp1 = Proc.new{|x| x % 2 == 0 } #=> #<Proc:0x507f300#(pry):152>
ppp1.call(2,3,4,5) #true
ppp2 = ->(x){ x % 2 == 0 } # => #<Proc:0x2c5ac40#(pry):158 (lambda)>
ppp2.(2,4) # ArgumentError: wrong number of arguments (2 for 1)
You can read more here.

ruby inject with conditional in block?

doing the first Project Euler question: summing the multiples of 3 and 5 between 1 and 1000, I came up with this (pretty simple)
sum = 0
1.upto(999) { |i| sum += i if 0 == i%3 || 0 == i%5 }
sum
but I thought this would work but it doesn't, can someone show me what I'm doing wrong, or why it doesn't work?
1.upto(999).inject(0) { |sum, i| sum + i if 0 == i%3 || 0 == i%5 }
thanks!
inject passes the result of the block through to the next iteration as the first argument. Your block will return nil when your if statement is false, which then gets passed back in as sum.
To get the correct answer, the block should return the current sum when it's false:
1.upto(999).inject(0) { |sum, i| (0 == i%3 || 0 == i%5) ? sum + i : sum }
Complementary answer: if you're about to tackle Euler problems you should start to build your own extensions of reusable code. In this case, the first extension would be Enumerable#sum:
module Enumerable
def sum
inject(0, :+)
end
end
And now you can write a solution that separates the condition of the summatory (you can read it out loud and it makes sense, that's typical of functional/declarative style):
1.upto(999).select { |x| x % 3 == 0 || x % 5 == 0 }.sum
You could even push it one step further and create Fixnum#divisible_by? so you can write:
1.upto(999).select { |x| x.divisible_by?(3) || x.divisible_by?(5) }.sum
More: here it's not a problem, but later on strict implementations (those using arrays) will require too much memory. Try then with laziness:
require 'lazy'
1.upto(999).lazy.select { |x| x % 3 == 0 || x % 5 == 0 }.sum
1.upto(999).inject(0) { |sum, i| sum += i if 0 == i%3 || 0 == i%5; sum }
Would also work (note the +=).
Or, use & proc which addresses self.
(1..999).select{|x| x%3==0||x%5==0}.inject &:+
(1..999).to_a.keep_if{|d| d%3 == 0 || d%5 == 0}.reduce(:+) for completeness.

Resources