Iter()

From TinyMUX
Jump to navigation Jump to search

Description

FUNCTION: iter(list, eval [, input delimiter [, output delimiter]])

The name of the function iter() appropriately comes from the word iterate as iter() iterates over an input list to form an output list. The input and output delimiters control how the input list is broken down into items which are then fed into eval resulting in other items which are then formed into the output list and returned.

That is, the string, eval, is evaluated once for each item in list replacing the special symbol, ##, with the corresponding item from the input list and the symbol #@ with the position within the list being iterated (starting with 1).

The result of iter() is an output list. The effect is very similar to @dolist except that the results are made into a list and returned instead of executed.

All servers support single-character delimiters and the use of %r as an output delimiter. All servers support some notation for indicating that output items should not be delimited from each other. See below for details.

By default, input and output delimiters are both spaces.

Related Topics: @dolist, ilev(), inum(), itext(), list(), parse().

Examples

> say iter(This is a test,strlen(##))
You say "4 2 1 4"
> say iter(lnum(10),mul(mul(##,##),10))
You say "0 10 40 90 160 250 360 490 640 810"
> say iter(lcon(me),[name(##)]..[money(##)])
You say "test..1 t1..1 radio..1 The Wizard's Pointy Hat..1"
> say iter(Was it a cat I saw,[words(##)] #@,s)
You say "1 1 4 2 1 3"

iter() adds a space between items by default. The way around this is to make use of the null output delimiter, thus:

> say iter(This is a test,strlen(##),,@@)
You say "4214"

Server Differences

@@

The default output delimiter across MUSH platforms is a single space, but there are times when it is desireable to have no output delimiter at all. On PennMUSH and RhostMUSH, this is accomplished by using a fourth, empty argument. That is, 'iter(1 2 3,##,,)' evaluates to '123' on PennMUSH and RhostMUSH.

On TinyMUSH and TinyMUX, even with a fourth, empty argument, the default output delimiter continues to be a single space. To achieve no output delimiter at all, the so-called NULL delimiter, @@, notation is used. That is, 'iter(1 2 3,##,,@@)' evalutes to '123' on TinyMUSH and TinyMUX.

## and #@

On PennMUSH, RhostMUSH, and TinyMUX, eval is evaluated after ## and #@ are substituted. This behavior opens a softcode security hole if the input list contains unvalidated data from a different security context. For this reason, itext() is preferred over ##, and inum() is preferred over #@ whenever the input list contains unvalidated data from a different security context. It is still perfectly reasonable to use ## and #@ when the input list contains only data originating from the same security context as the iter() call itself.

A second issue with the above behavior is that iter() cannot be nested in a useful way. The substitution is made once by the outer-most iter(). That is, the result of 'iter(1 2 3,iter(A B C,##))' is '1 1 1 2 2 2 3 3 3' on PennMUSH, RhostMUSH, and TinyMUX.

On TinyMUSH, ## and #@ are treated as tokens by the parser. That is, the result of 'iter(1 2 3,iter(A B C,##))' is 'A B C A B C A B C'. As a token, the value is controlled by the inner-most iter(). In this approach, there is no second evaluation of items in the input list, and therefore, there is no security vunerability from parsing unvalidated data from a different security context.

Notice that in both cases, ## can only refer to the value of items at exactly one level of nesting.

Even using itext(), inum(), and ilev() has problems. PennMUSH, RhostMUSH, and TinyMUX support nested iter() calls and transform 'iter(1 2 3,iter(A B C,itext(0)[itext(1)]))' into 'A1 B1 C1 A2 B2 C2 A3 B3 C3', but TinyMUSH transforms 'iter(1 2 3,iter(A B C,itext(0)[itext(1)]))' into '1A 1B 1C 2A 2B 2C 3A 3B 3C'. That is, TinyMUSH treats the nesting level opposite from PennMUSH, RhostMUSH, and TinyMUX.

The itext(), inum(), and ilev() functions support full access to items at all nesting levels from any nesting level and are safe to use with unvalidated data from a different security context. In PennMUSH, itext() with levels 0-9 can be abbreviated as %i<level>, e.g. %i0 is itext(0).

Here are some edge cases that explore some of the quirky behaviors:

'iter(1 2 ##,iter(A B C,##))' evaluate to '1 1 1 2 2 2 A B C' on PennMUSH, RhostMUSH, and TinyMUX, and 'A B C A B C A B C' on TinyMUSH.

Next consider,

&foo me=##-##-##
think iter(1 2 3,u(foo))

On TinyMUSH, this gives '1-1-1 2-2-2 3-3-3', but on PennMUSH, RhostMUSH, and TinyMUX, this gives '##-##-## ##-##-## ##-##-##'.

That is, TinyMUSH substitutes deeply in a potentially undesireable and unexpected way, but PennMUSH, RhostMUSH, and TinyMUX treat the input list in a potentially undesireable and unexpected way.

For all of these reasons, and also because at some point PennMUSH, RhostMUSH, and TinyMUX could potentially change to the TinyMUSH behavior, the use of itext(), inum(), and ilev() is recommend in cases where a specific behavior is required.

Multi-Character Delimiters

TinyMUSH 3.1 and TinyMUX 2.4 support multi-character input and output delimiters. That is, 'iter(1--2--3,##,--,==)' produces '1==2==3'. PennMUSH and RhostMUSH support single-character input delimiters, but multi-character output delimiters. That is, 'iter(1-2-3,##,-,==)' produces '1==2==3'.


For servers that support multi-characters delimiters, you can use %r as part of an input delimiter.

Compatibility Attempts

On PennMUSH and TinyMUX, parse() is a built-in alias for iter(). On TinyMUSH, parse() behaves like PennMUSH's and TinyMUX's iter() by treating ## and #@ as substitutions instead of tokens.