• Subject: Re: Microlight
• From: Philipp Janda <siffiejoe@...>
• Date: Fri, 21 Dec 2012 12:13:53 +0100

```Am 20.12.2012 23:12, schrieb Jay Carlson:
```
```On Dec 20, 2012, at 11:02 AM, Philipp Janda wrote:

```
```And collect/icollect ([1], slightly altered) are actually surprisingly interesting:

icollect( {}, 1, pairs( t ) )        --> keys( t )
icollect( {}, 2, pairs( t ) )        --> values( t )
icollect( {}, 2, ipairs( t ) )       --> shallow_arraycopy( t )
icollect( {}, 1, io.lines( fname ) ) --> my @lines = <FILE>;
icollect( t1, 2, ipairs( t2 ) )      --> append( t1, t2 )

collect( {}, 1, 2, pairs( t ) )  --> shallow_tablecopy( t )
collect( {}, 1, 2, ipairs( t ) ) --> shallow_arraycopy( t )
collect( {}, 2, 1, ipairs( t ) ) --> invert( t ), makeset( t )
collect( t1, 1, 2, pairs( t2 ) ) --> extend( t1, t2 )
```
```
I really like this, but I hate numbers. Consider a function swap():

swap(1, 2)
-> return 2, 1
swap(1, 2, 3)
-> return 2, 1, 3
swap(1, nil)
-> return nil, 1

-- I'm ultra-paranoid. Would you ever want this to work?
swap(1)
-> error()

I thought it was a shame you can't write a no-memory iterator that applies swap() to another iterator's stepping. (You can't because the "next step" function is only called with *first* value it previously returned.)
```
```
```
That is not the (only) reason. Since you are wrapping another iterator, you need to store its iterator function somewhere. Otherwise this would work (without extra memory):
```
do
local function dup1( a, ... )
return a, a, ...
end

function dups1( f, s, var )
return function( st, v )
return dup1( f( st, v ) ) -- need f here!
end, s, var
end
end

-- icollect would always take the 2nd value
icollect( {}, pairs( t ) )          --> values( t )
icollect( {}, dups1( pairs( t ) ) ) --> keys( t )

```
Btw., the memory overhead is constant even for swaps, and you are allocating a table anyway ...
```
```
```Perhaps a wrapper calling "swap" per step could be called swaps()--it fits with pairs() and ipairs().

That way those examples could become

icollect({}, pairs( t ) ) -> keys(t)
icollect({}, swaps( pairs( t ) )) -> values(t)

collect({}, swaps( ipairs( t ) )) -> invert/Set(t)
```
```
or you could use:

icollect( {}, identity, pairs( t ) ) --> keys( t )
icollect( {}, swap, pairs( t ) )     --> values( t )
collect( {}, swap, ipairs( t ) )     --> invert( t )

*But*:
- the extra function would be mandatory
```
- some iterators (e.g. string.gmatch) can generate more than two values, so we might need
```    collect( {}, swap_1_4_and_2_3, s:gmatch( "..." ) )
```
- using some arbitrary indices (even when it's 1 and 2) implicitly is worse than passing numbers - I find numbers pretty clear, and there is precedent (select, table.insert, etc.), and you could make the numbers optional for a common case (alternatively you could make the table optional)
```
```
On the pro side: You could actually use a for-loop in collect/icollect instead of the tail recursive function.
```
Philipp

```