lolcat

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

See Also

fptools
Provides 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.

Description

lolcat is a portmanteau of lmap, {*} and concat:

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

It's useful in cases whene using 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

concat joins its arguments, adding whitespace in between, and concat {*}$ls receives the elements of $ls as its arguments. Thus, concat {*}$ls removes one layer of structure from the original 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.

set res [lolcat {x y} {1 2 3 4 5 6} {
        if {$x == 3} {
                # skip 4
                return -level 0 {answer 3}
        } else {
                list "answer $y" "answer $x"
        }
}]

Output:

{answer 2} {answer 1} answer 3 {answer 6} {answer 5}

The desired output would normally be

{answer 2} {answer 1} {answer 3} {answer 6} {answer 5}

and the way to achieve that would be return -level 0 list {answer 3}

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]
}

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)