Tuesday, February 27, 2007

What is your FizzBuzz factor...

I was reading "Why Can't Programmers.. Program?" this morning.  It is sad - but near close to true.  I do not believe 199 out of 200 cannot program - but it isn't too far from the truth.  It manifests itself in many ways - from the examples in that article of just not being able to come up with simple algorithms - to my biggest pet peeve.  Not being able to phrase a comprehensible QUESTION

If you don't know how to ask for help solving your problem, that is - if you cannot phrase your problem as a set of specifications, detailed specifications - you'll never get a reasonable answer except purely by luck or accident.  More likely, what you get in response to a poorly framed question is an answer that appears to work (given the poorly specified requirements - typically without any of the boundary value conditions thought out), but doesn't.  And the "but doesn't" part is never discovered until "too late" (when it becomes impossible to fix due to "management says we cannot touch the code").

Anyway, when I read their FizzBuzz "specification", I immediately thought of how I would solve it :)  Using SQL of course!

ops$tkyte%ORA10GR2> with data
2 as
3 (select level l
4 from dual
5 connect by level <= 100)
6 select l,
7 case when mod(l,3)=0
8 then case when mod(l,5)=0
9 then 'FizzBuzz'
10 else 'Fizz'
11 end
12 when mod(l,5)=0
13 then 'Buzz'
14 else to_char(l)
15 end fb
16 from data
17 /

L FB
---------- ----------------------------------------
1 1
2 2
3 Fizz
4 4
5 Buzz
6 Fizz
7 7
8 8
9 Fizz
10 Buzz
11 11
12 Fizz
13 13
14 14
15 FizzBuzz
...
90 FizzBuzz
91 91
92 92
93 Fizz
94 94
95 Buzz
96 Fizz
97 97
98 98
99 Fizz
100 Buzz

100 rows selected.
POST A COMMENT

24 Comments:

Blogger Laurent Schneider said....

or if you do not want to repeat Fizz and Buzz and 5 then

SQL> exec for i in 1..100 loop dbms_output.put_line(i||' '||nvl(case mod(i,3) when 0 then 'Fizz' end||case mod(i,5) when 0 then 'Buzz' end,i));end loop
1 1
2 2
3 Fizz
4 4
5 Buzz
6 Fizz
7 7
8 8
9 Fizz
10 Buzz
11 11
12 Fizz
13 13
14 14
15 FizzBuzz
...
98 98
99 Fizz
100 Buzz

Tue Feb 27, 11:18:00 AM EST  

Anonymous Markku Uttula said....

Scratching head... the specification didn't specify whether to print the number also when it is a multiple of 5 or a multiple of both 3 and 5 (it only stated that when the number is a multiple of 3, output should be "Fizz" instead of the number, no similar mention for the other cases).

Of course, this could once again be my limited english kicking in, but to be sure and secure, I'd request more specific specifications :)

Tue Feb 27, 11:26:00 AM EST  

Anonymous Anonymous said....

I did it in PL/SQL ( I know, when it can be done in SQL....). I read it as wanting:
1
2
Fizz
4
5
Fizz
7
8
Fizz
10
11
Fizz
13
14
Buzz
16
.
.

I did it in python too. Just learning python, that is why I did it in PL/SQL first. Get a test/reference.

Tue Feb 27, 11:41:00 AM EST  

Blogger neil said....

Funny, that was a drinking game back in my freshman year of college. Never did make it to 100 :)

Love the SQL example.

Tue Feb 27, 11:41:00 AM EST  

Blogger EscVector said....

' VBScript FizBuzz

'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.

Main

Private Sub Main
For x = 1 to 100
WScript.Echo EfferVesce(x)
Next
End Sub

Private Function EfferVesce(ByVal TheNumber)
If 0 = (TheNumber mod 3) + (TheNumber Mod 5) Then
EfferVesce = "FizzBuzz"
ElseIf 0 = TheNumber Mod 3 Then
EfferVesce = "Fizz"
ElseIf 0 = TheNumber Mod 5 Then
EfferVesce = "Buzz"
Else
EfferVesce = TheNumber
End If
Next
End Function

Tue Feb 27, 11:46:00 AM EST  

Anonymous neil said....

Just curious Tom-what's the advantage (if any) of using a WITH clause rather than an inline view? I've never jumped on the WITH bandwagon-maybe its time to?

Tue Feb 27, 11:50:00 AM EST  

Anonymous Anonymous said....

On my first try, I used the same solution as Tom, but then I liked the idea of not repeating Fizz, Buzz and 5, so I switched to:

select n, nvl(
case when mod(n,3) = 0 then 'Fizz' end ||
case when mod(n,5) = 0 then 'Buzz' end,
to_char(n)) as answer
from (select level as n from dual connect by level <= 100)

Tue Feb 27, 12:16:00 PM EST  

Blogger EscVector said....

in coding, isn't it better to make the code understandable? Thus, coding in the fewest terms isn't necessarily optimal. If it takes more than 5 seconds to understand something that should take 2, doesn't that cost?

Tue Feb 27, 04:58:00 PM EST  

Blogger Gary Myers said....

I skipped MOD, but I'm surprised that people chose to code a MOD 3 and a MOD 5 for FizzBuzz rather than work out the 'lowest common multiple' themselves and code that it.

select case when (fb) = trunc(fb) then 'Fizzbuzz'
when (f) = trunc(f) then 'Fizz'
when (b) = trunc(b) then 'Buzz'
else to_char(rn) end fb
from (select rownum rn, rownum/15 fb, rownum/5 b, rownum/3 f from user_objects where rownum <=100)

Tue Feb 27, 08:12:00 PM EST  

Blogger Noons said....

Neil:
apart from being supposedly the correct "spelling" of ANSI-SQL, WITH can make reading the statement a little easier in the case of complex inline views. but I'm not aware of any inherent advantage in terms of efficiency. Tom, do you agree?

Gary:
Cool! ;-)

Wed Feb 28, 12:23:00 AM EST  

Blogger Laurent Schneider said....

with is often more efficient if the subquery is used more than once !


SQL> with t as (select 1 from dual group by cube(1,1,1,1,1,1)) select count(*) from t,t,t,t;

COUNT(*)
----------
16777216

Elapsed: 00:00:03.10
SQL> select count(*) from
2 (select 1 from dual group by cube(1,1,1,1,1,1)),
3 (select 1 from dual group by cube(1,1,1,1,1,1)),
4 (select 1 from dual group by cube(1,1,1,1,1,1)),
5 (select 1 from dual group by cube(1,1,1,1,1,1));

COUNT(*)
----------
16777216

Elapsed: 00:00:03.67

Wed Feb 28, 04:05:00 AM EST  

Blogger Tasos Vogiatzoglou said....

print '\n'.join(map(lambda x : str((x%5==0 and x%3==0) and 'FizzBuzz' or (x%5==0 and 'Buzz' or ( x%3 == 0 and 'Fizz' or x))),range(1,101)))

Python... kinda functional

Wed Feb 28, 05:58:00 AM EST  

Blogger Thomas Kyte said....

As Laurent noted above, with subquery factoring can have an edge when the same subquery is repeated over and over as the optimizer will tend to materialize it and reuse the results from temp.

So, sometimes "yes", it is a performance thing.

Wed Feb 28, 07:43:00 AM EST  

Blogger Noons said....

good info!
thanks, folks

Wed Feb 28, 08:12:00 AM EST  

Anonymous Anthony Wilson said....

So... do we win points by using MODEL, or get thrown out of the interview on mental health grounds?

Nobody in their right mind actually uses this stuff, right?

:)

select id
, n
from all_objects
where object_id = 1
model
dimension by (object_id id)
measures (object_name n)
rules (
n[for id from 1 to 100 increment 1] = to_char(cv(id)),
n[mod(id, 3) = 0] = 'fizz',
n[mod(id, 5) = 0] = 'buzz',
n[mod(id, 15) = 0] = 'fizzbuzz'
)
order by id
SQL(189)> /

ID N
---------- ---------
1 1
2 2
3 fizz
4 4
5 buzz
6 fizz
7 7
8 8
9 fizz
10 buzz
11 11
12 fizz
13 13
14 14
15 fizzbuzz
...
98 98
99 fizz
100 buzz

100 rows selected.

Thu Mar 01, 01:01:00 AM EST  

Blogger sumit said....

SELECT
DECODE(DECODE(MOD(LEVEL,3),0,'FIZZ')||DECODE(MOD(LEVEL,5),0,'BUZZ'),NULL,TO_CHAR(LEVEL),DECODE(MOD(LEVEL,3),0,'FIZZ')||DECODE(MOD(LEVEL,5),0,'BUZZ'))
"My Buzz"
FROM dual
CONNECT BY LEVEL<=100

Fri Mar 02, 01:08:00 AM EST  

Anonymous Sokrates said....

the strength of case

funny enough - nobody seemed to notice that one case is sufficient, due to the strength of (Oracle)-case

select l, case 0 when power("3", 2) + power("5", 2) then 'FizzBuzz'
when "3" then 'Fizz'
when "5" then 'Buzz'
else '' || l
end
from
(
select level l, mod(level,3) "3", mod(level, 5) "5"
from dual
connect by level<=100
)

Fri Mar 02, 08:04:00 AM EST  

Anonymous Anonymous said....

of course
simply "3" + "5"
instead of
power("3", 2) + power("5", 2)
would also do it

but anyway - the "avoiding the to_char" - trick is nice too, isn't it ?

Fri Mar 02, 08:22:00 AM EST  

Anonymous Anonymous said....

SELECT DECODE(MOD(LEVEL,15),0,'FizzBuzz',
5,'Buzz',10,'Buzz',
DECODE(MOD(LEVEL,3),0,'Fizz',LEVEL)) as FIZZBUZZ
FROM dual
CONNECT BY LEVEL <= 100

Fri Mar 09, 02:13:00 PM EST  

Blogger Wisnu said....

predicates
fizzbuzz(integer)
solution(integer)

clauses
fizzbuzz(I) :- I mod 15 = 0, write("fizzbuzz", "\n"), !.
fizzbuzz(I) :- I mod 3 = 0, write("fizz", "\n"), !.
fizzbuzz(I) :- I mod 5 = 0, write("buzz","\n"), !.
fizzbuzz(I) :- write(I, "\n"), !.

solution(X) :-
X <= 100, fizzbuzz(X), Y = X + 1, solution(Y).

goal
solution(1).

Sun Mar 11, 01:09:00 PM EDT  

Blogger Jared said....

I have in the past been asked to participate in developer interviews.

After some discussion I would show the applicants a simple schema (2 tables) with requirements for simple SQL queries to write.

After getting past the first 3 of 5 questions, I would add a table and a request for data that would require an outer join.

I have been pleasantly surprised to find that most developers we interviewed had little trouble with this test, though all applicants were used SQL Server syntax. (sigh)

Tue Mar 13, 05:18:00 PM EDT  

Anonymous dharanidhar karimojji said....

hai tom,
you have done a wrong
see specifications

http://ostermiller.org/calc/fizzbuzz.html

FizzBuzz is played by counting but saying "Buzz" if a number is divisible by three or contains the digit three and saying "Fizz" if a number is divisiable by seven or contains the digit seven.

The sequence starts: 1, 2, Buzz, 4, 5, Buzz, Fizz, 8, Buzz, 10, 11, Buzz, Buzz, Fizz, Buzz, 16, Fizz, Buzz, 19, 20, FizzBuzz, 22, ...



you have taken 3 and 5 see 13 not a fizz

Mon Mar 19, 02:40:00 AM EDT  

Blogger Thomas Kyte said....

you have done a wrong
see specifications


No, you have done wrong, the specifications are not at the URL you pointed to but rather - they were at the URL I pointed to initially.

You see, you just proved out a point - why programmers cannot program. You moved the goalpost here.

Mon Mar 19, 07:22:00 AM EDT  

Blogger Marcos said....

Here's the C#, took me < 2min...

for (int i = 1; i <= 100; i++)
{
string output = "";
if (i % 3 == 0) output += "Fizz";
if (i % 5 == 0) output += "Buzz";
if (output == "") output = i.ToString();
Console.WriteLine(output);
}

Fri May 11, 11:55:00 AM EDT  

POST A COMMENT

<< Home