lolcat

lolcat is an additional list function which aspect uses almost every day. Once you come to love it, you will too!

It's a portmanteau of lmap, {*} and concat:

proc lolcat args {
    concat {*}[uplevel 1 lmap $args]
}

It's useful in cases where you want to lmap, but the body might need to yield multiple results for a single iteration. Here's a simple example:

% lolcat {x y} {1 2 3 4} {list $y $x}
2 1 4 3
% lolcat x {1 2 3 4} {if {$x%2} {list $x $x} else {list $x}}
1 1 2 3 3 4

To understand how it works, remember that concat concatenates lists, and concat {*}$ls receives the elements of $ls as arguments. Thus, concat {*}$ls will flatten (one level of) a list:

% concat {*}{{1 2} {3 4 5} {6 {7 8}} 9}
1 2 3 4 5 6 {7 8} 9
# equivalently:
% concat {1 2} {3 4 5} {6 {7 8}} 9
1 2 3 4 5 6 {7 8} 9

PYK 2016-01-09 cautions: lolcat requires that the result of the lmap body be a list, not a simple value. Otherwise, data might get munged.

-- Please provide an example.

PYK 2016-04-19: join is a little more concise and efficient, and the 1 in uplevel isn't strictly necessary since lmap can't be interpreted as a level:

proc lolcat args {
    join [uplevel lmap $args]
}

-- Timing trials indicate that the join version is some 8% slower. YMMV.

One neat extension on lolcat is dictify:

proc dictify {cmdPrefix ls} {
    lolcat x $ls {
        list $x [uplevel 1 $cmdPrefix [list $x]]
    }
}

This allows you to make a dictionary whose keys are a list, and values are the result of evaluating a command on each element.

% dictify {expr 2**} {1 2 3 4 5}
1 2 2 4 3 8 4 16 5 32

Or more elaborately:

% proc pdict {d} {array set {} $d; parray {}}
% pdict [dictify {tcl::pkgconfig get} [tcl::pkgconfig list]]
debug              = 0
threaded           = 1
profiled           = 0
64bit              = 0
optimized          = 1
mem_debug          = 0
compile_debug      = 0
compile_stats      = 0
libdir,runtime     = /home/tcl/lib
bindir,runtime     = /home/tcl/bin
scriptdir,runtime  = /home/tcl/lib/tcl8.6
includedir,runtime = /home/tcl/include
docdir,runtime     = /home/tcl/man
libdir,install     = /home/tcl/lib
bindir,install     = /home/tcl/bin
scriptdir,install  = /home/tcl/lib/tcl8.6
includedir,install = /home/tcl/include
docdir,install     = /home/tcl/man

The name comes courtesy hypnotoad - previously I called this procedure lconcat, which is obviously a terrible name, and I only stuck with for lack of a better alternative. It took the toad's genius to find this proc's correct name, which just goes to show: give a dog a bad name, and it will stick ... but a cat can change its stripes!

Similar Works

AMG: [lolcat] resembles [lcomp]:

% lolcat {a b} {1 2 3 4} {list $b $a}
% lolcat a {1 3} b {2 4} {list $b $a}
% lcomp {$b} {$a} for {a b} in {1 2 3 4}
% lcomp {$b} {$a} for a in {1 3} and b in {2 4}

All return 2 1 4 3. The differences are:

[lolcat] [lcomp]
Result generator is last argumentResult generator is first argument(s)
Result generator is one argument Result generator is one or more argument(s)
Result generator is Tcl script Result generator is expr expressions
No noise words Supports numerous tokens such as for

While I'm on the subject, Brush offers (will offer) capability very similar to [lcomp], though the language-level syntax is different:

% collect b a for (&a &b) in (1 2 3 4)
% collect b a for &a in (1 3) and &b in (2 4)

See Also

fptools
contains a proc lmultimap which offers a different way to have multiple results from mapping over a single list
map in functional programming
another small but powerful variation on lmap