diff --git a/build.tcl b/build.tcl index 407d6ea..4f9cdfc 100644 --- a/build.tcl +++ b/build.tcl @@ -1,7 +1,7 @@ package require tin 1.0 tin import assert from tin tin import tcltest -set version 0.1.1 +set version 0.2 set vutil_version 1.1 set config [dict create VERSION $version VUTIL_VERSION $vutil_version] tin bake src build $config @@ -22,7 +22,7 @@ test nrepeat { # Assert that nrepeat works } -body { ndlist 2D {1 {2 3}} -} -result {{1 0} {2 3}} +} -result {{1 {}} {2 3}} # nrepeat ################################################################################ @@ -33,17 +33,17 @@ test nrepeat { nrepeat {1 2 3} 0 } -result {{{0 0 0} {0 0 0}}} -# nrange +# range ################################################################################ -puts "Creating nrange" -test nrange { +puts "Creating range" +test range { # Generate range of integers } -body { -assert [nrange 3] eq [nrange 0 2] -assert [nrange 10 3 -2] eq {10 8 6 4} -assert [nrange 4] eq {0 1 2 3} -assert [nrange 0 4] eq {0 1 2 3 4} -assert [nrange 0 4 2] eq {0 2 4} +assert [range 3] eq [range 0 2] +assert [range 10 3 -2] eq {10 8 6 4} +assert [range 4] eq {0 1 2 3} +assert [range 0 4] eq {0 1 2 3 4} +assert [range 0 4 2] eq {0 2 4} } -result {} # nshape/nsize @@ -321,7 +321,7 @@ test nset_I_2D { for {set i 0} {$i < 3} {incr i} { nset I $i $i 1 } - set I + nfill 2D $I 0 } -result {{1 0 0} {0 1 0} {0 0 1}} test nset_I_3D { @@ -331,7 +331,7 @@ test nset_I_3D { for {set i 0} {$i < 3} {incr i} { nset I $i $i $i 1 } - set I + nfill 3D $I 0 } -result {{{1 0 0} {0 0 0} {0 0 0}} {{0 0 0} {0 1 0} {0 0 0}} {{0 0 0} {0 0 0} {0 0 1}}} test nset2 { @@ -341,17 +341,13 @@ test nset2 { nset a {1 0} : [nget $a {0 1} :] } -result {{3 4} {1 2} {5 6}} -test nset_filler { - # Test out custom filler +test nset_fill_foobar { + # Fill blanks with a value. } -body { - assert $::ndlist::filler eq 0 set a "" - set ::ndlist::filler bar; # custom filler nset a 1 1 1 foo + nfill 3D $a bar } -result {{{bar bar} {bar bar}} {{bar bar} {bar foo}}} -set ::ndlist::filler 0; # reset to default - - # nop test nop1 { diff --git a/doc/ndlist.pdf b/doc/ndlist.pdf index de02b02..8976aaa 100644 Binary files a/doc/ndlist.pdf and b/doc/ndlist.pdf differ diff --git a/doc/ndlist.tex b/doc/ndlist.tex index b444bee..63b679a 100644 --- a/doc/ndlist.tex +++ b/doc/ndlist.tex @@ -68,7 +68,7 @@ \subsection{Initialization} \end{example} The command \cmdlink{ndlist} initializes an ND list from a value input, expanding if necessary to match maximum dimensions. -If the input value is ``ragged'', as in it has inconsistent dimensions, it will be expanded to match the maximum dimensions, filled in with the value \texttt{\$::ndlist::filler}. By default, this is 0, but can be modified. +If the input value is ``ragged'', as in it has inconsistent dimensions, it will be expanded to match the maximum dimensions, filled in with blanks. \begin{syntax} \command{ndlist} \$nd \$value \end{syntax} @@ -82,43 +82,20 @@ \subsection{Initialization} \end{lstlisting} \tcblower \begin{lstlisting} -{{1 0} {2 3}} +{{1 {}} {2 3}} \end{lstlisting} \end{example} -\clearpage -\section{ND List Indexing}\label{indexformat} -Index input for all ND list access and modification functions (\cmdlink{nget}, \cmdlink{nreplace}, \cmdlink{nset} and the ND list object method ``\methodlink[0]{ndlist}{@}'') gets passed through the ND list index parser \cmdlink{::ndlist::ParseIndex}. -\begin{syntax} -\command{::ndlist::ParseIndex} \$input \$n -\end{syntax} -\begin{args} -\$input & Index input. Options are shown below: \\ -\quad : & All indices \\ -\quad \$start:\$stop & Range of indices (e.g. 0:4 or 1:end-2).\\ -\quad \$start:\$step:\$stop & Stepped range of indices (e.g. 0:2:-2 or 2:3:end), using \cmdlink{nrange}. \\ -\quad \$iList & List of indices (e.g. \{0 end-1 5\} or 3). \\ -\quad \$i* & Single index with asterisk, ``flattens'' the ndlist (e.g. 0* or end-3*). \\ -\$n & Number of elements in list. -\end{args} -Additionally, index range arguments \texttt{\$start} and \texttt{\$stop}, all indices in \texttt{\$iList}, and single indices \texttt{\$i} get passed through the \cmdlink{::ndlist::Index2Integer} command, which converts \texttt{end}$\pm$\textit{integer}, \textit{integer}$\pm$\textit{integer} and negative wrap-around indexing (where -1 is equivalent to ``end'') into normal integer indices. -\begin{syntax} -\command{::ndlist::Index2Integer} \$index \$n -\end{syntax} -\begin{args} -\$index & Single index. \\ -\$n & Number of elements in list. -\end{args} \clearpage -\subsection{ND List Access} +\subsection{Access} Portions of an ND list can be accessed with the command \cmdlink{nget}, using index notation as described on pg. \pageref{indexformat}. \begin{syntax} \command{nget} \$ndlist \$i ... \end{syntax} \begin{args} \$ndlist & ND list value. \\ -\$i ... & Index arguments, using index notation (see pg. \pageref{indexformat}). The number of index arguments determines the interpreted dimensions. For ND list objects, must match dimensionality of object. +\$i ... & Index arguments, using index notation (see pg. \pageref{indexformat}). The number of index arguments determines the interpreted dimensions. \end{args} @@ -142,22 +119,22 @@ \subsection{ND List Access} \end{example} \clearpage -\subsection{ND List Modification} +\subsection{Modification} A ND list can be modified by reference with \cmdlink{nset}, and by value with \cmdlink{nreplace}, using the same index argument syntax as \cmdlink{nget}. If the blank string is used as a replacement value, it will remove values from the ND lists, as long as it is only removing along one dimension. Otherwise, the replacement ND list must agree in dimension to the to the index argument dimensions, or be unity. For example, you can replace a 4x3 portion of a matrix with 4x3, 4x1, 1x3, or 1x1 matrices. If modifying outside of the dimensions of the ND list, the ND list will be expanded to the new dimensions, like in the command \cmdlink{ndlist}. \begin{syntax} -\command{nset} \$varName \$arg1 \$arg2 ... \$sublist +\command{nset} \$varName \$i ... \$sublist \end{syntax} \begin{syntax} -\command{nreplace} \$ndlist \$arg1 \$arg2 ... \$sublist +\command{nreplace} \$ndlist \$i ... \$sublist \end{syntax} \begin{args} \$varName & Name of ND list to modify. \\ \$ndlist & ND list to modify. \\ -\$arg1 \$arg2 ... & Index arguments, using index notation (see pg. \pageref{indexformat}). The number of index arguments determines the interpreted dimensions. \\ +\$i ... & Index arguments, using index notation (see pg. \pageref{indexformat}). The number of index arguments determines the interpreted dimensions. \\ \$sublist & Replacement list, or blank to delete values. \end{args} \begin{example}{Swapping rows in a matrix} @@ -175,47 +152,58 @@ \subsection{ND List Modification} \end{lstlisting} \end{example} + \clearpage -\subsection{Range Generator} -The index range notation ``\texttt{\$start:\$step:\$stop}'' uses the command \cmdlink{nrange}, which simply generates a range of integer values. +\section{N-Dimensional Mapping} +The command \cmdlink{nmap} is a general purpose mapping function for N-dimensional lists in Tcl. +If multiple ND lists are provided for iteration, they must agree in dimension or be unity, like in \cmdlink{nset}. +Returns an ND list in similar fashion to the Tcl \textit{lmap} command. +Additionally, elements can be skipped with \textit{continue}, and the entire loop can be exited with \textit{break}. \begin{syntax} -\command{nrange} \$n \\ -nrange \$start \$stop <\$step> +\command{nmap} \$nd \$varName \$ndlist <\$varName \$ndlist ...> \$body \end{syntax} \begin{args} -\$n & Number of indices, starting at 0 (e.g. 3 returns 0 1 2). \\ -\$start & Starting value. \\ -\$stop & Stop value. \\ -\$step & Step size. Default 1 or -1, depending on direction of start to stop. +\$nd & Dimensionality of ND list (e.g. 2D for a matrix). \\ +\$varName & Variable name to iterate with. \\ +\$ndlist & ND list to iterate over. \\ +\$body & Tcl script to evaluate at every loop iteration. \end{args} -\begin{example}{Integer range generator} +\subsection{Map Indices} +The iteration indices of \cmdlink{nmap} are accessed with the commands \cmdlink{i}, \cmdlink{j}, \& \cmdlink{k}. +The commands \cmdlink{j} and \cmdlink{k} are simply shorthand for \cmdlink{i} with dimensions 1 and 2. +\begin{syntax} +\command{i} <\$axis> +\end{syntax} +\begin{syntax} +\command{j} +\end{syntax} +\begin{syntax} +\command{k} +\end{syntax} +\begin{args} +\$axis & Dimension to access mapping index at. Default 0. +\end{args} +\begin{example}{ND list mapping} \begin{lstlisting} -puts [nrange 3] -puts [nrange 0 2] -puts [nrange 10 3 -2] -# Alternative for-loop -foreach i [nrange 5] { - puts $i -} +set testmat {{1 2 3} {4 5 6} {7 8 9}} +# Checkerboard sign pattern +puts [nmap 2D x $testmat {expr { + $x*([i]%2 + [j]%2 == 1?-1:1) +}}] +# Simple formatting +puts [nmap 2D x $testmat {format %.2f $x}] \end{lstlisting} \tcblower \begin{lstlisting} -0 1 2 -0 1 2 -10 8 6 4 -0 -1 -2 -3 -4 +{1 -2 3} {-4 5 -6} {7 -8 9} +{1.00 2.00 3.00} {4.00 5.00 6.00} {7.00 8.00 9.00} \end{lstlisting} \end{example} \clearpage - -\section{Basic Math Operations} +\subsection{Math Mapping} Basic math operators can be mapped over an ND list with the command \cmdlink{nop}. -This command is built-in to ND list objects with the ``\texttt{.=}'' operator. +This method is much faster than \cmdlink{nmap} for simple math operations. \begin{syntax} \command{nop} \$nd \$ndlist \$op \$arg... @@ -227,6 +215,7 @@ \section{Basic Math Operations} \$arg... & Operator arguments (see tcl::mathop documentation). \end{args} +Additionally, this command is built-in to ND list objects with the ``\texttt{.=}'' operator, as shown below. \begin{example}{Element-wise operations} \begin{lstlisting} # Using ND list values @@ -243,53 +232,6 @@ \section{Basic Math Operations} {0 0 1} {1 1 1} \end{lstlisting} \end{example} -\clearpage - -\section{General Purpose Mapping} -The command \cmdlink{nmap} is a general purpose mapping function for N-dimensional lists in Tcl. -If multiple ND lists are provided for iteration, they must agree in dimension or be unity, like in \cmdlink{nset}. -Returns an ND list in similar fashion to the Tcl \textit{lmap} command. -Additionally, elements can be skipped with \textit{continue}, and the entire loop can be exited with \textit{break}. -\begin{syntax} -\command{nmap} \$nd \$varName \$ndlist <\$varName \$ndlist ...> \$body -\end{syntax} -\begin{args} -\$nd & Dimensionality of ND list (e.g. 2D for a matrix). \\ -\$varName & Variable name to iterate with. \\ -\$ndlist & ND list to iterate over. \\ -\$body & Tcl script to evaluate at every loop iteration. -\end{args} -\begin{example}{ND list mapping} -\begin{lstlisting} -set testmat {{1 2 3} {4 5 6} {7 8 9}} -# Checkerboard sign pattern -puts [nmap 2D x $testmat {expr { - $x*([i]%2 + [j]%2 == 1?-1:1) -}}] -puts [nmap 2D x $testmat {format %.2f $x}]; # Simple formatting -\end{lstlisting} -\tcblower -\begin{lstlisting} -{1 -2 3} {-4 5 -6} {7 -8 9} -{1.00 2.00 3.00} {4.00 5.00 6.00} {7.00 8.00 9.00} -\end{lstlisting} -\end{example} - -\subsection{Map Index Access} -The iteration indices of \cmdlink{nmap} are accessed with the commands \cmdlink{i}, \cmdlink{j}, \& \cmdlink{k}. -The commands \cmdlink{j} and \cmdlink{k} are simply shorthand for \cmdlink{i} with dimensions 1 and 2. -\begin{syntax} -\command{i} <\$axis> -\end{syntax} -\begin{syntax} -\command{j} -\end{syntax} -\begin{syntax} -\command{k} -\end{syntax} -\begin{args} -\$axis & Dimension to access mapping index at. Default 0. -\end{args} \clearpage \section{ND List Objects} @@ -353,7 +295,7 @@ \subsection{Standard Methods} \begin{example}{ND list object methods} \begin{lstlisting} matrix X {{1 2} {3 4}} -$X ::= {format %0.2f $@.}; Format values +$X ::= {format %0.2f $@.}; # Format values $X print $X --> Y; # Copy object $Y .= {+ 1}; # Perform math operation @@ -362,7 +304,7 @@ \subsection{Standard Methods} \tcblower \begin{lstlisting} {1.00 2.00} {3.00 4.00} -{2.00 3.00} {4.00 5.00} +{2.0 3.0} {4.0 5.0} \end{lstlisting} \end{example} @@ -385,7 +327,7 @@ \subsection{Index Method} \quad ::= \$body & \quad Modify range in place using \cmdlink{neval}. \end{args} -\begin{example}{ND List Object Access/Manipulation} +\begin{example}{ND list object access/manipulation} \begin{lstlisting} # Access ND List Objects matrix X {{1 2} {3 4}} @@ -418,14 +360,14 @@ \section{Object Reference Mapping} \$expr & Tcl expression with list object references. \\ \$nd & Dimensionality of ND list (e.g. 2D for a matrix). \\ \$ndlist & ND list to iterate over, with \texttt{\$@.} reference. \\ -\$refName & Optional reference variable to tie resulting ND list to. Blank to return value. +\$refName & Reference variable to tie resulting ND list to. Default blank returns value. \end{args} \begin{example}{Element-wise expressions} \begin{lstlisting} matrix x {{1 2} {3 4} {5 6}} matrix y 5.0 -nexpr {$@x + $@y} +puts [nexpr {$@x + $@y}] \end{lstlisting} \tcblower \begin{lstlisting} @@ -598,6 +540,96 @@ \section{Combine ND Lists} \end{lstlisting} \end{example} +\clearpage + +\section{Filling Blanks} +Modifying an ND list outside of its dimensions automatically expands the list with blanks to match the new shape. +The command \cmdlink{nfill} replaces blank values with a specified filler value. +For ND list objects, the method \methodlink[0]{ndlist}{fill} fills blanks in the value stored in the object. +\begin{syntax} +\command{nfill} \$nd \$ndlist \$filler \\ +\method{ndlist}{fill} \$filler +\end{syntax} +\begin{args} +\$nd & Dimensionality of ND list (e.g. 2D for a matrix). \\ +\$ndlist & ND list to iterate over. \\ +\$filler & Filler to replace blanks with. +\end{args} + +\begin{example}{Creating an identity matrix} +\begin{lstlisting} +set I "" +for {set i 0} {$i < 3} {incr i} { + nset I $i $i 1 +} +set I [nfill 2D $I 0] +puts $I +\end{lstlisting} +\tcblower +\begin{lstlisting} +{1 0 0} {0 1 0} {0 0 1} +\end{lstlisting} +\end{example} +\clearpage +\section{Index Notation}\label{indexformat} +Index input for all ND list access and modification functions (\cmdlink{nget}, \cmdlink{nreplace}, \cmdlink{nset} and the ND list object method ``\methodlink[0]{ndlist}{@}'') gets passed through the ND list index parser \cmdlink{::ndlist::ParseIndex}. +\begin{syntax} +\command{::ndlist::ParseIndex} \$input \$n +\end{syntax} +\begin{args} +\$input & Index input. Options are shown below: \\ +\quad : & All indices \\ +\quad \$start:\$stop & Range of indices (e.g. 0:4 or 1:end-2).\\ +\quad \$start:\$step:\$stop & Stepped range of indices (e.g. 0:2:-2 or 2:3:end), using \cmdlink{range}. \\ +\quad \$iList & List of indices (e.g. \{0 end-1 5\} or 3). \\ +\quad \$i* & Single index with asterisk, ``flattens'' the ndlist (e.g. 0* or end-3*). \\ +\$n & Number of elements in list. +\end{args} +Additionally, index range arguments \texttt{\$start} and \texttt{\$stop}, all indices in \texttt{\$iList}, and single indices \texttt{\$i} get passed through the \cmdlink{::ndlist::Index2Integer} command, which converts \texttt{end}$\pm$\textit{integer}, \textit{integer}$\pm$\textit{integer} and negative wrap-around indexing (where -1 is equivalent to ``end'') into normal integer indices. +\begin{syntax} +\command{::ndlist::Index2Integer} \$index \$n +\end{syntax} +\begin{args} +\$index & Single index. \\ +\$n & Number of elements in list. +\end{args} + + +\clearpage +\subsection{Range Generator} +The index range notation ``\texttt{\$start:\$step:\$stop}'' uses the command \cmdlink{range}, which simply generates a list of integer values. +\begin{syntax} +\command{range} \$n \\ +range \$start \$stop <\$step> +\end{syntax} +\begin{args} +\$n & Number of indices, starting at 0 (e.g. 3 returns 0 1 2). \\ +\$start & Starting value. \\ +\$stop & Stop value. \\ +\$step & Step size. Default 1 or -1, depending on direction of start to stop. +\end{args} +\begin{example}{Integer range generator} +\begin{lstlisting} +puts [range 3] +puts [range 0 2] +puts [range 10 3 -2] +# Alternative for-loop +foreach i [range 5] { + puts $i +} +\end{lstlisting} +\tcblower +\begin{lstlisting} +0 1 2 +0 1 2 +10 8 6 4 +0 +1 +2 +3 +4 +\end{lstlisting} +\end{example} \clearpage {\normalsize\printindex} \end{document} diff --git a/doc/template/version.tex b/doc/template/version.tex index f64f7bc..7e990f0 100644 --- a/doc/template/version.tex +++ b/doc/template/version.tex @@ -1 +1 @@ -\newcommand{\version}{0.1.1} +\newcommand{\version}{0.2} diff --git a/examples/doc_examples.tcl b/examples/doc_examples.tcl new file mode 100644 index 0000000..206d83c --- /dev/null +++ b/examples/doc_examples.tcl @@ -0,0 +1,119 @@ +package require tin +tin import ndlist + +puts "Create nested ND list with one value" +puts [nrepeat {1 2 3} 0] + +puts "Expand ragged ND list" +puts [ndlist 2D {1 {2 3}}] + +puts "ND list access" +set A {{1 2 3} {4 5 6} {7 8 9}} +puts [nget $A 0 :] +puts [nget $A 0* :]; # can "flatten" row +puts [nget $A 0:1 1] +puts [nget $A end:0 end:0]; # can have reverse ranges +puts [nget $A {0 0 0} 1*]; # can repeat indices + +puts "Swapping rows in a matrix" +# ND List Value Modification +set a {{1 2} {3 4} {5 6}} +puts [nreplace $a : 1 ""]; # Delete a column (modify in-place) +nset a {1 0} : [nget $a {0 1} :]; # Swap rows and columns (modify by reference) +puts $a + +puts "Element-wise operations" +# Using ND list values +puts [nop 1D {1 2 3} -] +puts [nop 1D {1 2 3} + 1] +# Using ND list objects +matrix x {{1 2 3} {4 5 6}} +[$x .= {>= 3}] print + +puts "ND list mapping" +set testmat {{1 2 3} {4 5 6} {7 8 9}} +# Checkerboard sign pattern +puts [nmap 2D x $testmat {expr { + $x*([i]%2 + [j]%2 == 1?-1:1) +}}] +# Simple formatting +puts [nmap 2D x $testmat {format %.2f $x}] + +puts "Create ND list object" +matrix x {{1 2 3} {4 5 6} {7 8 9}} +puts [$x info] + +puts "ND list object methods" +matrix X {{1 2} {3 4}} +$X ::= {format %0.2f $@.}; # Format values +$X print +$X --> Y; # Copy object +$Y .= {+ 1}; # Perform math operation +$Y print + +puts "ND list object access/manipulation" +# Access ND List Objects +matrix X {{1 2} {3 4}} +puts [$X @ : 1]; # get column value +$X @ 1* : --> Y; # create row vector (1D list) +$Y @ end .= {* 2}; # double last element of Y +puts [$Y info] + +puts "Element-wise expressions" +matrix x {{1 2} {3 4} {5 6}} +matrix y 5.0 +puts [nexpr {$@x + $@y}] + +puts "Self-operation, using index access commands" +matrix x [nrepeat {2 3} 1] +[$x := {$@. * [i]}] print + +puts "Shape and size" +set x {{1 2} {3 4} {5 6}} +puts [nshape 2D $x] +puts [nsize 2D $x] +# Convert scalar ND list object to matrix +scalar x 5.0 +$x ndims 2 +puts [$x shape] + +puts "Flatten and reshape ND lists" +vector x [nflatten 2D {{1 2 3 4} {5 6 7 8}}] +[$x reshape {2 2 2}] print + +puts "Transposing a matrix" +puts [ntranspose 2D {{1 2} {3 4}}] + +puts "Swapping axes of a tensor" +tensor x 3D {{{1 2} {3 4}} {{5 6} {7 8}}} +[$x transpose 0 2] print + +puts "Inserting rows and columns in a matrix" +# Insert row +puts [ninsert 2D {{1 2 3} {4 5 6} {7 8 9}} 0 {{A B C}}] +# Insert column +matrix x {1 2 3} +$x insert end {4 5 6} 1 +$x print + +puts "Stack tensors" +set x [nreshape 1D {1 2 3 4 5 6 7 8 9} {3 3 1}] +set y [nreshape 1D {A B C D E F G H I} {3 3 1}] +puts [ninsert 3D $x end $y 2] + +puts "Creating an identity matrix" +set I "" +for {set i 0} {$i < 3} {incr i} { + nset I $i $i 1 +} +set I [nfill 2D $I 0] +puts $I + +puts "Integer range generator" +puts [range 3] +puts [range 0 2] +puts [range 10 3 -2] +# Alternative for-loop +foreach i [range 5] { + puts $i +} \ No newline at end of file diff --git a/install.tcl b/install.tcl index ce7c000..3188e26 100644 --- a/install.tcl +++ b/install.tcl @@ -1,4 +1,4 @@ package require tin 1.0 tin depend vutil 1.1 -set dir [tin mkdir -force ndlist 0.1.1] +set dir [tin mkdir -force ndlist 0.2] file copy pkgIndex.tcl ndlist.tcl README.md LICENSE $dir diff --git a/ndlist.tcl b/ndlist.tcl index 3bc93de..11ef24c 100644 --- a/ndlist.tcl +++ b/ndlist.tcl @@ -18,10 +18,9 @@ namespace eval ::ndlist { variable nmap_i; # nmap index array array unset nmap_i variable nmap_break; # nmap break passer - variable filler 0; # Filler for nreplace # N-dimensional list access and mapping - namespace export ndlist nrepeat nrange; # Create ndlists + namespace export ndlist nrepeat; # Create ndlists namespace export nshape nsize; # Get dimensions and size namespace export nflatten nreshape; # Reshape an ndlist namespace export ntranspose ninsert; # Transpose and combine ndlists @@ -30,6 +29,8 @@ namespace eval ::ndlist { namespace export nop; # Math mapping over ndlists namespace export neval nexpr; # ND version of vutil leval and lexpr namespace export nmap i j k; # Functional mapping over ndlists + namespace export nfill; # Fill blanks with a value. + namespace export range; # Index range } # BASIC NDLIST CREATION AND METADATA @@ -211,7 +212,7 @@ proc ::ndlist::MaxShape {ndims ndlist} { # # Expand an ndlist to specified shape. Not the same as "nreshape". # If the given dimensions are smaller, it will throw an error. -# Fills with "$::ndlist::filler" (default 0) which can be modified by user. +# Fills with blanks. # # Syntax: # Expand $ndlist $n1 $n2 ... @@ -221,10 +222,9 @@ proc ::ndlist::MaxShape {ndims ndlist} { # n1 n2 ... New dimensions (must be greater) proc ::ndlist::Expand {ndlist n args} { - variable filler # Expand list as needed if {[llength $ndlist] < $n} { - lappend ndlist {*}[lrepeat [expr {$n-[llength $ndlist]}] $filler] + lappend ndlist {*}[lrepeat [expr {$n-[llength $ndlist]}] ""] } # Throw error if dimension is greater than n if {[llength $ndlist] != $n} { @@ -874,58 +874,6 @@ proc ::ndlist::Index2Integer {index n} { return $i } -# nrange -- -# -# Generate integer range -# -# nrange $n -# nrange $start $stop -# nrange $start $stop $step -# -# Arguments: -# n: Number of integers -# start: Start of resultant range. -# stop: End limit of resultant range. -# step: Step size. Default 1 or -1, depending on direction. - -proc ::ndlist::nrange {args} { - # Switch for arity - if {[llength $args] == 1} { - # Basic case - set n [lindex $args 0] - if {![string is integer -strict $n] || $n < 0} { - return -code error "n must be integer >= 0" - } - set start 0 - set stop [expr {$n - 1}] - set step 1 - } elseif {[llength $args] == 2} { - lassign $args start stop - if {![string is integer -strict $start]} { - return -code error "start must be integer" - } - if {![string is integer -strict $stop]} { - return -code error "stop must be integer" - } - set step [expr {$stop > $start ? 1 : -1}] - } elseif {[llength $args] == 3} { - lassign $args start stop step - if {![string is integer -strict $start]} { - return -code error "start must be integer" - } - if {![string is integer -strict $stop]} { - return -code error "stop must be integer" - } - if {![string is integer -strict $step]} { - return -code error "step must be integer" - } - } else { - return -code error "wrong # args: should be \"nrange n\",\ - \"nrange start stop\", or \"nrange start stop step\"" - } - return [Range $start $stop $step] -} - # Range -- # # Private handler to generate an integer range @@ -1427,6 +1375,11 @@ proc ::ndlist::Replace {list sublist iType iList} { # $ndobj insert $index $sublist <$axis> # # Insert an ndlist object into another ndlist object. + # + # Arguments: + # index Index to insert at + # sublist ndlist to insert + # axis Axis to insert along. Default 0. method insert {index sublist {axis 0}} { set sublist [ndlist $(ndims) $sublist] @@ -1434,6 +1387,17 @@ proc ::ndlist::Replace {list sublist iType iList} { return [self] } + # $ndobj fill $filler + # + # Fill blanks with a value. + # + # Arguments: + # filler Filler to replace blanks. + + method fill {filler} { + set (value) [nfill $(ndims) [my GetValue] $filler] + } + # @ -- # # Method to get or set a value (or ranges of values) in an ndlist @@ -1912,7 +1876,77 @@ proc ::ndlist::k {} { return [i 2] } +# nfill -- +# +# Fill all blanks in an ndlist with a value. +# +# Syntax: +# nfill $nd $ndlist $filler +# +# Arguments: +# nd Number of dimensions (e.g. 1D, 2D, etc.) +# ndlist ND list to get dimensions of +# filler Filler to replace blanks. + +proc ::ndlist::nfill {nd ndlist filler} { + nmap $nd value $ndlist { + expr {$value eq "" ? $filler : $value} + } +} + +# range -- +# +# Utility to generate integer range +# +# range $n +# range $start $stop +# range $start $stop $step +# +# Arguments: +# n: Number of integers +# start: Start of resultant range. +# stop: End limit of resultant range. +# step: Step size. Default 1 or -1, depending on direction. + +proc ::ndlist::range {args} { + # Switch for arity + if {[llength $args] == 1} { + # Basic case + set n [lindex $args 0] + if {![string is integer -strict $n] || $n < 0} { + return -code error "n must be integer >= 0" + } + set start 0 + set stop [expr {$n - 1}] + set step 1 + } elseif {[llength $args] == 2} { + lassign $args start stop + if {![string is integer -strict $start]} { + return -code error "start must be integer" + } + if {![string is integer -strict $stop]} { + return -code error "stop must be integer" + } + set step [expr {$stop > $start ? 1 : -1}] + } elseif {[llength $args] == 3} { + lassign $args start stop step + if {![string is integer -strict $start]} { + return -code error "start must be integer" + } + if {![string is integer -strict $stop]} { + return -code error "stop must be integer" + } + if {![string is integer -strict $step]} { + return -code error "step must be integer" + } + } else { + return -code error "wrong # args: should be \"range n\",\ + \"range start stop\", or \"range start stop step\"" + } + return [Range $start $stop $step] +} + ################################################################################ # Finally, provide the package -package provide ndlist 0.1.1 +package provide ndlist 0.2 diff --git a/pkgIndex.tcl b/pkgIndex.tcl index 544b74f..7e36f70 100644 --- a/pkgIndex.tcl +++ b/pkgIndex.tcl @@ -1,2 +1,2 @@ if {![package vsatisfies [package provide Tcl] 8.6]} {return} -package ifneeded ndlist 0.1.1 [list source [file join $dir ndlist.tcl]] +package ifneeded ndlist 0.2 [list source [file join $dir ndlist.tcl]] diff --git a/src/ndlist.tin b/src/ndlist.tin index 83566cf..d51eef6 100644 --- a/src/ndlist.tin +++ b/src/ndlist.tin @@ -18,10 +18,9 @@ namespace eval ::ndlist { variable nmap_i; # nmap index array array unset nmap_i variable nmap_break; # nmap break passer - variable filler 0; # Filler for nreplace # N-dimensional list access and mapping - namespace export ndlist nrepeat nrange; # Create ndlists + namespace export ndlist nrepeat; # Create ndlists namespace export nshape nsize; # Get dimensions and size namespace export nflatten nreshape; # Reshape an ndlist namespace export ntranspose ninsert; # Transpose and combine ndlists @@ -30,6 +29,8 @@ namespace eval ::ndlist { namespace export nop; # Math mapping over ndlists namespace export neval nexpr; # ND version of vutil leval and lexpr namespace export nmap i j k; # Functional mapping over ndlists + namespace export nfill; # Fill blanks with a value. + namespace export range; # Index range } # BASIC NDLIST CREATION AND METADATA @@ -211,7 +212,7 @@ proc ::ndlist::MaxShape {ndims ndlist} { # # Expand an ndlist to specified shape. Not the same as "nreshape". # If the given dimensions are smaller, it will throw an error. -# Fills with "$::ndlist::filler" (default 0) which can be modified by user. +# Fills with blanks. # # Syntax: # Expand $ndlist $n1 $n2 ... @@ -221,10 +222,9 @@ proc ::ndlist::MaxShape {ndims ndlist} { # n1 n2 ... New dimensions (must be greater) proc ::ndlist::Expand {ndlist n args} { - variable filler # Expand list as needed if {[llength $ndlist] < $n} { - lappend ndlist {*}[lrepeat [expr {$n-[llength $ndlist]}] $filler] + lappend ndlist {*}[lrepeat [expr {$n-[llength $ndlist]}] ""] } # Throw error if dimension is greater than n if {[llength $ndlist] != $n} { @@ -874,58 +874,6 @@ proc ::ndlist::Index2Integer {index n} { return $i } -# nrange -- -# -# Generate integer range -# -# nrange $n -# nrange $start $stop -# nrange $start $stop $step -# -# Arguments: -# n: Number of integers -# start: Start of resultant range. -# stop: End limit of resultant range. -# step: Step size. Default 1 or -1, depending on direction. - -proc ::ndlist::nrange {args} { - # Switch for arity - if {[llength $args] == 1} { - # Basic case - set n [lindex $args 0] - if {![string is integer -strict $n] || $n < 0} { - return -code error "n must be integer >= 0" - } - set start 0 - set stop [expr {$n - 1}] - set step 1 - } elseif {[llength $args] == 2} { - lassign $args start stop - if {![string is integer -strict $start]} { - return -code error "start must be integer" - } - if {![string is integer -strict $stop]} { - return -code error "stop must be integer" - } - set step [expr {$stop > $start ? 1 : -1}] - } elseif {[llength $args] == 3} { - lassign $args start stop step - if {![string is integer -strict $start]} { - return -code error "start must be integer" - } - if {![string is integer -strict $stop]} { - return -code error "stop must be integer" - } - if {![string is integer -strict $step]} { - return -code error "step must be integer" - } - } else { - return -code error "wrong # args: should be \"nrange n\",\ - \"nrange start stop\", or \"nrange start stop step\"" - } - return [Range $start $stop $step] -} - # Range -- # # Private handler to generate an integer range @@ -1427,6 +1375,11 @@ proc ::ndlist::Replace {list sublist iType iList} { # $ndobj insert $index $sublist <$axis> # # Insert an ndlist object into another ndlist object. + # + # Arguments: + # index Index to insert at + # sublist ndlist to insert + # axis Axis to insert along. Default 0. method insert {index sublist {axis 0}} { set sublist [ndlist $(ndims) $sublist] @@ -1434,6 +1387,17 @@ proc ::ndlist::Replace {list sublist iType iList} { return [self] } + # $ndobj fill $filler + # + # Fill blanks with a value. + # + # Arguments: + # filler Filler to replace blanks. + + method fill {filler} { + set (value) [nfill $(ndims) [my GetValue] $filler] + } + # @ -- # # Method to get or set a value (or ranges of values) in an ndlist @@ -1912,6 +1876,76 @@ proc ::ndlist::k {} { return [i 2] } +# nfill -- +# +# Fill all blanks in an ndlist with a value. +# +# Syntax: +# nfill $nd $ndlist $filler +# +# Arguments: +# nd Number of dimensions (e.g. 1D, 2D, etc.) +# ndlist ND list to get dimensions of +# filler Filler to replace blanks. + +proc ::ndlist::nfill {nd ndlist filler} { + nmap $nd value $ndlist { + expr {$value eq "" ? $filler : $value} + } +} + +# range -- +# +# Utility to generate integer range +# +# range $n +# range $start $stop +# range $start $stop $step +# +# Arguments: +# n: Number of integers +# start: Start of resultant range. +# stop: End limit of resultant range. +# step: Step size. Default 1 or -1, depending on direction. + +proc ::ndlist::range {args} { + # Switch for arity + if {[llength $args] == 1} { + # Basic case + set n [lindex $args 0] + if {![string is integer -strict $n] || $n < 0} { + return -code error "n must be integer >= 0" + } + set start 0 + set stop [expr {$n - 1}] + set step 1 + } elseif {[llength $args] == 2} { + lassign $args start stop + if {![string is integer -strict $start]} { + return -code error "start must be integer" + } + if {![string is integer -strict $stop]} { + return -code error "stop must be integer" + } + set step [expr {$stop > $start ? 1 : -1}] + } elseif {[llength $args] == 3} { + lassign $args start stop step + if {![string is integer -strict $start]} { + return -code error "start must be integer" + } + if {![string is integer -strict $stop]} { + return -code error "stop must be integer" + } + if {![string is integer -strict $step]} { + return -code error "step must be integer" + } + } else { + return -code error "wrong # args: should be \"range n\",\ + \"range start stop\", or \"range start stop step\"" + } + return [Range $start $stop $step] +} + ################################################################################ # Finally, provide the package