I have list of tuples like this:
rounds = [('R', 'S'), ('S', 'R'), ('S', 'R'), ('R', 'P'), ('S', 'S'),
('P', 'S'), ('P', 'S'), ('S', 'P'), ('R', 'R'), ('R', 'S')]
that are simulating RPS game also i have a function like this:
def RPS_winner(weapon1, weapon2):
if weapon1 == weapon2:
return 0
elif (weapon1, weapon2) in [('R', 'S'), ('P', 'S'), ('P', 'S')]:
return 1
else:
return 2
how do i use map() to derive a list of the winners in those 10 rounds? i know is starting like this: list(map(RPS_winner, ....)
The map() function iterates over all elements in a list (or a tuple), applies a function to each and returns a new iterator of the new elements. In this syntax, fn is the name of the function that will call on each element of the list. In fact, you can pass any iterable to the map() function, not just a list or tuple.
Convert to a list. If you want to convert the result of map() to a list, use list() . For example, apply len() to a list of strings to convert it to a list of the number of characters. In the second argument of map() , not only a list but also an iterable such as a tuple or range can be specified.
The best way to apply a function to each element of a tuple is the Python built-in map(function, iterable) function that takes a function and an iterable as arguments and applies the function to each iterable element. An alternate way is to use list comprehension.
Itertools provides starmap
for this.
from itertools import starmap
rounds = [('R', 'S'), ('S', 'R'), ('S', 'R'), ('R', 'P'), ('S', 'S'),
('P', 'S'), ('P', 'S'), ('S', 'P'), ('R', 'R'), ('R', 'S')]
def RPS_winner(weapon1, weapon2):
if weapon1 == weapon2:
return 0
elif (weapon1, weapon2) in [('R', 'S'), ('P', 'S'), ('P', 'S')]:
return 1
else:
return 2
list(starmap(RPS_winner, rounds))
# [1, 2, 2, 2, 0, 1, 1, 2, 0, 1]
You don't need map
for something like that, and in fact would gain readability by using a list comprehension:
>>> [RPS_winner(a, b) for a, b in rounds]
[1, 2, 2, 2, 0, 1, 1, 2, 0, 1]
Another possibility is to use itertools.starmap
, which is designed precisely for that:
from itertools import starmap
list(starmap(RPS_winner, rounds))
And, of course, you can do the same by hand:
list(map(lambda ab: RPS_winner(*ab), rounds)
which, if you intend to use on very long list of rounds
, would benefit being rewritten as:
def RPS_winner_star(ab):
return RPS_winner(*ab)
list(map(RPS_winner_star, rounds))
Note
A justifiable reason to request the use of map
or the like is that rounds
is in fact not a list but another iterator. In that case, it's nice to obtain a new iterator that spits out winners as rounds
go, without making lists. For example, you could then "pipe" that resulting iterator into a Counter
:
irounds = generate_rounds(n=1_000_000) # a generator
iwinner = map(RPS_winner_star, irounds)
score_board = Counter(iwinner)
Here is a full example and timings:
(Note: the generate_rounds
generator is now predictably repetitive in an effort to reduce its own time in the overall measurements; we also now subtract the time spent in the generator in the time comparisons at the end).
import random
from collections import Counter
from itertools import starmap
def generate_rounds(n):
choices = list('RPS')
m = len(choices)
for k in range(n):
i = k % m
j = (k // m) % m
yield choices[i], choices[j]
def f_onlygen(n):
for _ in generate_rounds(n):
pass
def f_map(n):
irounds = generate_rounds(n) # a generator
iwinner = map(RPS_winner_star, irounds)
return Counter(iwinner)
def f_starmap(n):
irounds = generate_rounds(n) # a generator
iwinner = starmap(RPS_winner, irounds)
return Counter(iwinner)
def f_listmap(n):
rounds = list(generate_rounds(n))
winner = list(map(RPS_winner_star, rounds))
return Counter(winner)
def f_listcomprehension(n):
rounds = list(generate_rounds(n))
winner = [RPS_winner(a, b) for a, b in rounds]
return Counter(winner)
def f_comprehension(n):
irounds = generate_rounds(n)
winner = [RPS_winner(a, b) for a, b in rounds]
return Counter(winner)
Measurements:
n = 1_000_000
t = {}
t['onlygen'] = %timeit -o f_onlygen(n)
t['map'] = %timeit -o f_map(n)
t['starmap'] = %timeit -o f_starmap(n)
t['listmap'] = %timeit -o f_listmap(n)
t['listcomprehension'] = %timeit -o f_listcomprehension(n)
t['comprehension'] = %timeit -o f_comprehension(n)
Results:
res = sorted([
(k, v.average, v.average - t['onlygen'].average)
for k, v in t.items()
], key=lambda tup: tup[2])
print(f'{"name":<17} {"total":<6} above onlygen')
for name, tot, rel in res:
print(f'{name:<17} {tot*1000:3.0f} ms, {rel*1000:3.0f} ms')
name total above onlygen
onlygen 172 ms, 0 ms
comprehension 235 ms, 62 ms
starmap 376 ms, 204 ms
map 432 ms, 260 ms
listcomprehension 470 ms, 298 ms
listmap 482 ms, 310 ms
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With