Version 12 of zip

Updated 2010-12-27 09:37:32 by AMG

[...]

Steve Cassidy: "Trf lets you zip up a bit of data but doesn't provide the machinery to zip directories or files. mkZipLib [L1 ] provides such an interface as does zvfs [L2 ]. More up to date is the zipvfs package which is part of tclvfs but that doesn't seem to be working cleanly yet and requires tcl8.4 features."

http://www.equi4.com/critlib/zipper.README http://www.equi4.com/critlib/zlib.README

See also Using a zip file as a Tcl Module.

See also Using zipper to create zip files.

Also, explain pkware vs. winzip vs. zLibDll.


NEM zip is also the name of another functional programming classic. You can think of it as a function that takes a bunch of "columns" (think relational) and returns a list of "rows":

 proc zip {cola colb} {
   set ret [list]
   foreach a $cola b $colb { lappend ret [list $a $b] }
   return $ret
 }
 zip {1 2 3} {a b c} ;# returns {{1 a} {2 b} {3 c}}

You can generalise zip to zipWith which applies an arbitrary function on each pair of values from the columns:

 proc apply {func args} { uplevel #0 $func $args }
 proc zipWith {f cola colb} {
   set ret [list]
   foreach a $cola b $colb { lappend ret [apply $f $a $b] }
   return $ret
 }
 interp alias {} zip {} zipWith list

You could further generalise the function to take an arbitrary number of columns. I'll leave that, and the reverse unzip operation as exercises. See also fold, filter, iterators and map.

Lars H: Isn't that more commonly known as "transposing"? See Transposing a matrix.

NEM: Depends who you ask. zipWith is more general, though.

AMG: "list" isn't a valid lambda, so it's not directly usable with the Tcl 8.5 [apply] command. Here's an 8.5-compatible version. It uses single-argument [lindex] instead of [list] to avoid adding an extra level of list nesting. (Single-argument [lindex] simply returns its argument, even if its argument isn't a valid list.)

interp alias "" zip "" zipWith {{args} {lindex $args}}

AMG: Implementation using lcomp:

proc zip {cola colb} {
    lcomp {[list $a $b]} for a in $cola and b in $colb
}

Here's a version that handles an arbitrary number of columns:

proc zip {args} {
    if {[llength $args]} {
        for {set i 0} {$i < [llength $args]} {incr i} {
            append expression " \$$i"
            lappend operations and $i in [lindex $args $i]
        }
        lset operations 0 for
        lcomp \[list$expression\] {*}$operations
    }
}

Also, I'll take your unzip challenge. ;^)

interp alias "" unzip "" zip

As Lars H pointed out, zip is transpose, so using it twice gives back the original input.

Examples:

% zip {a 1} {b 2} {c 3}
{a b c} {1 2 3}
% unzip {a b c} {1 2 3}
{a 1} {b 2} {c 3}