As mentioned in the overview post regarding Gekko 3.0, lists were generalized in Gekko 3.0, using primarily Python as the inspiration. In Gekko 2.x, lists could only contain strings, so in that sense, they were quite simple. Gekko 3.0 allows any variable to be an element of a list, including other lists. Therefore, lists can be nested, and can contain strings, dates, values, series, etc. So in Gekko 3.0, lists may contain series objects, but it should be noted that since series typically live in databanks, it is often more convenient to store names of these series (in the form of strings), rather that the series themselves.
The general form of a Gekko 3.0 list is “(element1, element2, … , element_n)”, so the list elements are put inside parentheses, and separated with commas. For instance, (‘a’, ‘b’, ‘c’) is a list of three strings, (1, 2, 3) is a list of three values, and (‘a’, (1.5, 2010q3)) is a nested list with mixed types (string, value and date). This is consistent, but the following uses become cumbersome:
#m = ('a', 'b', 'c'); x = (1, 2, 3);
In the first line, we create a list of three strings and put this into a list called #m, and in the second line, we create a list of three values, and put these values into the series x. This syntax may not seem like a big deal, but definitions of lists of strings in Gekko can be quite long, and assigning values to a series is also such an frequent occurrence that the mandatory parentheses become a nuisance.
Therefore, Gekko 3.0 operates with the concept of naked lists. Naked means that they are without parentheses, and that any strings inside them are without quotes. Using such naked lists, we may write:
#m = a, b, c; x = 1, 2, 3;
For long Gekko command files with many lines like that, the possibility of using naked lists is a relief. Naked lists may seem simple in cases like above, but beneath the surface, there are some details to take note of. First of all, a naked list can only be put after the equals sign in an assignment, or in a FOR loop (for instance: “FOR string %i = a, b, c; … END;”). Apart from that, naked list elements can only be of the following kind:
- normal names like a1 or 1a, even -a1 is legal (becomes ‘-a1’)
- series names with bank, frequency and index (for array-series), like b:a!q[i, j]
- Integers like 2 or 007, minus may be used
- Floating point values like 1.2 or 1.2e5, minus may be used. Missing value can be stated with m().
- {}-curlies may be used, for instance {%s}, a{%s}b, {#m}, a{#m}b, and any expression is legal inside the {}-curlies. In general, {}-curlies can be thought of as producing sequences of characters, but there are some special rules regarding these, see this blog post.
In addition to this, there are some special rules. A list like #m = 12, 02 will become a list of the two strings ‘12’ and ‘02’, and not a list of the two values 12 and 2. And a list like #m = 12, 1e5 will also become a list of two strings ‘12’ and ‘1e5’, and not the two values 12 and 100000. This is because both these lists could be codes (name-parts used for name-composition), for instance these name-parts could appear in variable names like x02y or x1e5y. If 02 and 1e5 were converted into 2 and 100000, respectively, such codes would become scrambled if used as strings afterwards (we would get: x2y and x100000y, which would be unexpected). This cannot be the case with a simple integer like 12, and a value like 1.2 or 1.2e5 could not be used as a code anyway, since such codes/name parts normally do not contain dots (R allows dots in names, and uses $ where all other languages use dots, but such strangeness of R is not going to be emulated in Gekko). So the special rules are to ensure that information is not lost or scrambled when using the list elements to compose names. List files use the very same rules, by the way, to keep the syntax and logic of naked lists and list files similar.
Like normal lists, naked lists allow the use of rep (repetition), for instance “#m = 1, 2 rep 10, 3;”, and like normal lists, a singleton list can be stated with the use of a trailing comma — for instance “#m = a,;” becomes a list with one element, namely the string ‘a’. If the comma is omitted, Gekko will fail with a type error (it will look for the series a, and even if such a series is found, it cannot be assigned to a list).
As stated in the post on the release of Gekko 3.0, life in Gekko 2.x was easier in some respects, for instance that an expression like “LIST m = a, #m2, b;” in Gekko 2.x is unequivocal. Since Gekko 2.x only allows strings inside lists (and not, for instance, series or lists), the statement has to mean creating a list consisting of first the string ‘a’, then the elements of the list #m2 (these elements can only be strings in Gekko 2.0), and finally the string ‘b’. In Gekko 3.0, this gets more complicated, since a 3.0 assignment like “#m = (a, #m2, b);” actually means finding the series a (as an object) and putting it into the first position in the list, then finding the list #m2 and putting it (the list itself, not its elements) into the second position in the list, and finally putting the series b into the last position of the list. So the list becomes nested: the second element of the list is another list. Nested lists have many uses, and it would be a cardinal sin to disallow them in Gekko. So now the question arises: In Gekko 3.0, how to create a list where strings and list elements are combined? There are basically three ways:
#m = a, {#m2}, b; #m = list(); #m = #m.append('a').extend(#m2).append('b'); #m = ('a',) + #m2 + ('b',);
- In the first variant where a naked list is used, the {}-curly makes sure that the elements (strings) of #m2 are added one by one, just like the statement “PRT a, {#m2}, b;” would do.
- In the second variant, an empty list is created first, and then elements are added with the append() and extend() functions. Note that extend() is used for #m2. If append() was used for #m2, the list itself would be added, not its elements. The append() and extend() functions follow Python logic closely.
- In the last variant, the ‘+’ operator is used, since this operator is understood as extend() regarding two lists. So between lists, you can just use ‘+’ to join their *elements* together, and for instance (‘a’,) creates a one-element list with the string ‘a’ (note the trailing comma).
Perhaps the first variant is easiest to use for such cases of joining strings and list elements together. But it should also be noted that the += operator can be practical, since it allows a naked list on the right-hand side:
#m = list(); #m += a,; #m += #m2; #m += b,;
This is yet another way of combining lists and strings in Gekko 3.0, where the += operator adds elements at the end of the list. Note the trailing commas in the two one-element naked lists. Trailing commas are borrowed from Python where they are prevalent regarding tuple definitions.
Besides trailing commas, there is another thing to remember about naked lists: they do not allow variables with type symbols ‘%’ or ‘#’. So do not expect a naked list like “#m = a, %s, #m, b;” to work: it will fail with a syntax error. Since a naked list like “#m = a, b, c;” is understood as “#m = (‘a’, ‘b’, ‘c’);”, a naked list like “#m = a, %s, #m, b;” should logically be understood as “#m = (‘a’, ‘%s’, ‘#m’, ‘b’);”, since the type symbols (sigils) are always just understood as a kind of special characters alongside normal characters like ‘a’, ‘b’, etc. (cf. this post). But such use would probably just create a lot of confusion, so therefore type symbols are not allowed in naked lists, except if they are located inside {}-curlies. As mentioned before, “#m = a, {#m2}, b;” is perfectly legal, and actually any expression can be put inside {}-curlies. But outside these curlies, type symbols cannot be used in naked lists. In any case, in practical use, long lists of names are typically lists of series names, not scalar or collection names, so the restriction if probably not inconvenient in any practical way. And if the user really wants a list like #m = (‘a’, ‘%s’, ‘#m’, ‘b’), it can just be stated that way (with parentheses and quotes). Such a list could have its uses, for instance “PRT {#m};” would print the four variables inside the quotes.
Lists and naked lists will probably take some getting used to for Gekko 2.x users. The syntactical complications stem from the fact that lists in Gekko 3.0 allow all kinds of elements, including other lists, and Gekko 3.0 tries to comply rather tightly with the list logic of Python. Of course, one-dimensional non-nested list are in a sense easier to use and understand, but multidimensional nested lists are immensely practical for a lot of purposes. For instance, a list like ((1, 2), (3, 4)) can be thought of as representing a 2 x 2 matrix, and a list like ((‘a’, ‘b’), (‘c’, ‘d’)) can be thought of as representing a 2 x 2 table, and arrays of higher dimensions than 2 can be constructed in a similar way, and in this way different kinds of variables/data can be bundled together in very flexible ways. Elements can be fetched with indexes, for instance if #m = ((1, 2), (3, 4)), the index #m[2][1] would fetch the value 3.
If the user would rather like to fetch elements by names, like for instance #m[‘second row’][‘first col’], a map variable could be used instead. But this would be the subject of a another blog post…
Recent Comments