Moving to functional programming

J

James Fassett

Hi all,

Had a simple problem that turned into an interesting solution and I
thought I would share it here.

I had a list of tuples that I needed to get the first value from and
generate a list.

tuple_list = (
('John', 'Doe'),
('Mark', 'Mason'),
('Jeff', 'Stevens'),
('Bat', 'Man')
)

# what I'd do in C or other procedural languages
result_list = []
for item in tuple_list:
result_list.append(item[0])

# the first Pythonic attempt using comprehensions
result_list = [x[0] for x in tuple_list]

# the final functional way
[result_list, _] = zip(*tuple_list)

I really like how Python allows me to do what I feel is the most
natural solution (for a seasoned procedural programmer) while allowing
a satisfying path towards a more functional approach.

Cheers,
James
 
B

bearophileHUGS

James Fassett:
# the first Pythonic attempt using comprehensions
result_list = [x[0] for x in tuple_list]

# the final functional way
[result_list, _] = zip(*tuple_list)

I really like how Python allows me to do what I feel is the most
natural solution (for a seasoned procedural programmer) while allowing
a satisfying path towards a more functional approach.

The list comprehension is quite more readable to me, so I suggest you
to use it. It's probably the default way to do it in Python.

If you want functional code this is another way (I have not tested the
relative performance but it may be quick):
.... ('John', 'Doe'),
.... ('Mark', 'Mason'),
.... ('Jeff', 'Stevens'),
.... ('Bat', 'Man')
.... )['John', 'Mark', 'Jeff', 'Bat']

Bye,
bearophile
 
C

craig75

James Fassett:
# the first Pythonic attempt using comprehensions
result_list = [x[0] for x in tuple_list]
# the final functional way
[result_list, _] = zip(*tuple_list)
I really like how Python allows me to do what I feel is the most
natural solution (for a seasoned procedural programmer) while allowing
a satisfying path towards a more functional approach.

The list comprehension is quite more readable to me, so I suggest you
to use it. It's probably the default way to do it in Python.

If you want functional code this is another way (I have not tested the
relative performance but it may be quick):

...     ('John', 'Doe'),
...     ('Mark', 'Mason'),
...     ('Jeff', 'Stevens'),
...     ('Bat', 'Man')
...   )>>> from operator import itemgetter
['John', 'Mark', 'Jeff', 'Bat']

Bye,
bearophile


Functional programmers love pattern matching (which I think makes the
code much easier to understand):

[x for (x,y) in tuple_list]

or

map(lambda (x,y):x, tuple_list)
 
S

sturlamolden

tuple_list = (
    ('John', 'Doe'),
    ('Mark', 'Mason'),
    ('Jeff', 'Stevens'),
    ('Bat', 'Man')
  )

# what I'd do in C or other procedural languages
result_list = []
for item in tuple_list:
    result_list.append(item[0])

Here are some various 'functional' solutions. Pick the one that fits
your problem best:

result_list = [fn for fn,ln in tuple_list]

result_generator = (fn for fn,ln in tuple_list)

result_list = map(lambda (fn,ln): fn, result_list)

result_generator = itertools.imap(lambda (fn,ln): fn, result_list)
 
T

Terry Reedy

James Fassett:
# the first Pythonic attempt using comprehensions
result_list = [x[0] for x in tuple_list]

This has the virtue of working for tuples of any length and doing the
minimal work required.
# the final functional way
[result_list, _] = zip(*tuple_list)

This requires the tuples in tuple_list to be of length 2. It also
produces a second list that is then tossed away.
The list comprehension is quite more readable to me, so I suggest you
to use it. It's probably the default way to do it in Python.

It also has two virtues that the non-equivalent alternative lacks.
If you want functional code this is another way (I have not tested the
relative performance but it may be quick):
... ('John', 'Doe'),
... ('Mark', 'Mason'),
... ('Jeff', 'Stevens'),
... ('Bat', 'Man')
... )['John', 'Mark', 'Jeff', 'Bat']

This again makes just one list from tuples of any length.

Some of the other alternatives in another post do minimal work but only
work with pairs.

tjr
 
G

George Sakkis

Hi all,

Had a simple problem that turned into an interesting solution and I
thought I would share it here.

I had a list of tuples that I needed to get the first value from and
generate a list.

tuple_list = (
    ('John', 'Doe'),
    ('Mark', 'Mason'),
    ('Jeff', 'Stevens'),
    ('Bat', 'Man')
  )

# what I'd do in C or other procedural languages
result_list = []
for item in tuple_list:
    result_list.append(item[0])

# the first Pythonic attempt using comprehensions
result_list = [x[0] for x in tuple_list]

# the final functional way
[result_list, _] = zip(*tuple_list)

I really like how Python allows me to do what I feel is the most
natural solution (for a seasoned procedural programmer) while allowing
a satisfying path towards a more functional approach.

First off, what exactly does make you think of the last approach as
"functional" ? It relies on positional arguments, tuple unpacking and
the signature of zip(), none of which are functional in the usual
programming language sense of the word. Second, it is less readable,
robust and efficient than the list comprehension.

An example of a really functional approach without all these drawbacks
would be bearophile's map(itemgetter(0), tuple_list) since
itemgetter() is a high order function (more specifically, a function
that returns another function). The list comprehension is still the
most pythonic approach though.

Regards,
George
 
J

James Fassett

It relies on positional arguments, tuple unpacking and
the signature of zip(),

It moreso relies on the fact that:
t1 = (0,1,2,3)
t2 = (7,6,5,4)
[t1, t2] == zip(*zip(t1, t2))
True

This is mathematically true given the definition of zip. To me that is
very functional. Basically, unpacking a pair list into zip is the
canonical definition of unzipping the list (which is exactly my
intention).
Second, it is less readable,

For a Python programmer - you are correct. For someone familiar with
the use of zip (as described above) - I wonder. Since I am new to this
I can't say for sure. If I posted the same code to a Haskell list or a
ML list would their opinion be the same?
robust and efficient than the list comprehension.

I don't know the internals of how the Python interpreter treats list
comprehensions and zip but it seems reasonable to assume an extra list
is created for the zip approach.

However, in the limited functional code I have seen this is actually a
common practice. I would suppose in languages with lazy evaluation
this isn't a problem - but in Python it would be.
The list comprehension is still the most pythonic approach though.

I agree that it is more Pythonic and preferable in this case.

Cheers,
James
 
B

bruno.desthuilliers

It relies on positional arguments, tuple unpacking and
the signature of zip(),

It moreso relies on the fact that:
t1 = (0,1,2,3)
t2 = (7,6,5,4)
[t1, t2] == zip(*zip(t1, t2))

True

This is mathematically true given the definition of zip. To me that is
very functional. Basically, unpacking a pair list into zip is the
canonical definition of unzipping the list (which is exactly my
intention).
Second, it is less readable,

For a Python programmer - you are correct. For someone familiar with
the use of zip (as described above) - I wonder. Since I am new to this
I can't say for sure. If I posted the same code to a Haskell list or a
ML list would their opinion be the same?

You might find interesting than Python's list comprehensions were
stolen from Haskell then !-)
I don't know the internals of how the Python interpreter treats list
comprehensions

According to a post on the pypy team's blog, mostly as sugar candy for
the procedural version (IOW: the generated byte-code will be roughly
equivalent).
and zip but it seems reasonable to assume an extra list
is created for the zip approach.

However, in the limited functional code I have seen this is actually a
common practice. I would suppose in languages with lazy evaluation
this isn't a problem - but in Python it would be.

Python has some kind of lazy evaluation too - look for yield,
generator expressions, iterators, and the itertools package.
 
T

Terry Reedy

James Fassett wrote:
I don't know the internals of how the Python interpreter treats list
comprehensions and zip but it seems reasonable to assume an extra list
is created for the zip approach.

Minor times differences between this and that way of writing things tend
to be version and even system dependent. In 3.0, for instance, zip
produces an iterator, not a list. So it will be faster. On the other
hand, list comprehensions will be a bit slower to fix what many,
including Guido, consider to be a slight design bug.
 
P

Paul Rubin

James Fassett said:
tuple_list = (
('John', 'Doe'),
('Mark', 'Mason'),
('Jeff', 'Stevens'),
('Bat', 'Man')
)
# the final functional way
[result_list, _] = zip(*tuple_list)

That's really ugly IMO. I'd use:

result_list = list(x for (x,y) in tuple_list)

I don't like square-bracket listcomps because they leak the index
variables to the outside.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,225
Members
46,815
Latest member
treekmostly22

Latest Threads

Top