Discussion:
makelist vs create_list
Kostas Oikonomou
2007-04-07 00:12:14 UTC
Permalink
This is perhaps not a subject for the 5.12 release, but I'm wondering
why Maxima has both makelist() and create_list().

I read the documentation, and as far as I can tell, the functionality of
create_list is a superset of that of makelist.

Kostas
Stavros Macrakis
2007-04-07 13:49:56 UTC
Permalink
...I'm wondering why Maxima has both makelist() and create_list().
Bad design. Of course there shouldn't be two redundant functions like this.

There is also a serious problem with the design of create_list. Its
generalization to multiple variables is very useful and unproblematic.
On the other hand, the fact that it allows either lists or ranges
defined by integers makes its semantics weird, with the interpretation
of its arguments depending on their values. Consider:

(a:1,b:2,c:3,d:4)$
create_list(i*j, i, a, b, j, c, d) => [3,4,6,8]
vs.
(a:[1,2],j:[3,4],d:[5,6])
create_list(i*j, i, a, b, j, c, d) =>
[[3, 4], [3, 4], [3, 4], [3, 4], [6, 8], [6, 8], [6, 8], [6, 8]]

This is disastrously poor design. With identical syntax, in the first
case, i and j are treated as variable names; in the second case, i, b,
and c, are considered as variable names. We should remove the "range
defined by integers" case from create_list.

-s
Stavros Macrakis
2007-04-07 14:11:05 UTC
Permalink
...I'm wondering why Maxima has both makelist() and create_list().
Another weird thing about create_list: it is in the macdes.lisp source
file, which contains the documentation-related functions Example,
Describe, and Apropos; create_list certainly doesn't belong there.

-s
Robert Dodier
2007-04-07 14:34:55 UTC
Permalink
Post by Stavros Macrakis
There is also a serious problem with the design of create_list. Its
generalization to multiple variables is very useful and unproblematic.
On the other hand, the fact that it allows either lists or ranges
defined by integers makes its semantics weird, with the interpretation
of its arguments depending on their values.
Well, map already allows multiple arguments which are lists,
and makelist allows a range defined by integers, but only one index.
How about if we generalize makelist to take multiple indices
and cut create_list entirely.

Robert
Kostas Oikonomou
2007-04-08 13:29:58 UTC
Permalink
From the user's viewpoint, the most important thing would
be to eliminate one of the two.
My vote goes to retaining the name "makelist", and
generalizing its functionality.

Kostas
Post by Robert Dodier
Post by Stavros Macrakis
There is also a serious problem with the design of create_list. Its
generalization to multiple variables is very useful and unproblematic.
On the other hand, the fact that it allows either lists or ranges
defined by integers makes its semantics weird, with the interpretation
of its arguments depending on their values.
Well, map already allows multiple arguments which are lists,
and makelist allows a range defined by integers, but only one index.
How about if we generalize makelist to take multiple indices
and cut create_list entirely.
Robert
Stavros Macrakis
2007-04-09 00:54:42 UTC
Permalink
Post by Robert Dodier
How about if we generalize makelist to take multiple indices
and cut create_list entirely.
I agree that we should generalize makelist. But if we allow it to
take BOTH multiple indices and a range defined by i,lowval,highval we
run into the ambiguity I mentioned in my previous mail.

Something that would allow both in a much cleaner way would be some
sort of notation for an integer sequence, e.g. a .. b, so that you
could have

makelist( i*j, i, 1 .. 3, j, 3 .. 4)

Then there wouldn't be this disaster of the interpretation of the
arguments depending on their value. The simple way to implement this
would be to have a..b expand to [a, a+1, ... , b]. A more
sophisticated implementation (but which would require a lot more work
to do fully) would have ".." remain unexpanded.

The main hesitation I have about using ".." in this way is that it
covers one rather special case, not including other step sizes etc.
It gets complicated to have (2..5)/3 represent [ 2/3, 1, 4/3, 5/3]
without expanding it, because there are lots of other cases, e.g.
reverse(2..5), concat(2..5, 3..7), etc., none of which are currently
simplifying functions.

-s
Richard Fateman
2007-04-09 01:21:08 UTC
Permalink
Notation for this concept in Mathematica is embodied in the Table[] command.

◼ Table[expr, {k}] generates a list of k copies of expr.
◼ Table[expr, {i, k}] generates a list of the values of expr when i runs from 1 to k.
◼ Table[expr, {i, a, b}] starts with i = a.
◼ Table[expr, {i, a, b, s}] uses steps s.
◼ Table[expr, {i, a, b}, {j, c, d}, … ] gives a nested list. The list associated with i is outermost.

The order of evaluation is non-standard.

In Maple, you see notations like i=a..b
In Commercial Macsyma, there is also a..b notation

RJF
Richard Fateman
2007-04-09 15:02:22 UTC
Permalink
We could implement s command named Table. You might say, wouldn't
Mathematica sue us?
Uh, when we sue them for using % for the last expression?

We could even have an optional argument like

Type=list
Type=array
Type=sparse_array or hashtable

Type=vector
Type=matrix

RJF


So the idea is
-----Original Message-----
Sent: Monday, April 09, 2007 7:28 AM
Cc: 'Stavros Macrakis'; 'Robert Dodier'; 'Maxima'
Subject: Re: [Maxima] makelist vs create_list
I vote for the Mathematica notation, with [...] in place of
{...}.
Kostas
Post by Richard Fateman
Notation for this concept in Mathematica is embodied in the
Table[] command.
Post by Richard Fateman
? Table[expr, {k}] generates a list of k copies of expr.
? Table[expr, {i, k}] generates a list of the values of
expr when i runs from 1 to k.
Post by Richard Fateman
? Table[expr, {i, a, b}] starts with i = a.
? Table[expr, {i, a, b, s}] uses steps s.
? Table[expr, {i, a, b}, {j, c, d}, . ] gives a nested
list. The list associated with i is outermost.
Kostas Oikonomou
2007-04-09 14:27:42 UTC
Permalink
I vote for the Mathematica notation, with [...] in place of
{...}.

Kostas
Post by Richard Fateman
Notation for this concept in Mathematica is embodied in the Table[] command.
◼ Table[expr, {k}] generates a list of k copies of expr.
◼ Table[expr, {i, k}] generates a list of the values of expr when i runs from 1 to k.
◼ Table[expr, {i, a, b}] starts with i = a.
◼ Table[expr, {i, a, b, s}] uses steps s.
◼ Table[expr, {i, a, b}, {j, c, d}, … ] gives a nested list. The list associated with i is outermost.
Robert Dodier
2007-04-09 14:16:08 UTC
Permalink
Post by Stavros Macrakis
Post by Robert Dodier
How about if we generalize makelist to take multiple indices
and cut create_list entirely.
I agree that we should generalize makelist. But if we allow it to
take BOTH multiple indices and a range defined by i,lowval,highval we
run into the ambiguity I mentioned in my previous mail.
To be clear, I want makelist to recognize arguments like this:

makelist (expr, i, i0, i1, j, j0, j1, k, k0, k1, ...)

where i, j, k, ... are symbols and i0, i1, j0, j1, k0, k1, ... are integers.

makelist also recognizes makelist(expr, x, L) where L is a list.
I guess I would suggest cutting that. Allowing that and also the
notation with integers leads to ambiguity as noted before.
map(lambda([x], expr), L) has the same effect (ignoring
evaluation subtleties) as makelist(expr, x, L) so we don't really
need the latter, and map already allows multiple arguments too.
Post by Stavros Macrakis
Something that would allow both in a much cleaner way would be some
sort of notation for an integer sequence, e.g. a .. b, so that you
could have
makelist( i*j, i, 1 .. 3, j, 3 .. 4)
Then there wouldn't be this disaster of the interpretation of the
arguments depending on their value. The simple way to implement this
would be to have a..b expand to [a, a+1, ... , b]. A more
sophisticated implementation (but which would require a lot more work
to do fully) would have ".." remain unexpanded.
I like the idea of recognizing ellipsis notation but I wouldn't
bother changing makelist if it were available. That said, it
would be useful in other places so I am in favor of pursuing it.

I've never liked the notation .. for an ellipsis.
Why not type 3 dots and have a proper ellipsis.
Typing .. instead of ... is one of those unhelpful abbrvtions.
Post by Stavros Macrakis
The main hesitation I have about using ".." in this way is that it
covers one rather special case, not including other step sizes etc.
It gets complicated to have (2..5)/3 represent [ 2/3, 1, 4/3, 5/3]
without expanding it, because there are lots of other cases, e.g.
reverse(2..5), concat(2..5, 3..7), etc., none of which are currently
simplifying functions.
Non-integer step sizes are problematic anyway, because we
would need to enforce some rule about what to do when
(end - beginning) is not an integer number of steps.
An expression like ((m ... n) * step + x0) is unambiguous.
So if Maxima allows only 1 as the step, that's OK by me.

Making reverse, append, etc into simplifying functions makes
sense to me. It would be some work but I think it's well
within reach.

FWIW
Robert
Stavros Macrakis
2007-04-09 18:16:05 UTC
Permalink
I believe, map(lambda([x], expr), L) is a hard piece of code for newbies.
Agreed.
Is expr, x : L in top level or ev( expr, x : L ) in programs equivalent
to map(lambda([x], expr), L) resp. makelist(expr, x, L) or are there some
differences?
No, ev(expr,x:l) is very roughly equivalent to block([x:l],expr) or
equivalently lambda([x],expr)(l) or map(lambda([x], [expr]). It does
not map over lists.

But actually ev does some other things as well, notably substituting
values of variables before the main evaluation; its behavior is really
rather complicated and messy. For example:

var1: 'var2$
var2: 'var3$

var1 => var2
block([],var1) => var2
lambda([x],var1)(3) => var2

so far so good. Now, with ev:

var1, x=3 => var3
but
block([],var1),x=3 => var2
lambda([x],var1)(3),x=3 => var2

That is why I recommend avoiding ev, *especially* in programs. It is
primarily a convenience feature for top-level use.

-s
Stavros Macrakis
2007-04-09 18:32:19 UTC
Permalink
Post by Stavros Macrakis
No, ev(expr,x:l) is very roughly equivalent to block([x:l],expr) or
equivalently lambda([x],expr)(l) or map(lambda([x], [expr]).
Sorry, this last expression should be

map(lambda([x], expr), [l])[1]

(a pretty obscure way to do this...)
Stavros Macrakis
2007-04-09 16:43:08 UTC
Permalink
makelist (expr, i, i0, i1, j, j0, j1, k, k0, k1, ...)...
makelist also recognizes makelist(expr, x, L) where L is a list.
I guess I would suggest cutting that.
I would prefer to unify the two as follows:

makelist(expr, i, ilist, j, jlist, ...)
== makelist(expr, i, i0..i1, j, j0..j1, ...)

This is unambiguous. Map of course can map over a list, but the
syntax is heavy: map(lambda([x],x^2),list) vs. makelist(x^2,x,list),
and the semantics are different in the multivariate case (see below).
Of course, the latter should be thought of as syntactic sugar for the
former.
map(lambda([x], expr), L) has the same effect (ignoring
evaluation subtleties) as makelist(expr, x, L) so we don't really
need the latter, and map already allows multiple arguments too.
The multiple arguments to map have completely different semantics:

map(f,[1,2],[3,4]) => [ f(1,3), f(2,4) ]
create_list(f(a,b),a,[1,2],b,[3,4]) => [f(1, 3), f(1, 4), f(2,
3), f(2, 4)]

Both are useful.
I've never liked the notation .. for an ellipsis.
Why not type 3 dots and have a proper ellipsis.
Because (a) two dots for an integer range is established notation in
many languages (Pascal, Ada, Perl, Ruby, Maple); and (b) three dots is
generally used to mean something else both in mathematics and in
Maxima.

In mathematics, I don't think I've ever seen (m...n) to denote "the
integers from m to n"; instead, you see (m, m+1, ..., n); and in
general it has the rather vague meaning of "the reader can figure this
out", e.g. (1+a+...+a^n).
Typing .. instead of ... is one of those unhelpful abbrvtions.
I don't think ".." is intended as an abbreviation of "..." but as a
distinct notation.

-s
Robert Dodier
2007-04-09 18:54:46 UTC
Permalink
Post by Stavros Macrakis
makelist(expr, i, ilist, j, jlist, ...)
== makelist(expr, i, i0..i1, j, j0..j1, ...)
OK.
Post by Stavros Macrakis
map(f,[1,2],[3,4]) => [ f(1,3), f(2,4) ]
create_list(f(a,b),a,[1,2],b,[3,4]) => [f(1, 3), f(1, 4), f(2,
3), f(2, 4)]
I guess that makes create_list redundant with cartesian_product
or maybe outermap in the multiple argument case (not map).

But now I'm wondering --- is makelist w/ multiple arguments
supposed to be like map or like cartesian_product or outermap?
Or something else?
Post by Stavros Macrakis
Because (a) two dots for an integer range is established notation in
many languages (Pascal, Ada, Perl, Ruby, Maple); and (b) three dots is
generally used to mean something else both in mathematics and in
Maxima.
OK I guess. Not a big deal.

FWIW
Robert
Kostas Oikonomou
2007-04-09 20:17:50 UTC
Permalink
Maybe I missed something, but why are we tending toward adopting the
"i, i1..i2" range notation as opposed to "[i,i1,i2]", which, as far as I
know, is used in lots of places in Maxima?

Kostas
Post by Robert Dodier
Post by Stavros Macrakis
makelist(expr, i, ilist, j, jlist, ...)
== makelist(expr, i, i0..i1, j, j0..j1, ...)
OK.
Post by Stavros Macrakis
map(f,[1,2],[3,4]) => [ f(1,3), f(2,4) ]
create_list(f(a,b),a,[1,2],b,[3,4]) => [f(1, 3), f(1, 4), f(2,
3), f(2, 4)]
I guess that makes create_list redundant with cartesian_product
or maybe outermap in the multiple argument case (not map).
But now I'm wondering --- is makelist w/ multiple arguments
supposed to be like map or like cartesian_product or outermap?
Or something else?
Post by Stavros Macrakis
Because (a) two dots for an integer range is established notation in
many languages (Pascal, Ada, Perl, Ruby, Maple); and (b) three dots is
generally used to mean something else both in mathematics and in
Maxima.
OK I guess. Not a big deal.
FWIW
Robert
_______________________________________________
Maxima mailing list
http://www.math.utexas.edu/mailman/listinfo/maxima
Stavros Macrakis
2007-04-09 20:58:09 UTC
Permalink
Post by Kostas Oikonomou
Maybe I missed something, but why are we tending toward adopting the
"i, i1..i2" range notation as opposed to "[i,i1,i2]", which, as far as I
know, is used in lots of places in Maxima?
I can't think of any place it is used besides plotting.

I have several problems with the [i,m,n] notation:
1) it is not a natural way to specify a sequence where there is
no relevant variable name
1a) in fact in plot2d, it is used even when the variable is
irrelevant, e.g.
plot2d(sin,[q,0,10])
2) in plotting, it specifies a *real interval*, not an integer sequence.
3) it includes the variable syntactically, where that is in fact
a separate concept from the
sequence, so it gives no natural way to manipulate the
sequence itself.
4) in the case of create_list / makelist, how would you use it
an not have ambiguity?
I suppose you could have makelist(f(i),[i,2,4]) and
makelist(f(i),[i,[2,3,4]]),
but that seems clumsy

A notation that is used elsewhere in Maxima just has multiple
arguments, with no notation at all for the sequence, e.g.

sum(..., i, m, n) also product
makelist(..., i, m, n)
for i from m thru n do...

The problem with these is that they don't give any way of specifying
the sequence itself, and don't
generalize nicely to the list case.

-s
Stavros Macrakis
2007-04-09 22:18:11 UTC
Permalink
Post by Robert Dodier
I guess that makes create_list redundant with cartesian_product
or maybe outermap in the multiple argument case (not map).
Not quite.

create_list( [i,j],i,[i1,i2],j,[j1,j2]) => [ [i1,j1], ...
cartesian_product({i1,i2},{j1,j2}) => { [i1,j1], ...
create_list(f(i,j),i,[i1,i2],j,[j1,j2]) => [ f( i1,j1 ), ...
map(f,cartesian_product({i1,i2},{j1,j2}))=> { f([i1,j1]), ...
(note sets and list argument)
outermap(f,[i1,i2],[j1,j2]) => [[ f( i1,j1 ), ...
(note nesting)
Post by Robert Dodier
But now I'm wondering --- is makelist w/ multiple arguments
supposed to be like map or like cartesian_product or outermap?
Or something else?
They all seem useful. It would, on the other hand, be nice if we had a
systematic approach to all this using fewer primitives which would combine
nicely, or at least had systematic names.

Example:

map_cartesian == map( fapply(f), cartesian_product(...) )

where fapply converts a function of N arguments to a function of a *list of*
N arguments:

fapply(f):=
buildq( [f, args:?gensym()], lambda([[args]],apply(f,args))
(buildq and gensym needed because we don't have lexical scoping :-( )


-s
Robert Dodier
2007-04-10 01:46:59 UTC
Permalink
Post by Stavros Macrakis
create_list( [i,j],i,[i1,i2],j,[j1,j2]) => [ [i1,j1], ...
cartesian_product({i1,i2}, {j1,j2}) => { [i1,j1], ...
create_list(f(i,j),i,[i1 ,i2],j,[j1,j2]) => [ f( i1,j1 ), ...
map(f,cartesian_product({i1,i2},{j1,j2}))=> { f([i1,j1]), ...
(note sets and list argument)
outermap(f,[i1,i2],[j1,j2]) => [[ f( i1,j1 ),
Well, this is something of a mess. I hope we can avoid making it any worse ....
Post by Stavros Macrakis
Post by Robert Dodier
But now I'm wondering --- is makelist w/ multiple arguments
supposed to be like map or like cartesian_product or outermap?
Or something else?
They all seem useful. It would, on the other hand, be nice if we had a
systematic approach to all this using fewer primitives which would combine
nicely, or at least had systematic names.
Sure. I'm still wondering, though, if the extended makelist would be
more like map or more like cartesian_product. I'm in favor of making
it more like map.

FWIW
Robert
van Nek
2007-04-09 18:01:02 UTC
Permalink
Post by Robert Dodier
makelist also recognizes makelist(expr, x, L) where L is a list.
I guess I would suggest cutting that. Allowing that and also the
notation with integers leads to ambiguity as noted before.
map(lambda([x], expr), L) has the same effect (ignoring
evaluation subtleties) as makelist(expr, x, L) so we don't really
need the latter, and map already allows multiple arguments too.
I believe, map(lambda([x], expr), L) is a hard piece of code for newbies.

Is expr, x : L in top level or ev( expr, x : L ) in programs equivalent to
map(lambda([x], expr), L) resp. makelist(expr, x, L) or are there some differences?

Volker
van Nek
2007-04-10 17:48:30 UTC
Permalink
Post by Stavros Macrakis
Is expr, x : L in top level or ev( expr, x : L ) in programs equivalent
to map(lambda([x], expr), L) resp. makelist(expr, x, L) or are there some
differences?
No, ev(expr,x:l) is very roughly equivalent to block([x:l],expr) or
equivalently lambda([x],expr)(l) or map(lambda([x], [expr]). It does
not map over lists.
Thanks for clarification.

For expressions which only use the basic operations +,-,*,/ and ^ (polynomial expressions)
the result is the same, due to the definition of arithmetrics with lists, e.g.
(%i1) 4*x^3-x^2+12, x:[1,2,3];
(%o1) [15, 40, 111]
(%i2) map( lambda([x], 4*x^3-x^2+12), [1,2,3] );
(%o2) [15, 40, 111]

The difference is easily shown with simple expressions like sin(x), e.g.
(%i7) sin(x), x:[1,2,3];
(%o7) sin([1, 2, 3])
(%i8) map( sin, [1,2,3] );
(%o8) [sin(1), sin(2), sin(3)]

Volker
Stavros Macrakis
2007-04-10 18:26:35 UTC
Permalink
Post by van Nek
For expressions which only use the basic operations +,-,*,/ and ^ (polynomial expressions)
the result is the same, due to the definition of arithmetrics with lists,
...
Yes, when listarith is true (the default).

And there is another interesting case...:

x . x, x=[1, 2, 3], listarith:true => 14
vs.
x . x, x=[1, 2, 3], listarith:false => [1, 2, 3]^^2
vs.
map( lambda([x], x . x), [1, 2, 3] ) => [1, 4, 9]

Weirdly,
x^^2, x=[1, 2, 3], listarith:true => [ 14 ]
(surely a bug)

More fun...

x^^2,x=[[1,2],[3,4]],listarith:false => [[1,2],[3,4]]^^2
x^^2,x=[[1,2],[3,4]],listarith:true => matrix([[10,20]])
map(lambda([x],x^^2),[[1,2],[3,4]]),listarith:false => [[1,2]^^2,[3,4]^^2]
map(lambda([x],x^^2),[[1,2],[3,4]]),listarith:true =>
[matrix([5]),matrix([25])]

-s

Loading...