How to dereference and use (local) arrays in maxima (without using listarray)?

Viewed 21

I have two questions that I think go hand in hand.

I am trying to reuse the same local (!) variable name (res) in two different functions. In both cases, res is an undeclared array that stores the return value of its respective function. To my surprise, calling function1 and then function2 will overwrite the results of function1 - now, both functions point to res of function2:

function1(myList):=
block(
  [res],
  for i in myList do
  res[i]: i+2,
  res
  )
;


function2(myList):=
block(
  [res],
  for i in myList do
  res[i]: i+4,
  res
  )
;


aList: [0, 1, 2, 3];

                   [0, 1, 2, 3]


output1: function1(aList);

                   res


output2: function2(aList);

                   res;


listarray(output1);

                   [4, 5, 6, 7]


listarray(output2);

                   [4, 5, 6, 7]

My questions would be:

  1. Why is res not the local variable I expect it to be?
  2. Is there an alternative to using listarray? I know that I could have used it directly in the return statement of each function but the admins of the system I am working on have blacklisted listarray for some reason.
1 Answers

Well, there are a couple of things going on here. One is that local variables in block (likewise for function arguments) are actually global symbols, and their values are managed by dynamic binding (as opposed to lexical binding). The important point in this case is the res is the same global symbol in both functions, so what happens with one has an effect on the other.

(I have made progress on implementing lexical symbols for Maxima, and I think it might be ready to roll out this year. However, just having lexical symbols wouldn't fix the problem; please continue reading for that.)

The other issue is that undeclared arrays are symbol properties, not values. Maxima is a so-called Lisp-2: a symbol is conceptually a little table which contains several items, one of which is called the value, and the others can be a function definition or other arbitrary items which are collectively called properties. In particular what you are seeing is that the array is not the symbol value.

What you want as the return value is a list instead of an array. The difference is that a list is a value, while an undeclared array is a property. Yes, I know that's pretty obscure. But the good news is that it's easy to implement:

(%i2) function1 (aList) := makelist (aList[i] + 2, i, 1, length (aList)) $
(%i3) function2 (aList) := makelist (aList[i] + 4, i, 1, length (aList)) $
(%i4) aList: [0, 1, 2, 3];
(%o4)                     [0, 1, 2, 3]
(%i5) output1: function1 (aList);
(%o5)                     [2, 3, 4, 5]
(%i6) output2: function2 (aList);
(%o6)                     [4, 5, 6, 7]
(%i7) output1;
(%o7)                     [2, 3, 4, 5]
(%i8) output2;
(%o8)                     [4, 5, 6, 7]

There are more concise ways to express "add nnn to a list". Try these:

function1 (aList) := makelist (x + 2, x, aList);

or just

function1 (aList) := aList + 2;

This last one requires listarith to be true (which is the default value).

EDIT: I see on rereading this that I didn't use a local variable. You could say it like this:

function1 (aList) := block ([res], res: makelist (<previous stuff here>), res);

likewise for function2.

Related