Using functional tools

Eddie Corns eddie at holyrood.ed.ac.uk
Mon May 6 15:10:48 EDT 2002


Pekka Niiranen <krissepu at vip.fi> writes:

>I would like to feed every second (or 3rd or 4th .etc) item in a list to
>a function.

>list = ['a', 'b', 'c', 'd', 'e']

>**some fancy map/filter -trick here**

>=> list = ['a', function('b'), 'c', function('d'), 'e']

>Is there a (functional) way without using for or while -structures ?

>If I only manipulate every second line of a file with that function,
>is there a faster way than reading the whole file into a list ?:

>list = open ("myfile.txt", "rb").readlines()
>**modify every 2nd line with set of functions**
>** write the whole list into a new file**

>The size of a file is max 2Mb.

>-pekka-


Here's a fairly generic functional that I sometimes use:

def trans (func, inp, acc=[]):
    if not inp:
        return acc
    return apply (trans, (func,) + func (inp, acc))

It's more generic than map or reduce in that you control exactly what gets
passed down to the next iteration.  You do what you like with the current
value and use an expression to chop out what gets passed down.  So a[1:] would
be like map in iterating over a list left to right, a[:-1] iterates backwards
and a[1:-1] would take off both ends at each iteration.

# first arg to func controls what to pass down
# second arg is accumulation of what we have so far
# third arg to trans is starting value

Some examples:

# add beginning and end moving towards middle
l1=[1,2,3,4,5,6,7,8,9]
trans (lambda a,b: (a[1:-1],[a[0]+a[-1]]+b),l1)
# -> [10, 10, 10, 10, 10]

# add consecutive pairs but skip every third
trans (lambda a,b: (a[3:],b+[a[0]+a[1]]),l1)
# -> [3, 9, 15]

l2=[1,2,3,4,5,4,3,2,1]
l3=[1,2,3,4,5,4,3,2,1,0]

# check for palindrome
trans (lambda a,b: (a[1:-1],b and a[0]==a[-1]),l2,1)
# -> 1 (ie TRUE)
trans (lambda a,b: (a[1:-1],b and a[0]==a[-1]),l3,1)
# -> 0 (ie FALSE)

Although you split input into
 a) what to send to next iteration
 b) what to manipulate in this iteration
they can overlap. eg

# is a list in ascending order?
trans (lambda a,b: (len(a) > 2 and a[1:], b and a[0] < a[1]),l1,1)
# -> 1
trans (lambda a,b: (len(a) > 2 and a[1:], b and a[0] < a[1]),l2,1)
# -> 0

Can split into multiple lists eg

c,s = trans (lambda x,y: (x[2:], (y[0]+x[0:1], y[1]+x[1:2])),\
                         re.split('([^0-9A-F])' , maca), ([], []))

splits a mac address given in just about any format xx-xx*,  xx:xx*, xxxx.xxxx
or xxxxxxxxxxxx into the numbers and the separators (if any).

Works with strings, lists and tuples.  Because Python doesn't do tail
recursion, it would fall over on large list and I'm too lazy to re-write it as
a loop but it would be quite easy.

For you example (using a step of 2):
trans (lambda a,b: (a[2:], b + [function(a[0])] + a[1:min(2,len(a))]), list[1:], [list[0]])

OK, I admit it's pushing the technique beyond the bounds of sanity but I did
want to highlight the possibility we have with Python's high level
list/sequence constructs for creating more generic transformers than is
available in scheme/lisp (where map etc. was taken from).  Not that this
changes my opinion that Python is for programmers who can't cope with Lisp! :)
(Including myself, lest anyone take insult).

Eddie



More information about the Python-list mailing list