func

Richard Suchenwirth 2004-05-09 - Functions in Tcl are typically written with the proc command. But I notice more and more that, on my way to functional programming, my proc bodies are a single call to expr which does all the rest (often with the powerful x?y:z operator). So what about a thin abstraction (wrapper) around this recurring pattern?

 proc func {name argl body} {proc $name $argl [list expr $body]}

(Might have called it fun as well... it sure is.) That's all. A collateral advantage is that all expressions are braced, without me having to care. But to not make the page look so empty, here's some examples for func uses:

 func fac n     {$n<2? 1: $n*[fac [incr n -1]]}
 func gcd {u v} {$u? [gcd [expr $v%$u] $u]: $v}
 func min {a b} {$a<$b? $a: $b}
 func sgn x     {($x>0)-($x<0)} ;# courtesy rmax

Pity we have to make expr explicit again, in nested calls like in gcd... But func isn't limited to math functions (which, especially when recursive, come out nice), but for expr uses in testing predicates as well:

 func atomar list          {[lindex $list 0] eq $list}
 func empty  list          {[llength $list] == 0}
 func in    {list element} {[lsearch -exact $list $element] >= 0}
 func limit {x min max}    {$x<$min? $min: $x>$max? $max: $x}
 func ladd  {list e}       {[in $list $e]? $list: [lappend list $e]}

Exposing expr operators as Tcl commands goes quite easy too:

 foreach op {+ * / %} {func $op {a b} "\$a $op \$b"}

For "-", we distinguish unary and binary form:

 func - {a {b ""}} {$b eq ""? -$a: $a-$b}

Having the modulo operator exposed, gcd now looks nicer (from Playing with rationals):

 func gcd {u v} {$u? [gcd [% $v $u] $u]: abs($v)}

For unary not I prefer that name to "!", as it might also stand for factorial - and see the shortest function body I ever wrote :^) :

 func not x {!$x}

It could have been shortened by one byte, though :^)

 func not x !\$x

Feel free to add more meaningful examples! In any case, no one can blame me for not using return in these sweet one-liners... :)

Without big mention, functions implemented by recursion have a pattern for which func is well suited (see fac and gcd above). Another example is this integer range generator (starts from 1, and is inclusive, so [iota1 5] == {1 2 3 4 5}):

 func iota1 n {$n == 1? 1: [concat [iota1 [- $n 1]] $n]}

AM (1 june 2004) I have been musing over another, related subject:

  • Define functional relations between variables (possibly defining an equation)
  • Use high-level commands to actually solve the equation

This seems an excellent way of introducing some physics into the Wiki ;)

This is the page I wrote to work out the above ideas: An equation solver

WHD: Would this be a first step toward constraint programming? (A topic that intrigues me, but as yet I know almost nothing about it.)


RS 2006-01-25: If you have many parameters as global variables, you can extend func to import all of them. This runs the risk of side-effects, but here you go:

 proc func {name argl body} { 
    proc $name $argl "eval global \[info globals\]; expr {$body}" 
 }

Testing:

 % set a 6
 6
 % func f x {$x * $a}
 % f 7
 42

Arts and crafts of Tcl-Tk programming