• Publicity: Public Only All

ad-functional-procs.tcl

Functional Programming in Tcl? - Absolutely!

This library adds the expressive power of functional languages like LISP, Gofer or Haskell to the Tcl language!

If you don't know what functional programming is, here's a good place to start:

A general naming convention in this file is:

f = a function
x = an element
xs = a list of elements

Location:
packages/acs-tcl/tcl/ad-functional-procs.tcl
Created:
March 29, 2000
Author:
Mark Dettinger <mdettinger@arsdigita.com>
CVS Identification:
$Id: ad-functional-procs.tcl,v 1.10.2.6 2021/02/22 16:12:38 antoniop Exp $ This was part of ACS 3 Added to OpenACS by bdolicki on 11 Feb 2004 I just converted proc_doc to ad_proc, added ad_library, fixed an unmatched brace in a doc string and wrapped everything in a namespace

Procedures in this file

Detailed information

f::abs (public)

 f::abs x

returns the absolute value of x

Parameters:
x

Partial Call Graph (max 5 caller/called nodes):
%3 f::gcd f::gcd (public) f::abs f::abs f::gcd->f::abs f::lcm f::lcm (public) f::lcm->f::abs

Testcases:
No testcase defined.

f::all (public)

 f::all pred xs

Takes a predicate pred and a list xs and returns 1 if all elements of xs fulfill pred.

Examples

  • all even_p {2 44 64 80 10} = 1
  • all even_p {2 44 65 80 10} = 0

Parameters:
pred
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::and f::and (public) f::map f::map (public) f::all f::all f::all->f::and f::all->f::map

Testcases:
No testcase defined.

f::and (public)

 f::and xs

reduces a list of boolean values using &&

Parameters:
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::all f::all (public) f::and f::and f::all->f::and f::fold f::fold (public) f::and->f::fold

Testcases:
No testcase defined.

f::any (public)

 f::any pred xs

takes a predicate pred and a list xs and returns 1 if there exists an element of xs that fulfills pred

Parameters:
pred
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::map f::map (public) f::or f::or (public) f::any f::any f::any->f::map f::any->f::or

Testcases:
No testcase defined.

f::bind (public, deprecated)

 f::bind f [ args... ]
Deprecated. Invoking this procedure generates a warning.

binds args to the first k arguments of the n-ary function f and returns the resulting (n-k)-ary function DEPRECATED: As of tcl8.5, Tcl has native support for 'lambda' provided by means of 'apply' per TIP 194. Tcllib provides a 'lambda' package with procs that make use of it.

Parameters:
f

See Also:

Partial Call Graph (max 5 caller/called nodes):
%3 f::choose f::choose (public, deprecated) f::bind f::bind f::choose->f::bind f::pascal f::pascal (public, deprecated) f::pascal->f::bind f::reverse f::reverse (public, deprecated) f::reverse->f::bind ad_get_tcl_call_stack ad_get_tcl_call_stack (public) f::bind->ad_get_tcl_call_stack f::lambda f::lambda (public, deprecated) f::bind->f::lambda

Testcases:
No testcase defined.

f::bind2nd (public, deprecated)

 f::bind2nd f arg
Deprecated. Invoking this procedure generates a warning.

binds arg to the 2nd argument of f DEPRECATED: As of tcl8.5, Tcl has native support for 'lambda' provided by means of 'apply' per TIP 194. Tcllib provides a 'lambda' package with procs that make use of it.

Parameters:
f
arg

See Also:

Partial Call Graph (max 5 caller/called nodes):
%3 ad_get_tcl_call_stack ad_get_tcl_call_stack (public) f::cons f::cons (public) f::head f::head (public) f::lambda f::lambda (public, deprecated) f::bind2nd f::bind2nd f::bind2nd->ad_get_tcl_call_stack f::bind2nd->f::cons f::bind2nd->f::head f::bind2nd->f::lambda

Testcases:
No testcase defined.

f::choose (public, deprecated)

 f::choose n k
Deprecated. Invoking this procedure generates a warning.

Here's how to compute 'n choose k' like a real nerd. DEPRECATED: As of tcl8.5, Tcl has native support for 'lambda' provided by means of 'apply' per TIP 194. Tcllib provides a 'lambda' package with procs that make use of it.

Parameters:
n
k

See Also:

Partial Call Graph (max 5 caller/called nodes):
%3 ad_get_tcl_call_stack ad_get_tcl_call_stack (public) f::bind f::bind (public, deprecated) f::enum_from_to f::enum_from_to (public) f::fold f::fold (public) f::iterate f::iterate (public) f::choose f::choose f::choose->ad_get_tcl_call_stack f::choose->f::bind f::choose->f::enum_from_to f::choose->f::fold f::choose->f::iterate

Testcases:
No testcase defined.

f::compose (public)

 f::compose f g x

function composition: evaluates f (g x)

Parameters:
f
g
x

Partial Call Graph (max 5 caller/called nodes):
%3

Testcases:
No testcase defined.

f::cons (public)

 f::cons x xs

inserts x at the front of the list xs

Parameters:
x
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::bind2nd f::bind2nd (public, deprecated) f::cons f::cons f::bind2nd->f::cons

Testcases:
No testcase defined.

f::const (public, deprecated)

 f::const k
Deprecated. Invoking this procedure generates a warning.

Returns a unary function that ignores its argument and constantly returns k.

Example

  • map [const 7] [list 1 2 3 4 5] = {7 7 7 7 7}
DEPRECATED: As of tcl8.5, Tcl has native support for 'lambda' provided by means of 'apply' per TIP 194. Tcllib provides a 'lambda' package with procs that make use of it.

Parameters:
k

See Also:

Partial Call Graph (max 5 caller/called nodes):
%3 ad_get_tcl_call_stack ad_get_tcl_call_stack (public) f::lambda f::lambda (public, deprecated) f::const f::const f::const->ad_get_tcl_call_stack f::const->f::lambda

Testcases:
No testcase defined.

f::copy (public)

 f::copy n x

returns list of n copies of x

Parameters:
n
x

Partial Call Graph (max 5 caller/called nodes):
%3

Testcases:
No testcase defined.

f::curry (public)

 f::curry f [ args... ]

Converts a function that takes one tuple as an argument into a function that takes a series of single arguments.

Parameters:
f

Partial Call Graph (max 5 caller/called nodes):
%3

Testcases:
No testcase defined.

f::cycle (public)

 f::cycle n xs

returns concatenated list of n copies of xs

Parameters:
n
xs

Partial Call Graph (max 5 caller/called nodes):
%3

Testcases:
No testcase defined.

f::drop (public)

 f::drop n xs

returns the remaining elements of xs (without the first n)

Parameters:
n
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::drop_while f::drop_while (public) f::drop f::drop f::drop_while->f::drop f::split_at f::split_at (public) f::split_at->f::drop

Testcases:
No testcase defined.

f::drop_while (public)

 f::drop_while p xs

returns the remaining portion of the list

Parameters:
p
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::span f::span (public) f::drop_while f::drop_while f::span->f::drop_while f::drop f::drop (public) f::drop_while->f::drop

Testcases:
No testcase defined.

f::elem_p (public)

 f::elem_p x xs

checks if x is contained in s

Parameters:
x
xs

Partial Call Graph (max 5 caller/called nodes):
%3

Testcases:
No testcase defined.

f::enum_from_to (public)

 f::enum_from_to lo hi

generates {lo lo+1 ... hi-1 hi}

Parameters:
lo
hi

Partial Call Graph (max 5 caller/called nodes):
%3 f::choose f::choose (public, deprecated) f::enum_from_to f::enum_from_to f::choose->f::enum_from_to f::factorial f::factorial (public) f::factorial->f::enum_from_to f::pascal f::pascal (public, deprecated) f::pascal->f::enum_from_to

Testcases:
No testcase defined.

f::even_p (public)

 f::even_p n

returns 1 if n is even and 0 otherwise

Parameters:
n

Partial Call Graph (max 5 caller/called nodes):
%3 acs_mail_lite::imap_check_incoming acs_mail_lite::imap_check_incoming (private) f::even_p f::even_p acs_mail_lite::imap_check_incoming->f::even_p acs_mail_lite::sched_parameters acs_mail_lite::sched_parameters (public) acs_mail_lite::sched_parameters->f::even_p f::prime_p f::prime_p (public) f::prime_p->f::even_p

Testcases:
No testcase defined.

f::factorial (public)

 f::factorial n

compute n!

Parameters:
n

Partial Call Graph (max 5 caller/called nodes):
%3 f::enum_from_to f::enum_from_to (public) f::product f::product (public) f::factorial f::factorial f::factorial->f::enum_from_to f::factorial->f::product

Testcases:
No testcase defined.

f::filter (public)

 f::filter pred xs

Returns all elements of the list xs that fulfill the predicate pred.

Examples

  • filter even_p {3 1 4 1 5 9 2 6} = {4 2 6}
  • filter [lambda {x} {expr $x>500}] {317 826 912 318} = {826 912}

Parameters:
pred
xs

Partial Call Graph (max 5 caller/called nodes):
%3

Testcases:
No testcase defined.

f::flip (public)

 f::flip f a b

takes a binary function 'f' and two arguments 'a' and 'b' and returns f b a (arguments are flipped)

Parameters:
f
a
b

Partial Call Graph (max 5 caller/called nodes):
%3

Testcases:
No testcase defined.

f::fold (public)

 f::fold f e xs

Takes a binary function f, a start element e and a list {x1 x2 ...} and returns f (...(f (f (f e x1) x2) x3)...).

Examples

  • fold + 0 [list 1 2 3 4] = 10
  • fold * 1 [list 1 2 3 4] = 24

Parameters:
f
e
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::and f::and (public) f::fold f::fold f::and->f::fold f::choose f::choose (public, deprecated) f::choose->f::fold f::fold1 f::fold1 (public) f::fold1->f::fold f::or f::or (public) f::or->f::fold f::product f::product (public) f::product->f::fold

Testcases:
No testcase defined.

f::fold1 (public)

 f::fold1 f xs

Takes a binary function f and a list {x1 x2 x3 ...} and returns (...(f (f (f x1 x2) x3) x4)...).

"fold1" behaves like "fold", but does not take a start element and does not work for empty lists.

Examples

  • fold1 min [list 3 1 4 1 5 9 2 6] = 1
  • fold1 max [list 3 1 4 1 5 9 2 6] = 9

Parameters:
f
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::lmax f::lmax (public) f::fold1 f::fold1 f::lmax->f::fold1 f::lmin f::lmin (public) f::lmin->f::fold1 f::fold f::fold (public) f::fold1->f::fold f::head f::head (public) f::fold1->f::head f::null_p f::null_p (public) f::fold1->f::null_p f::tail f::tail (public) f::fold1->f::tail

Testcases:
No testcase defined.

f::fst (public)

 f::fst xs

returns the first element of a list

Parameters:
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::mul f::mul (public) f::fst f::fst f::mul->f::fst

Testcases:
No testcase defined.

f::gcd (public)

 f::gcd x y

returns the greatest common divisor of x and y

Parameters:
x
y

Partial Call Graph (max 5 caller/called nodes):
%3 f::lcm f::lcm (public) f::gcd f::gcd f::lcm->f::gcd f::mul f::mul (public) f::mul->f::gcd f::abs f::abs (public) f::gcd->f::abs

Testcases:
No testcase defined.

f::head (public)

 f::head xs

first element of a list

Parameters:
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::bind2nd f::bind2nd (public, deprecated) f::head f::head f::bind2nd->f::head f::fold1 f::fold1 (public) f::fold1->f::head f::qsort f::qsort (public) f::qsort->f::head f::scanl1 f::scanl1 (public) f::scanl1->f::head f::transpose f::transpose (public) f::transpose->f::head

Testcases:
No testcase defined.

f::id (public)

 f::id x

Identity function: just returns its argument.

I'm not kidding! An identity function can be useful sometimes, e.g. as a default initializer for optional arguments of functional kind.

Parameters:
x

Partial Call Graph (max 5 caller/called nodes):
%3

Testcases:
No testcase defined.

f::init (public)

 f::init xs

all elements of a list but the last

Parameters:
xs

Partial Call Graph (max 5 caller/called nodes):
%3

Testcases:
No testcase defined.

f::iterate (public)

 f::iterate n f x

Returns {x (f x) (f (f x) (f (f (f x))) ...}.

Examples

  • iterate 10 [lambda {x} {expr $x+1}] 5 = {5 6 7 8 9 10 11 12 13 14}
  • iterate 10 [lambda {x} {expr $x*2}] 1 = {1 2 4 8 16 32 64 128 256 512}
  • iterate 4 tail {1 2 3 4 5} = {1 2 3 4 5} {2 3 4 5} {3 4 5} {4 5}

Parameters:
n
f
x

Partial Call Graph (max 5 caller/called nodes):
%3 f::choose f::choose (public, deprecated) f::iterate f::iterate f::choose->f::iterate

Testcases:
No testcase defined.

f::lambda (public, deprecated)

 f::lambda args body
Deprecated. Invoking this procedure generates a warning.

The lambda function - one of the foundations of functional programming - defines an anonymous proc and returns it. This is useful if you quickly need an auxiliary function for a small task.

Examples

  • map [lambda {x} {expr $x*$x}] {1 2 3 4 5}
    = {1 4 9 16 25}
  • zip_with [lambda {x y} {return "$x and $y"}] {1 2 3} {4 5 6}
    = "1 and 4" "2 and 5" "3 and 6"

Note

Although lambda defines a proc and therefore consumes memory, executing the same lambda expression twice will just re-define this proc. Thus, there is no memory leak, if you have a lambda inside a loop. DEPRECATED: As of tcl8.5, Tcl has native support for 'lambda' provided by means of 'apply' per TIP 194. Tcllib provides a 'lambda' package with procs that make use of it.

Parameters:
args
body

See Also:

Partial Call Graph (max 5 caller/called nodes):
%3 f::bind f::bind (public, deprecated) f::lambda f::lambda f::bind->f::lambda f::bind2nd f::bind2nd (public, deprecated) f::bind2nd->f::lambda f::const f::const (public, deprecated) f::const->f::lambda ad_get_tcl_call_stack ad_get_tcl_call_stack (public) f::lambda->ad_get_tcl_call_stack

Testcases:
No testcase defined.

f::last (public)

 f::last xs

last element of a list

Parameters:
xs

Partial Call Graph (max 5 caller/called nodes):
%3

Testcases:
No testcase defined.

f::lcm (public)

 f::lcm x y

returns the least common multiple of x and y

Parameters:
x
y

Partial Call Graph (max 5 caller/called nodes):
%3 f::/ f::/ f::abs f::abs (public) f::gcd f::gcd (public) f::lcm f::lcm f::lcm->f::/ f::lcm->f::abs f::lcm->f::gcd

Testcases:
No testcase defined.

f::lmax (public)

 f::lmax xs

returns the maximum element of the list xs

Parameters:
xs

Partial Call Graph (max 5 caller/called nodes):
%3 acs_mail_lite::inbound_prioritize acs_mail_lite::inbound_prioritize (public) f::lmax f::lmax acs_mail_lite::inbound_prioritize->f::lmax f::fold1 f::fold1 (public) f::lmax->f::fold1

Testcases:
No testcase defined.

f::lmin (public)

 f::lmin xs

returns the minimum element of the list xs

Parameters:
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::fold1 f::fold1 (public) f::lmin f::lmin f::lmin->f::fold1

Testcases:
No testcase defined.

f::map (public)

 f::map f xs

Takes a function f and a list { x1 x2 x3 ...}, applies the function on each element of the list and returns the result, i.e. { f x1, f x2, f x3, ...}.

Examples

(fib = fibonacci function, sqr = square function)
  • Applying a function to each element of a list:
    map fib [list 0 1 2 3 4 5 6 7 8] = {0 1 1 2 3 5 8 13 21}

  • Applying a function to each element of a matrix (a list of lists) can be done with a nested call:
    map [lambda {row} {map sqr $row}] [list [list 1 2 3] [list 4 5 6]] = {{1 4 9} {16 25 36}}

Parameters:
f
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::all f::all (public) f::map f::map f::all->f::map f::any f::any (public) f::any->f::map f::pascal f::pascal (public, deprecated) f::pascal->f::map

Testcases:
No testcase defined.

f::max (public)

 f::max x y

returns the maximum of x and y

Parameters:
x
y

Partial Call Graph (max 5 caller/called nodes):
%3 acs_mail_lite::inbound_prioritize acs_mail_lite::inbound_prioritize (public) f::max f::max acs_mail_lite::inbound_prioritize->f::max

Testcases:
No testcase defined.

f::min (public)

 f::min x y

returns the minimum of x and y

Parameters:
x
y

Partial Call Graph (max 5 caller/called nodes):
%3 acs_mail_lite::inbound_prioritize acs_mail_lite::inbound_prioritize (public) f::min f::min acs_mail_lite::inbound_prioritize->f::min

Testcases:
No testcase defined.

f::mul (public)

 f::mul n fraction

multiplies n with a fraction (given as a tuple)

Parameters:
n
fraction

Partial Call Graph (max 5 caller/called nodes):
%3 f::fst f::fst (public) f::gcd f::gcd (public) f::snd f::snd (public) f::mul f::mul f::mul->f::fst f::mul->f::gcd f::mul->f::snd

Testcases:
No testcase defined.

f::not_elem_p (public)

 f::not_elem_p x xs

checks if x is not contained in s

Parameters:
x
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::nub f::nub (public) f::not_elem_p f::not_elem_p f::nub->f::not_elem_p

Testcases:
No testcase defined.

f::nub (public)

 f::nub xs

removes duplicates from xs

Parameters:
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::not_elem_p f::not_elem_p (public) f::nub f::nub f::nub->f::not_elem_p

Testcases:
No testcase defined.

f::null_p (public)

 f::null_p xs

checks if xs is the empty list

Parameters:
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::fold1 f::fold1 (public) f::null_p f::null_p f::fold1->f::null_p f::scanl1 f::scanl1 (public) f::scanl1->f::null_p f::transpose f::transpose (public) f::transpose->f::null_p f::zip_with f::zip_with (public) f::zip_with->f::null_p

Testcases:
No testcase defined.

f::odd_p (public)

 f::odd_p n

returns 1 if n is odd and 0 otherwise

Parameters:
n

Partial Call Graph (max 5 caller/called nodes):
%3

Testcases:
No testcase defined.

f::or (public)

 f::or xs

reduces a list of boolean values using ||

Parameters:
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::any f::any (public) f::or f::or f::any->f::or f::fold f::fold (public) f::or->f::fold

Testcases:
No testcase defined.

f::pascal (public, deprecated)

 f::pascal size
Deprecated. Invoking this procedure generates a warning.

prints Pascal's triangle DEPRECATED: As of tcl8.5, Tcl has native support for 'lambda' provided by means of 'apply' per TIP 194. Tcllib provides a 'lambda' package with procs that make use of it.

Parameters:
size

See Also:

Partial Call Graph (max 5 caller/called nodes):
%3 ad_get_tcl_call_stack ad_get_tcl_call_stack (public) f::bind f::bind (public, deprecated) f::enum_from_to f::enum_from_to (public) f::map f::map (public) f::pascal f::pascal f::pascal->ad_get_tcl_call_stack f::pascal->f::bind f::pascal->f::enum_from_to f::pascal->f::map

Testcases:
No testcase defined.

f::prime_p (public)

 f::prime_p n
Parameters:
n
Returns:
1 if n is prime

Partial Call Graph (max 5 caller/called nodes):
%3 f::even_p f::even_p (public) f::prime_p f::prime_p f::prime_p->f::even_p

Testcases:
No testcase defined.

f::product (public)

 f::product xs

returns the product of the elements of the list xs

Parameters:
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::factorial f::factorial (public) f::product f::product f::factorial->f::product f::fold f::fold (public) f::product->f::fold

Testcases:
No testcase defined.

f::products (public)

 f::products xs

returns the list of partial products of the list xs

Parameters:
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::scanl f::scanl (public) f::products f::products f::products->f::scanl

Testcases:
No testcase defined.

f::qsort (public)

 f::qsort xs [ value ]

sorts a sequence with the quicksort algorithm

Parameters:
xs
value (defaults to "id")

Partial Call Graph (max 5 caller/called nodes):
%3 f::head f::head (public) f::tail f::tail (public) f::qsort f::qsort f::qsort->f::head f::qsort->f::tail

Testcases:
No testcase defined.

f::reverse (public, deprecated)

 f::reverse xs
Deprecated. Invoking this procedure generates a warning.

reverses the list xs. Tcl has a built-in support for reversing lists: "lreverse". Use this instead.

Parameters:
xs

See Also:
  • lreverse

Partial Call Graph (max 5 caller/called nodes):
%3 ad_get_tcl_call_stack ad_get_tcl_call_stack (public) f::bind f::bind (public, deprecated) f::fold f::fold (public) f::reverse f::reverse f::reverse->ad_get_tcl_call_stack f::reverse->f::bind f::reverse->f::fold

Testcases:
No testcase defined.

f::scanl (public)

 f::scanl f e xs

takes a binary function f, a start element e and a list {x1 x2 ...} and returns {e (f e x1) (f (f e x1) x2) ...}

Parameters:
f
e
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::products f::products (public) f::scanl f::scanl f::products->f::scanl f::scanl1 f::scanl1 (public) f::scanl1->f::scanl f::sums f::sums (public) f::sums->f::scanl

Testcases:
No testcase defined.

f::scanl1 (public)

 f::scanl1 f xs

takes a binary function f and a list {x1 x2 x3 ...} and returns {x1 (f x1 x2) (f (f x1 x2) x3) ...}

Parameters:
f
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::head f::head (public) f::null_p f::null_p (public) f::scanl f::scanl (public) f::tail f::tail (public) f::scanl1 f::scanl1 f::scanl1->f::head f::scanl1->f::null_p f::scanl1->f::scanl f::scanl1->f::tail

Testcases:
No testcase defined.

f::snd (public)

 f::snd xs

returns the second element of a list

Parameters:
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::mul f::mul (public) f::snd f::snd f::mul->f::snd

Testcases:
No testcase defined.

f::span (public)

 f::span p xs

splits a list using take_while and drop_while

Parameters:
p
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::drop_while f::drop_while (public) f::take_while f::take_while (public) f::span f::span f::span->f::drop_while f::span->f::take_while

Testcases:
No testcase defined.

f::split_at (public)

 f::split_at n xs

splits a list using take and drop

Parameters:
n
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::drop f::drop (public) f::take f::take (public) f::split_at f::split_at f::split_at->f::drop f::split_at->f::take

Testcases:
No testcase defined.

f::sum (public)

 f::sum xs

returns the sum of the elements of the list xs

Parameters:
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::fold f::fold (public) f::sum f::sum f::sum->f::fold

Testcases:
No testcase defined.

f::sums (public)

 f::sums xs

returns the list of partial sums of the list xs

Parameters:
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::scanl f::scanl (public) f::sums f::sums f::sums->f::scanl

Testcases:
No testcase defined.

f::tail (public)

 f::tail xs

all elements of a list but the first

Parameters:
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::fold1 f::fold1 (public) f::tail f::tail f::fold1->f::tail f::qsort f::qsort (public) f::qsort->f::tail f::scanl1 f::scanl1 (public) f::scanl1->f::tail f::transpose f::transpose (public) f::transpose->f::tail

Testcases:
No testcase defined.

f::take (public)

 f::take n xs

returns the first n elements of xs

Parameters:
n
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::split_at f::split_at (public) f::take f::take f::split_at->f::take f::take_until f::take_until (public) f::take_until->f::take f::take_while f::take_while (public) f::take_while->f::take f::- f::- f::take->f::-

Testcases:
No testcase defined.

f::take_until (public)

 f::take_until p xs

returns the list of elements up to and including the first element of xs which satisfies p

Parameters:
p
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::take f::take (public) f::take_until f::take_until f::take_until->f::take

Testcases:
No testcase defined.

f::take_while (public)

 f::take_while p xs

returns the longest initial segment of xs whose elements satisfy p

Parameters:
p
xs

Partial Call Graph (max 5 caller/called nodes):
%3 f::span f::span (public) f::take_while f::take_while f::span->f::take_while f::take f::take (public) f::take_while->f::take

Testcases:
No testcase defined.

f::thd (public)

 f::thd xs

returns the third element of a list

Parameters:
xs

Partial Call Graph (max 5 caller/called nodes):
%3

Testcases:
No testcase defined.

f::transpose (public)

 f::transpose lists

tranposes a matrix (a list of lists)

Parameters:
lists

Partial Call Graph (max 5 caller/called nodes):
%3 f::choose f::choose (public, deprecated) f::transpose f::transpose f::choose->f::transpose f::zip f::zip (public) f::zip->f::transpose f::head f::head (public) f::transpose->f::head f::null_p f::null_p (public) f::transpose->f::null_p f::tail f::tail (public) f::transpose->f::tail

Testcases:
No testcase defined.

f::uncurry (public)

 f::uncurry f tuple

Converts a function that takes a series of single arguments into a function that takes one tuple as an argument.

Example

  • min 3 5 = 3
  • min {3 5} = error (because min expects two arguments)
  • uncurry min {3 5} = 3

Parameters:
f
tuple

Partial Call Graph (max 5 caller/called nodes):
%3

Testcases:
No testcase defined.

f::unzip (public)

 f::unzip xs

unzip takes a list of tuples {x1 y1} {x2 y2} {x3 y3} ... and returns a tuple of lists {x1 x2 x3 ...} {y1 y2 y3 ...}.

Parameters:
xs

Partial Call Graph (max 5 caller/called nodes):
%3

Testcases:
No testcase defined.

f::zip (public)

 f::zip [ args... ]

takes two lists {x1 x2 x3 ...} and {y1 y2 y3 ...} and returns a list of tuples {x1 y1} {x2 y2} {x3 y3} ... Works analogously with 3 or more lists.

Partial Call Graph (max 5 caller/called nodes):
%3 f::transpose f::transpose (public) f::zip f::zip f::zip->f::transpose

Testcases:
No testcase defined.

f::zip_with (public)

 f::zip_with f xs ys

takes two lists {x1 x2 x3 ...} and {y1 y2 y3 ...} and returns the list {(f x1 y1) (f x2 y2) (f x3 y3) ...}

Parameters:
f
xs
ys

Partial Call Graph (max 5 caller/called nodes):
%3 f::null_p f::null_p (public) f::zip_with f::zip_with f::zip_with->f::null_p

Testcases:
No testcase defined.
[ hide source ] | [ make this the default ]

Content File Source

# ad-functional-procs.tcl

ad_library {

    Functional Programming in Tcl? - Absolutely!
    <p>
    This library adds the expressive power of functional languages
    like LISP, Gofer or Haskell to the Tcl language!
    <p>
    If you don't know what functional programming is,
    here's a good place to start:
    <ul>
    <li><a href="http://www.haskell.org/aboutHaskell.html">http://www.haskell.org/aboutHaskell.html</a>
    </ul>
    A general naming convention in this file is:
    <p>
    f  = a function <br>
    x  = an element <br>
    xs = a list of elements

    @author Mark Dettinger (mdettinger@arsdigita.com)
    @creation-date March 29, 2000
    @cvs-id $Id: ad-functional-procs.tcl,v 1.10.2.6 2021/02/22 16:12:38 antoniop Exp $

    This was part of ACS 3
    Added to OpenACS by bdolicki on 11 Feb 2004
    I just converted proc_doc to ad_proc, added ad_library, fixed an unmatched
    brace in a doc string  and wrapped everything in a namespace
}

namespace eval ::f {

# This library was completely rewritten on July 18, 2000.
# The design is now much cleaner. Constructed functions
# are no longer represented by strings, but by real
# (callable) function objects. The auxiliary functions
# eval_unary and eval_binary are gone.

# Special thanks go to Sarah Arnold and Carsten Clasohm for extensive
# testing of this library and using it in the Sharenet project.
# Also many thanks to Branimir Dolicki for inventing the lambda function
# and to Archit Shah for finding a simple way to eliminate its memory leak.

# --------------------------------------------------------------------------------
# Lambda
# --------------------------------------------------------------------------------

ad_proc -public -deprecated lambda {args body} {
    The lambda function - one of the foundations of functional programming -
    defines an anonymous proc and returns it. This is useful if you quickly
    need an auxiliary function for a small task.
    <h4>Examples</h4>
    <ul>
    <li><code>
    map [lambda {x} {expr $x*$x}] {1 2 3 4 5} <br>
    = {1 4 9 16 25}
    </code>
    <li><code>
    zip_with [lambda {x y} {return "$x and $y"}] {1 2 3} {4 5 6} <br>
    = "1 and 4" "2 and 5" "3 and 6"
    </code>
    </ul>
    <h4>Note</h4>
    Although lambda defines a proc and therefore consumes memory, executing
    the same lambda expression twice will just re-define this proc.
    Thus, there is no memory leak, if you have a lambda inside a loop.

    DEPRECATED: As of tcl8.5, Tcl has native support for 'lambda' provided by
                means of 'apply' per TIP 194. Tcllib provides a 'lambda' package
                with procs that make use of it.

    @see https://www.tcl-lang.org/man/tcl/TclCmd/apply.htm
} {
    proc $args.$body $args $body
    return $args.$body
}

# I know, I know - it looks sooo harmless. But it unleashes the real power of Tcl.
# It defines a proc with name "args.body" (weird, but unique name) that takes "args"
# as arguments and has the body "body". Then, this proc is returned.

# Example:
# [lambda {x} {expr $x*$x}] 5 = 25

# --------------------------------------------------------------------------------
# binding values to arguments of a function
# --------------------------------------------------------------------------------

ad_proc -deprecated bind {f args} {
    binds args to the first k arguments of the n-ary function f
    and returns the resulting (n-k)-ary function

    DEPRECATED: As of tcl8.5, Tcl has native support for 'lambda' provided by
    means of 'apply' per TIP 194. Tcllib provides a 'lambda' package
    with procs that make use of it.

    @see https://www.tcl-lang.org/man/tcl/TclCmd/apply.htm
} {
    set i 0
    foreach arg $args {
        append code "set [lindex [info args $f$i] {$arg}\n"
        incr i
    }
    append code [info body $f]
    set proc_args [info args $f]
    set num_proc_args [llength $proc_args]
    lambda [lrange $proc_args [llength $args$num_proc_args$code
}

ad_proc -deprecated bind2nd {f arg} {
    binds arg to the 2nd argument of f

    DEPRECATED: As of tcl8.5, Tcl has native support for 'lambda' provided by
    means of 'apply' per TIP 194. Tcllib provides a 'lambda' package
    with procs that make use of it.

    @see https://www.tcl-lang.org/man/tcl/TclCmd/apply.htm
} {
    set code "set [lindex [info args $f] 1] {$arg}\n"
    append code [info body $f]
    set proc_args [info args $f]
    set num_proc_args [llength $proc_args]
    lambda [cons [head $proc_args] [lrange $proc_args 2 $num_proc_args]] $code
}

# --------------------------------------------------------------------------------
# We now define several binary operators as procs, so we can pass them
# as arguments to other functions.
# --------------------------------------------------------------------------------

proc +  {a b} {expr {$a +  $b}}
proc -  {a b} {expr {$a -  $b}}
proc *  {a b} {expr {$a *  $b}}
proc /  {a b} {expr {$a /  $b}}
proc && {a b} {expr {$a && $b}}
proc || {a b} {expr {$a || $b}}
proc >  {a b} {expr {$a >  $b}}
proc <  {a b} {expr {$a <  $b}}

# Example:
# + 5 6 = 11

# --------------------------------------------------------------------------------
# map
# --------------------------------------------------------------------------------

ad_proc -public map {f xs} {
    Takes a function f and a list { x1 x2 x3 ...},
    applies the function on each element of the list
    and returns the result, i.e. { f x1, f x2, f x3, ...}.
    <h4>Examples</h4>
    (fib = fibonacci function, sqr = square function)
    <ul>
    <li>Applying a function to each element of a list:<br>
    <code>map fib [list 0 1 2 3 4 5 6 7 8] = {0 1 1 2 3 5 8 13 21}
    </code>
    <p>
    <li>Applying a function to each element of a matrix (a list of lists)
    can be done with a nested call:<br>
    <code>map [lambda {row} {map sqr $row}] [list [list 1 2 3] [list 4 5 6]] = {{1 4 9} {16 25 36}}
    </code>
    </ul>
} {
    set result {}
    foreach x $xs {
        lappend result [$f $x]
    }
    return $result
}

# --------------------------------------------------------------------------------
# fold
# --------------------------------------------------------------------------------

ad_proc -public fold {f e xs} {
    Takes a binary function f, a start element e and a list {x1 x2 ...}
    and returns f (...(f (f (f e x1) x2) x3)...).
    <h4>Examples</h4>
    <ul>
    <li>
    <code>
    fold + 0 [list 1 2 3 4] = 10
    </code><li><code>
    fold * 1 [list 1 2 3 4] = 24
    </code>
    </ul>
} {
    set result $e
    foreach x $xs {
        set result [$f $result $x]
    }
    return $result
}

ad_proc -public fold1 {f xs} {
    Takes a binary function f and a list {x1 x2 x3 ...}
    and returns (...(f (f (f x1 x2) x3) x4)...).
    <p>
    "fold1" behaves like "fold", but does not take a start element and
    does not work for empty lists.
    <h4>Examples</h4>
    <ul>
    <li>
    <code>
    fold1 min [list 3 1 4 1 5 9 2 6] = 1
    </code><li><code>
    fold1 max [list 3 1 4 1 5 9 2 6] = 9
    </code>
    </ul>
} {
    if { [null_p $xs] } {
        error "ERROR: fold1 is undefined for empty lists."
    } else {
        fold $f [head $xs] [tail $xs]
    }
}

# --------------------------------------------------------------------------------
# scanl
# --------------------------------------------------------------------------------

d_proc -public scanl {f e xs} "takes a binary function f, a start element e and a list {x1 x2 ...}
                         and returns {e (f e x1) (f (f e x1) x2) ...}" {
    set current_element $e
    set result [list $e]
    foreach x $xs {
        set current_element [$f $current_element $x]
        lappend result $current_element
    }
    return $result
}

# Example:
# scanl + 0 [list 1 2 3 4] = {0 1 3 6 10}
# scanl * 1 [list 1 2 3 4] = {1 1 2 6 24}

d_proc -public scanl1 {f xs} "takes a binary function f and a list {x1 x2 x3 ...}
                       and returns {x1 (f x1 x2) (f (f x1 x2) x3) ...}" {
    if { [null_p $xs] } {
        error "ERROR: scanl1 is undefined for empty lists."
    } else {
        scanl $f [head $xs] [tail $xs]
    }
}

# "scanl1" behaves like "scanl", but does not take a start element and
# does not work for empty lists.
#
# Example:
# scanl1 min [list 3 1 4 1 5 9 2 6] = {3 1 1 1 1 1 1 1}
# scanl1 max [list 3 1 4 1 5 9 2 6] = {3 3 4 4 5 9 9 9}

# --------------------------------------------------------------------------------
# Standard combinators
# --------------------------------------------------------------------------------

ad_proc -public id {x} {
    Identity function: just returns its argument.
    <p>
    I'm not kidding! An identity function can be useful sometimes, e.g.
    as a default initializer for optional arguments of functional kind.
} {
    return $x
}

# Example application of id function:

d_proc -public qsort {xs {value id}} "sorts a sequence with the quicksort algorithm" {
    if { [llength $xs]<2 } { return $xs }
    set pivot [head $xs]
    set big_elmts {}
    set small_elmts {}
    foreach x [tail $xs] {
        if { [$value $x] > [$value $pivot] } {
            lappend big_elmts $x
        } else {
            lappend small_elmts $x
        }
    }
    concat [qsort $small_elmts $value] [list $pivot] [qsort $big_elmts $value]
}

# % qsort {5 2 9 4}
# 2 4 5 9
# % qsort {Oracle ArsDigita SAP Vignette} [lambda {s} {string length $s}]
# SAP Oracle Vignette ArsDigita

ad_proc -deprecated const {k} {
    Returns a unary function that ignores its argument and constantly returns k.
    <h4>Example</h4>
    <ul><li><code>
    map [const 7] [list 1 2 3 4 5] = {7 7 7 7 7}
    </code></ul>

    DEPRECATED: As of tcl8.5, Tcl has native support for 'lambda' provided by
    means of 'apply' per TIP 194. Tcllib provides a 'lambda' package
    with procs that make use of it.

    @see https://www.tcl-lang.org/man/tcl/TclCmd/apply.htm
} {
    lambda {x} [list return $k]
}

ad_proc -public curry {f args} {
    Converts a function that takes one tuple as an argument
    into a function that takes a series of single arguments.
} {
    uplevel [list $f $args]
}

ad_proc -public uncurry {f tuple} {
    Converts a function that takes a series of single arguments
    into a function that takes one tuple as an argument.
    <h4>Example</h4>
    <ul>
    <li><code>min 3 5 = 3</code>
    <li><code>min {3 5} = error</code> (because min expects two arguments)
    <li><code>uncurry min {3 5} = 3</code>
    </ul>
} {
    uplevel [list eval "$f $tuple"]
}

# Exercise 1
# ----------
# Using "map" and "uncurry", convert the tuple list
# {{3 1} {4 1} {5 9} {2 6}} into {1 1 5 2} (each tuple is replaced
# by the minimum of its two components).

d_proc -public fst {xs} "returns the first element of a list" {
    lindex $xs 0
}

d_proc -public snd {xs} "returns the second element of a list" {
    lindex $xs 1
}

d_proc -public thd {xs} "returns the third element of a list" {
    lindex $xs 2
}

# Example:
# set people [db_list_of_lists get "select first_name, last_name, email ..."]
# set first_names [map fst $people]
# set last_names  [map snd $people]
# set emails      [map thd $people]

d_proc -public flip {f a b} "takes a binary function 'f' and two arguments 'a' and 'b'
                       and returns f b a (arguments are flipped)" {
    $f $b $a
}

# Example:
# flip lindex 0 {42 37 59 14} = 42

# Exercise 2
# ----------
# Using "fold", "map", "flip" and "lindex",
# compute the sum of the 4th column of the matrix
# [list [list 3 1 4 1 5]
#       [list 9 2 6 5 3]
#       [list 5 8 9 7 9]
#       [list 3 2 3 8 4]]
# Hint:
# First try to extract the list {1 5 7 8} using "map", "flip" and "lindex",
# then reduce it to 21 using "fold".

d_proc -public compose {f g x} "function composition: evaluates f (g x)" {
    $f [$g $x]
}

# Example:
# map [bind compose sqr [bind + 7]] {1 2 3 4 5} = {64 81 100 121 144}

# Algebraic Property:
# map [bind compose f g] $xs = map f [map g $xs]

# --------------------------------------------------------------------------------
# Standard numerical functions
# --------------------------------------------------------------------------------

d_proc -public abs {x} "returns the absolute value of x" {
    expr {$x<0 ? -$x : $x}
}

d_proc -public gcd {x y} "returns the greatest common divisor of x and y" {
    gcd' [abs $x] [abs $y]
}

proc gcd' {x y} {
    if { $y==0 } { return $x }
    gcd$y [expr {$x%$y}]
}

d_proc -public lcm {x y} "returns the least common multiple of x and y" {
    if { $x==0} { return 0 }
    if { $y==0} { return 0 }
    abs [expr {$x/[gcd $x $y]*$y}]
}

d_proc -public odd_p {n} "returns 1 if n is odd and 0 otherwise" {
    expr {$n%2}
}

d_proc -public even_p {n} "returns 1 if n is even and 0 otherwise" {
    expr {1-$n%2}
}

d_proc -public min {x y} "returns the minimum of x and y" {
    expr {$x<$y ? $x : $y}
}

d_proc -public max {x y} "returns the maximum of x and y" {
    expr {$x>$y ? $x : $y}
}

# --------------------------------------------------------------------------------
# List Aggregate Functions
# --------------------------------------------------------------------------------

d_proc -public and {xs} "reduces a list of boolean values using &&" {
    fold && 1 $xs
}

# Example
# and {1 1 0 1} = 0
# and {1 1 1 1} = 1

d_proc -public or {xs} "reduces a list of boolean values using ||" {
    fold || 0 $xs
}

# Example
# or {1 1 0 1} = 1
# or {0 0 0 0} = 0

ad_proc -public all {pred xs} {
    Takes a predicate pred and a list xs and returns 1
    if all elements of xs fulfill pred.
    <h4>Examples</h4>
    <ul><li>
    <code>
    all even_p {2 44 64 80 10} = 1
    </code>
    <li>
    <code>
    all even_p {2 44 65 80 10} = 0
    </code>
    </ul>
} {
    and [map $pred $xs]
}

d_proc -public any {pred xs} "takes a predicate pred and a list xs and returns 1
                        if there exists an element of xs that fulfills pred" {
    or [map $pred $xs]
}

# Example:
# any odd_p {2 44 64 80 10} = 0
# any odd_p {2 44 65 80 10} = 1

d_proc -public lmin {xs} "returns the minimum element of the list xs" {
    fold1 min $xs
}

d_proc -public lmax {xs} "returns the maximum element of the list xs" {
    fold1 max $xs
}

d_proc -public sum {xs} "returns the sum of the elements of the list xs" {
    fold + 0 $xs
}

d_proc -public product {xs} "returns the product of the elements of the list xs" {
    fold * 1 $xs
}

d_proc -public sums {xs} "returns the list of partial sums of the list xs" {
    scanl + 0 $xs
}

d_proc -public products {xs} "returns the list of partial products of the list xs" {
    scanl * 1 $xs
}

# --------------------------------------------------------------------------------
# Standard list processing functions
# --------------------------------------------------------------------------------

d_proc -public head {xs} "first element of a list" {
    lindex $xs 0
}

d_proc -public last {xs} "last element of a list" {
    lindex $xs [expr {[llength $xs]-1}]
}

d_proc -public init {xs} "all elements of a list but the last" {
    lrange $xs 0 [expr {[llength $xs]-2}]
}

d_proc -public tail {xs} "all elements of a list but the first" {
    lrange $xs 1 [expr {[llength $xs]-1}]
}

d_proc -public take {n xs} "returns the first n elements of xs" {
    lrange $xs 0 [expr {$n-1}]
}

d_proc -public drop {n xs} "returns the remaining elements of xs (without the first n)" {
    lrange $xs $n [expr {[llength $xs]-1}]
}

ad_proc -public filter {pred xs} {
    Returns all elements of the list <em>xs</em> that fulfill the predicate <em>pred</em>.
    <h4>Examples</h4>
    <ul>
    <li><code>filter even_p {3 1 4 1 5 9 2 6} = {4 2 6}</code>
    <li><code>filter [lambda {x} {expr $x>500}] {317 826 912 318} = {826 912}</code>
    </ul>
} {
    set result {}
    foreach x $xs {
        if { [$pred $x] } {
            lappend result $x
        }
    }
    return $result
}

d_proc -public copy {n x} "returns list of n copies of x" {
    set result {}
    for {set i 0} {$i<$n} {incr i} {
        lappend result $x
    }
    return $result
}

# Example:
# copy 10 7 = {7 7 7 7 7 7 7 7 7 7}

d_proc -public cycle {n xs} "returns concatenated list of n copies of xs" {
    set result {}
    for {set i 0} {$i<$n} {incr i} {
        lappend result {*}$xs
    }
    return $result
}

# Example:
# cycle 4 {1 2 3} = {1 2 3 1 2 3 1 2 3 1 2 3}

d_proc -public cons {x xs} "inserts x at the front of the list xs" {
    concat [list $x$xs
}

ad_proc -deprecated -public reverse {xs} {
    reverses the list xs.

    Tcl has a built-in support for reversing lists: "lreverse".
    Use this instead.    

    @see lreverse
} {
    fold [bind flip cons] {} $xs
}

d_proc -public elem_p {x xs} "checks if x is contained in s" {
    expr {[lsearch $xs $x]==-1 ? 0 : 1}
}

d_proc -public not_elem_p {x xs} "checks if x is not contained in s" {
    expr {[lsearch $xs $x]==-1 ? 1 : 0}
}

d_proc -public nub {xs} "removes duplicates from xs" {
    set result {}
    foreach x $xs {
        if { [not_elem_p $x $result] } {
            lappend result $x
        }
    }
    return $result
}

d_proc -public null_p {xs} "checks if xs is the empty list" {
    expr {[llength $xs]==0}
}

d_proc -public enum_from_to {lo hi} "generates {lo lo+1 ... hi-1 hi}" {
    set result {}
    for {set i $lo} {$i<=$hi} {incr i} {
        lappend result $i
    }
    return $result
}

# --------------------------------------------------------------------------------
# zip and zip_with functions
# --------------------------------------------------------------------------------

d_proc -public zip {args} "takes two lists {x1 x2 x3 ...} and {y1 y2 y3 ...} and
                     returns a list of tuples {x1 y1} {x2 y2} {x3 y3} ...
                     Works analogously with 3 or more lists." {
    transpose $args
}

# Example:
# % set first_names {Nicole Tom}
# % set last_names  {Kidman Cruise}
# % zip $first_names $last_names
# {Nicole Kidman} {Tom Cruise}
# % map [bind flip join _] [zip $first_names $last_names]
# Nicole_Kidman Tom_Cruise

d_proc -public zip_with {f xs ys} "takes two lists {x1 x2 x3 ...} and {y1 y2 y3 ...} and
                             returns the list {(f x1 y1) (f x2 y2) (f x3 y3) ...}" {
    set result {}
    foreach x $xs y $ys {
        if { !([null_p $x] || [null_p $y]) } {
            lappend result [$f $x $y]
        }
    }
    return $result
}

# Example:
# % set first_names {Sandra Catherine Nicole}
# % set last_names  {Bullock Zeta-Jones Kidman}
# % zip_with [lambda {f l} {return "$f $l"}] $first_names $last_names
# "Sandra Bullock" "Catherine Zeta-Jones" "Nicole Kidman"


d_proc -public transpose {lists} "tranposes a matrix (a list of lists)" {
    set num_lists [llength $lists]
    if {!$num_lists} { return "" }
    for {set i 0} {$i<$num_lists} {incr i} {
        set l($i) [lindex $lists $i]
    }
    set result {}
    while {1} {
        set element {}
        for {set i 0} {$i<$num_lists} {incr i} {
            if {[null_p $l($i)]} { return $result }
            lappend element [head $l($i)]
            set l($i) [tail $l($i)]
        }
        lappend result $element
    }

    # Note: This function takes about n*n seconds
    #       to transpose a (100*n) x (100*n) matrix.
    #       Pretty fast, don't you think? :)
}


# --------------------------------------------------------------------------------
# Other Functions (that maybe are too weird for the ACS)
# --------------------------------------------------------------------------------

ad_proc -public iterate {n f x} {
    Returns {x (f x) (f (f x) (f (f (f x))) ...}.
    <h4>Examples</h4>
    <ul>
    <li><code>iterate 10 [lambda {x} {expr $x+1}] 5 = {5 6 7 8 9 10 11 12 13 14}</code>
    <li><code>iterate 10 [lambda {x} {expr $x*2}] 1 = {1 2 4 8 16 32 64 128 256 512}</code>
    <li><code>iterate 4 tail {1 2 3 4 5} = {1 2 3 4 5} {2 3 4 5} {3 4 5} {4 5}</code>
    </ul>
} {
    set result {}
    for {set i 0} {$i<$n} {incr i} {
        lappend result $x
        set x [$f $x]
    }
    return $result
}

d_proc -public unzip {xs} "unzip takes a list of tuples {x1 y1} {x2 y2} {x3 y3} ... and
                     returns a tuple of lists {x1 x2 x3 ...} {y1 y2 y3 ...}." {
    set left {}
    set right {}
    foreach x $xs {
        # assertion: x is a tuple
        lappend left [lindex $x 0]
        lappend right [lindex $x 1]
    }
    return [list $left $right]
}

# "unzip" is just a special case of the function "transpose"
# and is here just for completeness.

# --------------------------------------------------------------------------------
# List breaking functions: To gain a real advantage from using these functions,
# you would actually need a language that has "lazy evaluation" (like Haskell).
# In Tcl they can be useful too, but they are not as powerful.
#
#   split_at n xs    = (take n xs, drop n xs)
#
#   take_while p xs  returns the longest initial segment of xs whose
#                    elements satisfy p
#   drop_while p xs  returns the remaining portion of the list
#   span p xs        = (takeWhile p xs, dropWhile p xs)
#
#   take_until p xs  returns the list of elements up to and including the
#                    first element of xs which satisfies p
#
# --------------------------------------------------------------------------------

d_proc -public split_at {n xs} "splits a list using take and drop" {
    list [take $n $xs] [drop $n $xs]
}

d_proc -public take_while {p xs} "returns the longest initial segment of xs whose
                            elements satisfy p" {
    set index 0
    foreach x $xs {
        if { ![$p $x] } { break }
        incr index
    }
    take $index $xs
}

d_proc -public drop_while {p xs} "returns the remaining portion of the list" {
    set index 0
    foreach x $xs {
        if { ![$p $x] } { break }
        incr index
    }
    drop $index $xs
}

d_proc -public span {p xs} "splits a list using take_while and drop_while" {
    list [take_while $p $xs] [drop_while $p $xs]
}

d_proc -public take_until {p xs} "returns the list of elements up to and including the
                            first element of xs which satisfies p" {
    set index 0
    foreach x $xs {
        incr index
        if { [$p $x] } { break }
    }
    take $index $xs
}

# --------------------------------------------------------------------------------
# Tests and Experiments
# --------------------------------------------------------------------------------

ad_proc -public factorial {n} {
    compute n!
} {
    product [enum_from_to 1 $n]
}

d_proc -public mul {n fraction} "multiplies n with a fraction (given as a tuple)" {
    set num [fst $fraction]
    set denom [snd $fraction]
    set g [gcd $n $denom]
    expr {($n/$g)*$num/($denom/$g)}
}

ad_proc -deprecated choose {n k} {
    Here's how to compute 'n choose k' like a real nerd.

    DEPRECATED: As of tcl8.5, Tcl has native support for 'lambda' provided by
                means of 'apply' per TIP 194. Tcllib provides a 'lambda' package
                with procs that make use of it.

    @see https://www.tcl-lang.org/man/tcl/TclCmd/apply.htm
} {
    fold mul 1 [transpose [list [iterate $k [bind flip - 1] $n] [enum_from_to 1 $k]]]
}

ad_proc -deprecated pascal {size} {
    prints Pascal's triangle

    DEPRECATED: As of tcl8.5, Tcl has native support for 'lambda' provided by
                means of 'apply' per TIP 194. Tcllib provides a 'lambda' package
                with procs that make use of it.

    @see https://www.tcl-lang.org/man/tcl/TclCmd/apply.htm
} {
    for {set n 0} {$n<=$size} {incr n} {
        puts [map [bind choose $n] [enum_from_to 0 $n]]
    }
}

ad_proc -public prime_p {n} {
    @return 1 if n is prime
} {
    if { $n<2 } { return 0 }
    if { $n==2 } { return 1 }
    if { [even_p $n] } { return 0 }
    for {set i 3} {$i*$i<=$n} {incr i 2} {
        if { $n%$i==0 } { return 0 }
    }
    return 1
}

# % filter prime_p [enum_from_to 1 100]
# 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97

proc multiplication_table {x} {
    # This is an extreme example for test purposes only.
    # This way of programming is not recommended. Kids: do not try this at home.
    flip join \n [map [bind compose [bind flip join ""] [bind map [bind compose \
        [lambda {s} {format %4d $s}] product]]] \
        [map transpose [transpose [list [map [bind copy $x] [enum_from_to 1 $x]] \
        [copy $x [enum_from_to 1 $x]]]]]]
}

# --------------------------------------------------------------------------------
# Literature about functional programming on the web
# --------------------------------------------------------------------------------

# http://www.haskell.org/aboutHaskell.html
# http://www.md.chalmers.se/~rjmh/Papers/whyfp.html

namespace export *

}

# Local variables:
#    mode: tcl
#    tcl-indent-level: 4
#    indent-tabs-mode: nil
# End: