Preliminary remarks :
- Functional idioms and imperative mutations doesn't play well (except when they do. Cf Scala). Here, both FP and Python lovers will likely be lost !
- Since there is exactly 2 possible directions, use a Boolean. It will prevent typo (trying to pass 'Left' for instance).
- Extract the main computation to a dedicated function. First benefice : you won't have to test for direction twice, which is error prone.
- (1st version) : instead of copying + updating via index (awkward), generate a brand new sequence.
That's an interesting question, since it requires both folding (to propagate the carry) and mapping from updated values (to apply mod, ie get the less significant bits).
Since it's not that common, I suggest to stick with a standard Python approach. Don't force an idiom if it obfuscates.
def carry_from_left(z, word_size):
mod = (1<<word_size) - 1
res = []
acc = 0
for a in z :
tot = a + acc
res.append(tot & mod)
acc = tot >> word_size
return (acc, res)
def carry(z, left_to_right = False, word_size=8):
if left_to_right :
return carry_from_left(z, word_size)
else:
acc, res = carry_from_left(reversed(z), word_size)
res.reverse() #inplace. Nevermind, since it's a brand new list.
return (acc, res)
Now, if this "map+fold" pattern occurs frequently, you can abstract it.
def fold_n_map (f, l, acc):
""" Apply a function (x, acc) -> (y, acc') cumulatively.
Return a tuple consisting of folded (reduced) acc and list of ys.
TODO : handle empty sequence, optional initial value, etc,
in order to mimic 'reduce' interface"""
res = []
for x in l :
y, acc = f(x, acc)
res.append(y)
return acc, res
def carry_fold_left(z, word_size):
mod = (1<<word_size) - 1
#Nearly a one-liner ! Replace lambda by named function for your Python fellows.
return fold_n_map(lambda x, acc: ((x+acc) & mod, (x+acc) >> word_size), z, 0)
def carry(z, left_to_right = False, word_size=8):
#idem
...
asserts (3 ó 4) so people can test their refactors easily. – tokland Jan 29 at 13:30