FizzBuzz list comprehension
Question:
I was messing around with some different fizz buzz scripts as I learn python. I came across this one which works great but I can’t decipher how it works. I know how the normal fizz buzz works with a for loop and “if i % 3 == 0 and i % 5 == 0”. What has me stumped is how “Fizz”(not i%3) + “Buzz“(not i%5)” works.
x = ["Fizz"*(not i%3) + "Buzz"*(not i%5) or i for i in range(1, 100)]
Answers:
>>> 3%3
0
>>> not 3%3
True
>>>
>>> 3%2
1
>>> not 3%2
False
>>>
>>> if 0:
... print 'true'
...
>>> if 1:
... print 'true'
...
true
>>> False * 'Fizz'
''
>>> True * 'Fizz'
'Fizz'
You can see that 3%3
evaluates to 0
and 0
evaluates to False. 1
evaluates to true.
In the comprehension, it loops for every number from 1 to 99, and if i%3
evaluates to True
or i=3; 3%3
, then "Fizz"
is printed and the same is true for "Buzz"
if i%5 == 0
.
If neither "Fizz"
nor "Buzz"
get printed, the part before the or
here: "Fizz"*(not i%3) + "Buzz"*(not i%5) or i
evaluates to ''+''
so the or
part of the condition is printed, which is just i
.
In python you can replicate a string by using the multiplication operator:
print('aaa' * 3) # aaaaaaaaa
Also, boolean values are implicitly casted to integers on multiplication. Thus, if you do
"Fizz"*(not i%3)
First, the i%3 will return the result of the modulo. Then, the not
operator will convert it to either True
if the result was 0, or to False
otherwise (by casting it to boolean and then negating the value). By then applying a multiplication operator, False
turns to 0 and True
turns into 1.
Thus, if the number is divisible by 3, we get 0 as the result of the modulo, True
when applying not
, 1 when multiplying, and the string Fizz
replicated 1 time as the result of the multiplication. If it is not divisible, we get 0 as operand for the multiplication, effectively getting the string Fizz
replicated 0 times, thus an empty string.
The same goes for Buzz
, and the result for each i
in the range is just the concatenation of the two.
As you may know, the %
operator returns the remainder of a given dividend and divisor. This operation only returns 0
when the operation returns no remainder.
For example, if i % 3 == 0
, then 3
is a factor of i
. Therefore i % 3 and i % 5
will only return 0
when 3
or 5
can divide i
.
Thus not i % 3
will only return 1
if i
is divisible by 3
. The not
statement inverts the output, where a nonzero remainder (true
) becomes the 0 value (false
). Likewise, a zero remainder will become the value 1
(true
).
When a string like "Fizz"
is multiplied with a 0
, it will become the empty string. When multiplied with a 1
, it will remain the same. I believe this is due to the numerical representation of strings but someone can correct me if this is not the case.
Therefore, "Fizz"
and/or "Buzz"
will only exist in the output of that expression if i % 3
and/or i % 5
return 0
remainders. Meaning that i
is divisible by 3
and/or 5
.
List comprehension is expressed as
L = [mapping-expression for element in source-list if filter-expression]
now, replace “for element in source-list” part with
for i in range(1, 100)
which iterates over a list of containing integers 1 to 99 returning one integer at a time.
“mapping-expression” here is
"Fizz"*(not i%3) + "Buzz"*(not i%5) or i
which uses the integer i returned from “for i in range(1, 100)”
when i is divisible by 3 or 5, i % 3 and i % 5 returns 0, any other integer retuned when i is not divisible.
(not i % 3) # return True when i is divisible by 3
(not i % 5) # returns False when i is not divisible by 5
when the booleans returned from (not i % 3) or (not i % 5) is multiplied with strings “Fizz” and “Buzz”:
"Fizz" * True # returns "Fizz"
"Buzz" * False # returns ""
then the Strings returned above are concatenated
"Fizz" + ""
as a result “Fizz” is placed in the resulting list and this process goes on for each iteration, returning either “Fizz” or “Buzz” or sometimes times the integer i itself when both the string boolean multiplication returns a empty string “”. since
"" + "" or i # returns i
The resulting list is something like [1, 2, ‘Fizz’, 4, ‘Buzz’, ‘Fizz’, 7, 8, ‘Fizz’, ‘Buzz’, 11, ‘Fizz’, 13, 14, ‘FizzBuzz’ ……]
note : the optional “if filter-expression” is not used in the example.
My personal favorite:
#!python
from __future__ import print_function
def fizz_buzz(lower=1, upper=101):
return ["Fizz"*(x % 3 == 0) + "Buzz"*(x % 5 == 0) or str(x)
for x in range(lower, upper)]
print('n'.join(fizz_buzz()))
All the action is in a list comprehension expression and most of that is in conditional expression which either evaluates into a string (“Fizz”, or “Buzz” or “Fizz”+”Buzz”) or into a string representation of the integer being evaluated.
This relies on Python’s relatively unusual (perhaps even obscure) * operator for strings … to “multiply” a string via duplications and concatenation … and also on Python’s semantics regarding evaluation of “truth.” (In Python empty sequences: strings, lists, and tuples, as well as other empty containers, dictionaries and sets, and numerically zero values are all “false” — along with the special None and False objects).
Back to your question. Python treats “multiplying a string by zero” as an empty string. It also treats a Boolean object as 1 or 0 in any arithmetic context (such as multiplication). Thus the Boolean of not i%3 is True if the object is divisible by 3 … that is the modulo 3 operation returns a non-zero value. So each of these strings is “multiplied” by one or zero (the equivalent of calling int(not(bool(i%X))) … where X is 3 in one subexpression and 5 in the other. (Note: not is a unary operator, not a function; but it can be written like a function call because not(x) evaluates the same as not x … the extraneous parentheses are harmless in most cases).
I prefer returning (or yielding) the string conversion of the integer precisely because you can then uses the results from this expression in other functions in a natural way (for example when calling the string.join() method).
You can use:
["Fizzbuzz" if (i%3 == 0 and i %5 == 0) else "Fizz" if (i%3 == 0) else "Buzz" if (i%5 == 0) else i for i in x]
Hope this clears.
Try something like this:
def fizz_buzz(upper):
up_limit = upper +1
fbl = ['fb' if i%15 == 0 #fizzbuzz if divisible by both
else 'f' if i%3 == 0 #fizz if divisible by 3
else 'b' if i%5 == 0 #buzz if divisible by 5
else i #else present number
for i in range(1,up_limit)]
return fbl
I think the following approach helps me understanding the list comprehension structure.
def fizzBuzz(upper_limit):
def mapper(a):
result = ''
if a % 3 == 0:
result = result + 'Fizz'
if a % 5 == 0:
result = result + 'Buzz'
return result
return [mapper(x) if mapper(x) else x for x in range(1,upper_limit+1)]
fizzBuzz(16)
[1,2,'Fizz',4,'Buzz','Fizz',7,8,'Fizz','Buzz', 11,'Fizz',13,14,'FizzBuzz',16]
def fizzbuzz(n):
fizz_list = ["FizzBuzz" if (i % 3 == 0 and i % 5 == 0) else "Fizz" if (i % 3 == 0) else "Buzz" if (i % 5 == 0) else i
for i in range(1,n+1)]
print(fizz_list)
fizzbuzz(15)
I was messing around with some different fizz buzz scripts as I learn python. I came across this one which works great but I can’t decipher how it works. I know how the normal fizz buzz works with a for loop and “if i % 3 == 0 and i % 5 == 0”. What has me stumped is how “Fizz”(not i%3) + “Buzz“(not i%5)” works.
x = ["Fizz"*(not i%3) + "Buzz"*(not i%5) or i for i in range(1, 100)]
>>> 3%3
0
>>> not 3%3
True
>>>
>>> 3%2
1
>>> not 3%2
False
>>>
>>> if 0:
... print 'true'
...
>>> if 1:
... print 'true'
...
true
>>> False * 'Fizz'
''
>>> True * 'Fizz'
'Fizz'
You can see that 3%3
evaluates to 0
and 0
evaluates to False. 1
evaluates to true.
In the comprehension, it loops for every number from 1 to 99, and if i%3
evaluates to True
or i=3; 3%3
, then "Fizz"
is printed and the same is true for "Buzz"
if i%5 == 0
.
If neither "Fizz"
nor "Buzz"
get printed, the part before the or
here: "Fizz"*(not i%3) + "Buzz"*(not i%5) or i
evaluates to ''+''
so the or
part of the condition is printed, which is just i
.
In python you can replicate a string by using the multiplication operator:
print('aaa' * 3) # aaaaaaaaa
Also, boolean values are implicitly casted to integers on multiplication. Thus, if you do
"Fizz"*(not i%3)
First, the i%3 will return the result of the modulo. Then, the not
operator will convert it to either True
if the result was 0, or to False
otherwise (by casting it to boolean and then negating the value). By then applying a multiplication operator, False
turns to 0 and True
turns into 1.
Thus, if the number is divisible by 3, we get 0 as the result of the modulo, True
when applying not
, 1 when multiplying, and the string Fizz
replicated 1 time as the result of the multiplication. If it is not divisible, we get 0 as operand for the multiplication, effectively getting the string Fizz
replicated 0 times, thus an empty string.
The same goes for Buzz
, and the result for each i
in the range is just the concatenation of the two.
As you may know, the %
operator returns the remainder of a given dividend and divisor. This operation only returns 0
when the operation returns no remainder.
For example, if i % 3 == 0
, then 3
is a factor of i
. Therefore i % 3 and i % 5
will only return 0
when 3
or 5
can divide i
.
Thus not i % 3
will only return 1
if i
is divisible by 3
. The not
statement inverts the output, where a nonzero remainder (true
) becomes the 0 value (false
). Likewise, a zero remainder will become the value 1
(true
).
When a string like "Fizz"
is multiplied with a 0
, it will become the empty string. When multiplied with a 1
, it will remain the same. I believe this is due to the numerical representation of strings but someone can correct me if this is not the case.
Therefore, "Fizz"
and/or "Buzz"
will only exist in the output of that expression if i % 3
and/or i % 5
return 0
remainders. Meaning that i
is divisible by 3
and/or 5
.
List comprehension is expressed as
L = [mapping-expression for element in source-list if filter-expression]
now, replace “for element in source-list” part with
for i in range(1, 100)
which iterates over a list of containing integers 1 to 99 returning one integer at a time.
“mapping-expression” here is
"Fizz"*(not i%3) + "Buzz"*(not i%5) or i
which uses the integer i returned from “for i in range(1, 100)”
when i is divisible by 3 or 5, i % 3 and i % 5 returns 0, any other integer retuned when i is not divisible.
(not i % 3) # return True when i is divisible by 3
(not i % 5) # returns False when i is not divisible by 5
when the booleans returned from (not i % 3) or (not i % 5) is multiplied with strings “Fizz” and “Buzz”:
"Fizz" * True # returns "Fizz"
"Buzz" * False # returns ""
then the Strings returned above are concatenated
"Fizz" + ""
as a result “Fizz” is placed in the resulting list and this process goes on for each iteration, returning either “Fizz” or “Buzz” or sometimes times the integer i itself when both the string boolean multiplication returns a empty string “”. since
"" + "" or i # returns i
The resulting list is something like [1, 2, ‘Fizz’, 4, ‘Buzz’, ‘Fizz’, 7, 8, ‘Fizz’, ‘Buzz’, 11, ‘Fizz’, 13, 14, ‘FizzBuzz’ ……]
note : the optional “if filter-expression” is not used in the example.
My personal favorite:
#!python
from __future__ import print_function
def fizz_buzz(lower=1, upper=101):
return ["Fizz"*(x % 3 == 0) + "Buzz"*(x % 5 == 0) or str(x)
for x in range(lower, upper)]
print('n'.join(fizz_buzz()))
All the action is in a list comprehension expression and most of that is in conditional expression which either evaluates into a string (“Fizz”, or “Buzz” or “Fizz”+”Buzz”) or into a string representation of the integer being evaluated.
This relies on Python’s relatively unusual (perhaps even obscure) * operator for strings … to “multiply” a string via duplications and concatenation … and also on Python’s semantics regarding evaluation of “truth.” (In Python empty sequences: strings, lists, and tuples, as well as other empty containers, dictionaries and sets, and numerically zero values are all “false” — along with the special None and False objects).
Back to your question. Python treats “multiplying a string by zero” as an empty string. It also treats a Boolean object as 1 or 0 in any arithmetic context (such as multiplication). Thus the Boolean of not i%3 is True if the object is divisible by 3 … that is the modulo 3 operation returns a non-zero value. So each of these strings is “multiplied” by one or zero (the equivalent of calling int(not(bool(i%X))) … where X is 3 in one subexpression and 5 in the other. (Note: not is a unary operator, not a function; but it can be written like a function call because not(x) evaluates the same as not x … the extraneous parentheses are harmless in most cases).
I prefer returning (or yielding) the string conversion of the integer precisely because you can then uses the results from this expression in other functions in a natural way (for example when calling the string.join() method).
You can use:
["Fizzbuzz" if (i%3 == 0 and i %5 == 0) else "Fizz" if (i%3 == 0) else "Buzz" if (i%5 == 0) else i for i in x]
Hope this clears.
Try something like this:
def fizz_buzz(upper):
up_limit = upper +1
fbl = ['fb' if i%15 == 0 #fizzbuzz if divisible by both
else 'f' if i%3 == 0 #fizz if divisible by 3
else 'b' if i%5 == 0 #buzz if divisible by 5
else i #else present number
for i in range(1,up_limit)]
return fbl
I think the following approach helps me understanding the list comprehension structure.
def fizzBuzz(upper_limit):
def mapper(a):
result = ''
if a % 3 == 0:
result = result + 'Fizz'
if a % 5 == 0:
result = result + 'Buzz'
return result
return [mapper(x) if mapper(x) else x for x in range(1,upper_limit+1)]
fizzBuzz(16)
[1,2,'Fizz',4,'Buzz','Fizz',7,8,'Fizz','Buzz', 11,'Fizz',13,14,'FizzBuzz',16]
def fizzbuzz(n):
fizz_list = ["FizzBuzz" if (i % 3 == 0 and i % 5 == 0) else "Fizz" if (i % 3 == 0) else "Buzz" if (i % 5 == 0) else i
for i in range(1,n+1)]
print(fizz_list)
fizzbuzz(15)