Python vs Scheme: Simple list comprehensions

Python has a nifty feature called list comprehensions. Originally borrowed from Haskell, this is a (mostly) compact way to generate lists, without having to resort to the map and filter functions (which are often considered less clear and tend to rely on Python’s underpowered lambda).

Scheme, being a functional language, has an entirely different attitude toward map, filter and friends. Their use is encouraged, rather than being downplayed. In spite of that, there are situations where list comprehensions (or some form thereof) would be useful. So, SRFI-42 introduces “eager comprehensions”.

Rather than providing one construct, SRFI-42 has a number of forms, some of which seem to exist for efficiency reasons. For now, I’m just going to look at list-ec, which (in its most basic form) is a close cousin of Python’s list comprehensions.

Let’s run a simple example in Chicken:

> (use syntax-case)
; loading /usr/local/lib/chicken/3/syntax-case.so ...
; loading /usr/local/lib/chicken/3/syntax-case-chicken-macros.scm ...
> (use srfi-42)
; loading /usr/local/lib/chicken/3/srfi-42.scm ...
; loading /usr/local/lib/chicken/3/srfi-42-support.so ...

> (list-ec (: i 5) (* i i))
(0 1 4 9 16)

(Note: srfi-42 is an egg that needs to be installed first. It depends on another egg, syntax-case. Make sure these are installed, and imported in the correct order, or the examples won’t work!)

In Python, this would look like:

>>> [i*i for i in range(5)]
[0, 1, 4, 9, 16]

In this case, we could easily write this with a map… if Scheme had something similar to Python’s range function, which it doesn’t. In fact, the lack of such a construct seems to have been a major motivation for writing SRFI-42. The author states:

“The origin of this SRFI is my frustration that there is no simple [form] for the list of integers from 0 to n-1. With this SRFI it is (list-ec (: i n) i).”

Moreover, much like in Python, eager comprehensions are capable of more powerful expressions, that are not so easily written using map. Like this one, which isn’t terribly complex:

> (list-ec (: n 1 4) (: i n) (list n i))
((1 0) (2 0) (2 1) (3 0) (3 1) (3 2))

# Python:
>>> [(n,i) for n in range(1, 4) for i in range(n)]
[(1, 0), (2, 0), (2, 1), (3, 0), (3, 1), (3, 2)]

Much, much more is possible using SRFI-42, most of which I don’t understand yet :-), so for now I’m sticking to these simple examples. There will probably be a part II to this post, at some point. However, as a teaser, here’s a way to write Python’s enumerate (that works on strings and lists and probably some other types):

> (define (enumerate seq)
>   (list-ec (: x (index i) seq) (list i x)))

> (enumerate "hello")
((0 #\h) (1 #\e) (2 #\l) (3 #\l) (4 #\o))
> (enumerate '(guido larry matz))
((0 guido) (1 larry) (2 matz))

1 Comment

  1. Ozzi Lee said,

    January 9, 2008 @ 9:08 am

    For an equivalent to Python’s range, you might want to check out the iota function in srfi-1.

    http://srfi.schemers.org/srfi-1/srfi-1.html#iota

    Keep up the good work!

RSS feed for comments on this post