8

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
Mechanical snail
  • 29,755
  • 14
  • 88
  • 113
Damian
  • 663
  • 1
  • 9
  • 12

6 Answers6

16

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.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
11

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.

Jorge Ferreira
  • 96,051
  • 25
  • 122
  • 132
7

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}
glenn mcdonald
  • 15,290
  • 3
  • 35
  • 40
  • 1
    This is wrong. The specification states you should only print the number if you don't print fizz or buzz. – mxcl Nov 27 '10 at 11:44
3

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 )
Jarrett Meyer
  • 19,333
  • 6
  • 58
  • 52
1

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
    }
}
Nathan Fellman
  • 122,701
  • 101
  • 260
  • 319
0

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);
    }
user3225968
  • 141
  • 1
  • 9