Reputation: 1966
In the spirit of DRY, I was trying to replace and reduce a simple for-loop including if clause unsuccessfully after I read this article by Jonathan Hsu for a The Decent game. The For loop is as follows:
import sys
import math
# Auto-generated code below aims at helping you parse
# the standard input according to the problem statement.
# game loop
while 1:
max = 0
imax = 0
for i in range(8):
mountain_h = int(input()) # represents the height of one mountain, from 9 to 0. Mountain heights are provided from left to right.
if mountain_h > max:
max = mountain_h
imax = i
print(imax)
Based on Jonathan Hsu article I tried to replace for loop
by map()
and if-clause
by filter()
as follows:
from functools import reduce
# game loop
while 1:
m = [0,1,2,3,4,5,6,7]
max = 0
imax = 0
u = map(lambda n: mountain_h = int(input()) and print(imax), m)
r = filter(lambda n: mountain_h > max, n)
s = map(lambda n: max = mountain_h and imax = i, r)
# s = reduce(lambda acc, m: acc + n, r)
Any inputs to shed light on the realization of this replacement will be appreciated.
Upvotes: 1
Views: 295
Reputation: 531275
The part you aren't capturing is that your loop is implicitly producing a sequence of calls to input
; that's a job for the two-argument form of iter
, which will call input
until it returns a given value. iter(input, None)
can return an infinite number of values, since input()
will never return None
, but you can short-circuit that using itertools.islice
to limit it to 8 calls.
Once you have the inputs (or rather, an iterator that will produce your inputs), you can use enumerate
to tag each input with its sequence number and use max
(with an appropriate key
function) to find the largest value and its index.
from itertools import islice
while 1:
my_inputs = map(int, islice(iter(input, None), 8))
imax, m = max(enumerate(my_inputs), key=lambda x: x[1])
print(imax)
Piece by piece:
iter(input, None)
is an infinite stream of calls to input()
.islice(..., 8)
only yields the first 8 of them.map(int, ...)
calls int
on each value. (Warning: this won't catch a ValueError
raised by a non-integer input. There's no functional way to deal with the exception.)enumerate(my_inputs)
turns a stream like 3, 7, 2
into (0, 3), (1, 7), (2, 2)
.max(..., key=lambda x: x[1])
on the above sequence returns (1, 7)
, because 7
is the largest second element of all the tuples.You might also find the use of operator.itemgetter(1)
cleaner than an explicit lambda expression as the key
argument to max
.
A more Pythonic version of the same approach would use a generator function to produce the stream of integer values for max
/enumerate
to iterate over. It also makes it easier to handle input errors. For example:
def eight_integers():
for _ in range(8):
while True:
try:
x = int(input())
except ValueError:
continue
yield x
imax, m = max(enumerate(eight_integers()), key=lambda x: x[1])
Of course, you could also have eight_integers
yield an index and an integer itself, eliminating the need to use enumerate
.
Upvotes: 1
Reputation: 191738
So, map
is to update elements within a list, filter
is to remove elements, and reduce
is to collect/aggregate... You are never filtering or mapping data, only gathering input, then compaing against a running max
, which would be a use-case for reduce
, but still unncessary given Python's max()
function.
If you want Pythonic code for this, then it seems you really want a list comprehension, followed by a method that gets the max value & index within that list
values = [int(input()) for i in range(8)]
_max = max(values)
imax = len(values) - 1 - values[::-1].index(_max) # assuming you want the last index
But note that this is effectively replacing one loop with 3 and has nothing to do with DRY principles
Upvotes: 0