Reputation: 621
I am trying to create the built-in map() function in python. Here is my attempt:
def mapper(func, *sequences):
if len(sequences) > 1:
while True:
list.append(func(sequences[0][0],sequences[0][0],))
return list
return list
But I am really stuck, because if the user gives e.g. 100 arguments how do i deal with those?
Upvotes: 4
Views: 6966
Reputation: 63312
The prior answer by Pardhu attempts to decompose the problem into mapper
and zipper
. This is good, but it doesn't have a reasonable or working implementation of zipper
, also to more closely match Python's native zip
. This answer uses a more reasonable implementation of zipper
which uses iterables more correctly.
def zipper(*iterables):
# Ref: https://stackoverflow.com/a/77297966
if not iterables:
return (_ for _ in ()) # Without this the function runs forever.
iterables = [iter(it) for it in iterables]
try:
while True:
yield tuple([next(it) for it in iterables])
except StopIteration:
return
def mapper(func, *iterables):
# Ref: https://stackoverflow.com/a/77298108
return (func(*i) for i in zipper(*iterables)) # Note: `zipper` aims to match `zip`.
Usage example:
> mapper(lambda *i: sum(i), range(11, 15), range(20, 30))
<generator object mapper.<locals>.<genexpr> at 0x7ffa1a448900>
> list(_)
[31, 33, 35, 37]
Upvotes: 2
Reputation: 476669
You use the asterisk *
when you call the function:
def mapper(func, *sequences):
result = []
if len(sequences) > 0:
minl = min(len(subseq) for subseq in sequences)
for i in range(minl):
result.append(func(*[subseq[i] for subseq in sequences]))
return result
This produces:
>>> import operator
>>> mapper(operator.add, [1,2,4], [3,6,9])
[4, 8, 13]
By using the asterisk, we unpack the iterable as separate parameters in the function call.
Note that this is still not fully equivalent, since:
sequences
should be iterables, not per se lists, so we can not always index; andmap
in python-3.x is an iterable as well, so not a list.A more python-3.x-like map
function would be:
def mapper(func, *sequences):
if not sequences:
raise TypeError('Mapper should have at least two parameters')
iters = [iter(seq) for seq in sequences]
try:
while True:
yield func(*[next(it) for it in iters])
except StopIteration:
pass
Note however that most Python interpreters will implement map
closer to the interpreter than Python code, so it is definitely more efficient to use the builtin map
, than writing your own.
N.B.: it is better not to use variable names like
list
,set
,dict
, etc. since these will override (here locally) the reference to thelist
type. As a result a call likelist(some_iterable)
will no longer work.
Upvotes: 5
Reputation: 1957
Separating the part of combining of the sequence or sequences logic is much more easier to read and understand.
def mapper(func, *args):
for i in zip(*args):
yield func(*i)
Here we are using Python inbuilt zip
if you want to replace it entirely with your own implementation replace with zip
with the below zipper
function
def zipper(*args):
for i in range(len(args[0])):
index_elements = []
for arg in args:
index_elements.append(arg[i])
yield positional_elements
Upvotes: 0