Skip to content

Commit

Permalink
list comprehensions. see
Browse files Browse the repository at this point in the history
  • Loading branch information
Skip Montanaro committed Aug 12, 2000
1 parent b16b835 commit 803d6e5
Show file tree
Hide file tree
Showing 8 changed files with 528 additions and 232 deletions.
19 changes: 14 additions & 5 deletions Doc/ref/ref5.tex
Original file line number Diff line number Diff line change
Expand Up @@ -152,13 +152,22 @@ \subsection{List displays\label{lists}}
square brackets:

\begin{verbatim}
list_display: "[" [expression_list] "]"
list_display: "[" [expression_list [list_iter]] "]"
list_iter: list_for | list_if
list_for: "for" expression_list "in" testlist [list_iter]
list_if: "if" test [list_iter]
\end{verbatim}

A list display yields a new list object. If it has no expression
list, the list object has no items. Otherwise, the elements of the
expression list are evaluated from left to right and inserted in the
list object in that order.
A list display yields a new list object. Its contents are specified
by providing either a list of expressions or a list comprehension.
When a comma-separated list of expressions is supplied, its elements are
evaluated from left to right and placed into the list object in that
order. When a list comprehension is supplied, it consists of a
single expression followed by one or more "for" or "if" clauses. In this
case, the elements of the new list are those that would be produced
by considering each of the "for" or "if" clauses a block, nesting from
left to right, and evaluating the expression to produce a list element
each time the innermost block is reached.
\obindex{list}
\indexii{empty}{list}

Expand Down
21 changes: 21 additions & 0 deletions Doc/tut/tut.tex
Original file line number Diff line number Diff line change
Expand Up @@ -1753,6 +1753,27 @@ \subsection{Functional Programming Tools \label{functional}}
0
\end{verbatim}

\subsection{List Comprehensions}

List comprehensions provide a concise way to create lists without resorting
to use of the \func{map()} or \func{filter()} functions. The resulting
construct tends often to be clearer than use of those functions.

\begin{verbatim}
>>> spcs = [" Apple", " Banana ", "Coco nut "]
>>> print [s.strip() for s in spcs]
['Apple', 'Banana', 'Coco nut']
>>> vec = [2, 4, 6]
>>> print [3*x for x in vec]
[6, 12, 18]
>>> vec1 = [2, 4, 6]
>>> vec2 = [4, 3, -9]
>>> print [x*y for x in vec1 for y in vec2]
[8, 6, -18, 16, 12, -36, 24, 18, -54]
>>> print [x+y for x in vec1 for y in vec2]
[6, 5, -7, 8, 7, -5, 10, 9, -3]
\end{verbatim}

\section{The \keyword{del} statement \label{del}}

There is a way to remove an item from a list given its index instead
Expand Down
7 changes: 6 additions & 1 deletion Grammar/Grammar
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ arith_expr: term (('+'|'-') term)*
term: factor (('*'|'/'|'%') factor)*
factor: ('+'|'-'|'~') factor | power
power: atom trailer* ('**' factor)*
atom: '(' [testlist] ')' | '[' [testlist] ']' | '{' [dictmaker] '}' | '`' testlist '`' | NAME | NUMBER | STRING+
atom: '(' [testlist] ')' | '[' [listmaker] ']' | '{' [dictmaker] '}' | '`' testlist '`' | NAME | NUMBER | STRING+
listmaker: test ( list_iter | (',' test)* [','] )
lambdef: 'lambda' [varargslist] ':' test
trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
subscriptlist: subscript (',' subscript)* [',']
Expand All @@ -88,3 +89,7 @@ classdef: 'class' NAME ['(' testlist ')'] ':' suite

arglist: (argument ',')* (argument [',']| '*' test [',' '**' test] | '**' test)
argument: [test '='] test # Really [keyword '='] test

list_iter: list_for | list_if
list_for: 'for' exprlist 'in' testlist [list_iter]
list_if: 'if' test [list_iter]
26 changes: 15 additions & 11 deletions Include/graminit.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,18 @@
#define factor 299
#define power 300
#define atom 301
#define lambdef 302
#define trailer 303
#define subscriptlist 304
#define subscript 305
#define sliceop 306
#define exprlist 307
#define testlist 308
#define dictmaker 309
#define classdef 310
#define arglist 311
#define argument 312
#define listmaker 302
#define lambdef 303
#define trailer 304
#define subscriptlist 305
#define subscript 306
#define sliceop 307
#define exprlist 308
#define testlist 309
#define dictmaker 310
#define classdef 311
#define arglist 312
#define argument 313
#define list_iter 314
#define list_for 315
#define list_if 316
7 changes: 7 additions & 0 deletions Lib/test/output/test_grammar
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,10 @@ selectors

atoms
classdef
['Apple', 'Banana', 'Coco nut']
[3, 6, 9, 12, 15]
[3, 4, 5]
[(1, 'Apple'), (1, 'Banana'), (1, 'Coconut'), (2, 'Apple'), (2, 'Banana'), (2, 'Coconut'), (3, 'Apple'), (3, 'Banana'), (3, 'Coconut'), (4, 'Apple'), (4, 'Banana'), (4, 'Coconut'), (5, 'Apple'), (5, 'Banana'), (5, 'Coconut')]
[(1, 'Banana'), (1, 'Coconut'), (2, 'Banana'), (2, 'Coconut'), (3, 'Banana'), (3, 'Coconut'), (4, 'Banana'), (4, 'Coconut'), (5, 'Banana'), (5, 'Coconut')]
good: got a SyntaxError as expected
[('Boeing', 'Airliner'), ('Boeing', 'Engine'), ('Ford', 'Engine'), ('Macdonalds', 'Cheeseburger')]
40 changes: 40 additions & 0 deletions Lib/test/test_grammar.py
Original file line number Diff line number Diff line change
Expand Up @@ -542,3 +542,43 @@ class C:
def meth1(self): pass
def meth2(self, arg): pass
def meth3(self, a1, a2): pass

# list comprehension tests
nums = [1, 2, 3, 4, 5]
strs = ["Apple", "Banana", "Coconut"]
spcs = [" Apple", " Banana ", "Coco nut "]

print [s.strip() for s in spcs]
print [3 * x for x in nums]
print [x for x in nums if x > 2]
print [(i, s) for i in nums for s in strs]
print [(i, s) for i in nums for s in [f for f in strs if "n" in f]]
try:
eval("[i, s for i in nums for s in strs]")
print "FAIL: should have raised a SyntaxError!"
except SyntaxError:
print "good: got a SyntaxError as expected"

suppliers = [
(1, "Boeing"),
(2, "Ford"),
(3, "Macdonalds")
]

parts = [
(10, "Airliner"),
(20, "Engine"),
(30, "Cheeseburger")
]

suppart = [
(1, 10), (1, 20), (2, 20), (3, 30)
]

print [
(sname, pname)
for (sno, sname) in suppliers
for (pno, pname) in parts
for (sp_sno, sp_pno) in suppart
if sno == sp_sno and pno == sp_pno
]
143 changes: 129 additions & 14 deletions Python/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ struct compiling {
#ifdef PRIVATE_NAME_MANGLING
char *c_private; /* for private name mangling */
#endif
int c_tmpname; /* temporary local name counter */
};


Expand Down Expand Up @@ -368,8 +369,10 @@ static int com_addconst(struct compiling *, PyObject *);
static int com_addname(struct compiling *, PyObject *);
static void com_addopname(struct compiling *, int, node *);
static void com_list(struct compiling *, node *, int);
static void com_list_iter(struct compiling *, node *, node *, char *);
static int com_argdefs(struct compiling *, node *);
static int com_newlocal(struct compiling *, char *);
static void com_assign(struct compiling *, node *, int);
static PyCodeObject *icompile(struct _node *, struct compiling *);
static PyCodeObject *jcompile(struct _node *, char *,
struct compiling *);
Expand Down Expand Up @@ -419,6 +422,7 @@ com_init(struct compiling *c, char *filename)
c->c_last_addr = 0;
c->c_last_line = 0;
c-> c_lnotab_next = 0;
c->c_tmpname = 0;
return 1;

fail:
Expand Down Expand Up @@ -941,18 +945,116 @@ parsestrplus(node *n)
}

static void
com_list_constructor(struct compiling *c, node *n)
com_list_for(struct compiling *c, node *n, node *e, char *t)
{
int len;
int i;
if (TYPE(n) != testlist)
REQ(n, exprlist);
/* exprlist: expr (',' expr)* [',']; likewise for testlist */
len = (NCH(n) + 1) / 2;
for (i = 0; i < NCH(n); i += 2)
com_node(c, CHILD(n, i));
com_addoparg(c, BUILD_LIST, len);
com_pop(c, len-1);
PyObject *v;
int anchor = 0;
int save_begin = c->c_begin;

/* list_iter: for v in expr [list_iter] */
com_node(c, CHILD(n, 3)); /* expr */
v = PyInt_FromLong(0L);
if (v == NULL)
c->c_errors++;
com_addoparg(c, LOAD_CONST, com_addconst(c, v));
com_push(c, 1);
Py_XDECREF(v);
c->c_begin = c->c_nexti;
com_addoparg(c, SET_LINENO, n->n_lineno);
com_addfwref(c, FOR_LOOP, &anchor);
com_push(c, 1);
com_assign(c, CHILD(n, 1), OP_ASSIGN);
c->c_loops++;
com_list_iter(c, n, e, t);
c->c_loops--;
com_addoparg(c, JUMP_ABSOLUTE, c->c_begin);
c->c_begin = save_begin;
com_backpatch(c, anchor);
com_pop(c, 2); /* FOR_LOOP has popped these */
}

static void
com_list_if(struct compiling *c, node *n, node *e, char *t)
{
int anchor = 0;
int a = 0;
/* list_iter: 'if' test [list_iter] */
com_addoparg(c, SET_LINENO, n->n_lineno);
com_node(c, CHILD(n, 1));
com_addfwref(c, JUMP_IF_FALSE, &a);
com_addbyte(c, POP_TOP);
com_pop(c, 1);
com_list_iter(c, n, e, t);
com_addfwref(c, JUMP_FORWARD, &anchor);
com_backpatch(c, a);
/* We jump here with an extra entry which we now pop */
com_addbyte(c, POP_TOP);
com_backpatch(c, anchor);
}

static void
com_list_iter(struct compiling *c,
node *p, /* parent of list_iter node */
node *e, /* element expression node */
char *t /* name of result list temp local */)
{
/* list_iter is the last child in a listmaker, list_for, or list_if */
node *n = CHILD(p, NCH(p)-1);
if (TYPE(n) == list_iter) {
n = CHILD(n, 0);
switch (TYPE(n)) {
case list_for:
com_list_for(c, n, e, t);
break;
case list_if:
com_list_if(c, n, e, t);
break;
default:
com_error(c, PyExc_SystemError,
"invalid list_iter node type");
}
}
else {
com_addopnamestr(c, LOAD_NAME, t);
com_push(c, 1);
com_node(c, e);
com_addoparg(c, CALL_FUNCTION, 1);
com_addbyte(c, POP_TOP);
com_pop(c, 2);
}
}

static void
com_list_comprehension(struct compiling *c, node *n)
{
/* listmaker: test list_iter */
char tmpname[12];
sprintf(tmpname, "__%d__", ++c->c_tmpname);
com_addoparg(c, BUILD_LIST, 0);
com_addbyte(c, DUP_TOP); /* leave the result on the stack */
com_push(c, 2);
com_addopnamestr(c, LOAD_ATTR, "append");
com_addopnamestr(c, STORE_NAME, tmpname);
com_pop(c, 1);
com_list_iter(c, n, CHILD(n, 0), tmpname);
com_addopnamestr(c, DELETE_NAME, tmpname);
--c->c_tmpname;
}

static void
com_listmaker(struct compiling *c, node *n)
{
/* listmaker: test ( list_iter | (',' test)* [','] ) */
if (TYPE(CHILD(n, 1)) == list_iter)
com_list_comprehension(c, n);
else {
int len = 0;
int i;
for (i = 0; i < NCH(n); i += 2, len++)
com_node(c, CHILD(n, i));
com_addoparg(c, BUILD_LIST, len);
com_pop(c, len-1);
}
}

static void
Expand Down Expand Up @@ -990,18 +1092,18 @@ com_atom(struct compiling *c, node *n)
else
com_node(c, CHILD(n, 1));
break;
case LSQB:
case LSQB: /* '[' [listmaker] ']' */
if (TYPE(CHILD(n, 1)) == RSQB) {
com_addoparg(c, BUILD_LIST, 0);
com_push(c, 1);
}
else
com_list_constructor(c, CHILD(n, 1));
com_listmaker(c, CHILD(n, 1));
break;
case LBRACE: /* '{' [dictmaker] '}' */
com_addoparg(c, BUILD_MAP, 0);
com_push(c, 1);
if (TYPE(CHILD(n, 1)) != RBRACE)
if (TYPE(CHILD(n, 1)) == dictmaker)
com_dictmaker(c, CHILD(n, 1));
break;
case BACKQUOTE:
Expand Down Expand Up @@ -1743,6 +1845,19 @@ com_assign_sequence(struct compiling *c, node *n, int assigning)
com_assign(c, CHILD(n, i), assigning);
}

static void
com_assign_list(struct compiling *c, node *n, int assigning)
{
int i;
if (assigning) {
i = (NCH(n)+1)/2;
com_addoparg(c, UNPACK_SEQUENCE, i);
com_push(c, i-1);
}
for (i = 0; i < NCH(n); i += 2)
com_assign(c, CHILD(n, i), assigning);
}

static void
com_assign_name(struct compiling *c, node *n, int assigning)
{
Expand Down
Loading

0 comments on commit 803d6e5

Please sign in to comment.