I am trying to better understand list comprehension in Python. I completed an online challenge on codewars with a rather inelegant solution, given below.
The challenge was:
Given a list of even numbers and one odd, return the odd
Given a list of odd numbers and one even, return the even
My (inelegant) solution to this was:
def find_outlier(integers):
o = []
e = []
for i in integers:
if i % 2 == 0:
e.append(i)
else:
o.append(i)
# use sums to return int type
if len(o) == 1:
return sum(o)
else:
return sum(e)
Which works fine, but seems to be pretty brute force. Am I wrong in thinking that starting (most) functions with placeholder lists like o and e is pretty "noob-like"?
I would love to better understand why this solution works for the odd list, but fails on the even list, in an effort to better understand list comprehension:
def find_outlier(integers):
if [x for x in integers if x % 2 == 0]:
return [x for x in integers if x % 2 == 0]
elif [x for x in integers if x % 2 != 0]:
return [x for x in integers if x % 2 != 0]
else:
print "wtf!"
o = [1,3,4,5]
e = [2,4,6,7]
In[1]: find_outlier(o)
Out[1]: [4]
In[2]: find_outlier(e)
Out[2]: [2, 4, 6]
Where Out[2] should be returning 7.
Thanks in advance for any insights.
解决方案
Your attempt fails because the first if is always going to be true. You'll always have a list with at least 1 element; either the odd one out is odd and you tested a list with all even numbers, otherwise you have a list with the one even number in it. Only an empty list would be false.
List comprehensions are not the best solution here, no. Try to solve it instead with the minimum number of elements checked (the first 2 elements, if they differ in type get a 3rd to break the tie, otherwise iterate until you find the one that doesn't fit in the tail):
def find_outlier(iterable):
it = iter(iterable)
first = next(it)
second = next(it)
parity = first % 2
if second % 2 != parity:
# odd one out is first or second, 3rd will tell which
return first if next(it) % 2 != parity else second
else:
# the odd one out is later on; iterate until we find the exception
return next(i for i in it if i % 2 != parity)
The above will throw a StopIteration exception if there are either fewer than 3 elements in the input iterable, or there is no exception to be found. It also won't handle the case where there is more than one exception (e.g. 2 even followed by 2 odd; the first odd value would be returned in that case).