Tcl: write lambda and map using tcl 8.6 - tcl

How do I write and apply a simple lambda function using the tcl 8.6 features "apply" and "lmap"?
map (lambda x -> x*x) [list 1 2 3]
how can I write the above in Tcl 8.6? The man pages are not that self explanatory for me.
Perhaps also a more advance version, but I guess I can figure that out myself:
lambda y -> map (lambda x -> x*x) y
Basically I would like to improve this version:
proc \x {f val} {
set res [apply $f $val]
set res
}
set res [\x {x {expr $x*$x}} 5]
puts "res: $res"
So that I can just write:
set res [\x {expr $x*$x} 5]
puts "res: $res"

Here's what lambda looks like:
proc lambda {arguments expression} {
list ::apply [list $arguments [list expr $expression]]
}
Then we do this, noting that {*} is required because the inner lambda term can't be a command directly without causing other trouble that we didn't want to have in 8.5 (or 8.6):
set res [lmap x [list 1 2 3] {
{*}[lambda x {$x * $x}] $x
}]
The 8.6 lmap is syntactically like foreach, so an extra layer of application is needed. It is, however, more easily understood by average Tcl programmers for being like that.
Note that the lambdas are fully first-class values that can be passed around however you want (put in variables, returned, stored in a list, whatever):
set square [lambda x {$x * $x}]
puts "the square of 42 is [{*}$square 42]"
(You could use λ for a command name if you wanted, but I find it awkward to type on this keyboard. I don't recommend using \x though; Tcl uses backslashes for various escaping duties.)

Related

How to read the variable Name which store the value

TCL Program Sample:
proc fun { x } {
puts "$$x = $x"
}
set a 10
fun $a
In this above program which prints the output as $10 = 10 But i would like to get a = 10 has the output. The variable which passes the values has to be read and the corresponding values as well. Is there a way to read the variable name.
proc fun name {
upvar 1 $name var
puts "$name = $var"
}
set a 10
fun a
The upvar command takes a name and creates an alias of a variable with that name.
Documentation:
proc,
puts,
set,
upvar
If you've got a currently-supported version of Tcl (8.5 or 8.6), you can use info frame -1 to find out some information about the caller. That has all sorts of information in it, but we can do a reasonable approximation like this:
proc fun { x } {
set call [dict get [info frame -1] cmd]
puts "[lindex $call 1] = $x"
}
set a 10
fun $a
# ==> $a = 10
fun $a$a
# ==> $a$a = 1010
Now, the use of lindex there is strictly wrong; it's Tcl code, not a list (and you'll see the difference if you use a complex command substitution). But if you're only ever using a fairly simple word, it works well enough.
% set x a
a
% set a 10
10
% eval puts $x=$$x
a=10
% puts "$x = [subst $$x]"
a = 10
% puts "$x = [set $x]"
a = 10
%
If you are passing the variable to a procedure, then you should rely on upvar.

Is it possible to pass a function with multiple fixed arguments to ::math::optimize::min_bound_1d in Tcl

I'm fairly new to Tcl and I'm stuck on an assignment/project I have to do.
So I have a function F(x, A1, A2,...) = y. x is a free variable and A1, A2 etc. are some constants that will be known in advance. I have to find x for when y is a minimum for multiple values of A1, A2 etc.
I came across the function ::math::optimize::min_bound_1d which looks exactly like what I need (there are some constraints on the function). The problem is that this optimize function only seems to accept functions with 1 argument.
So in my sudo code below I have something like:
proc F {x arg1 arg1} {
# Crunch numbers
return $result
}
foreach arg1 $list_of_first_args {
foreach arg2 $list_of_second_args {
# Is it possible to pass $arg1 and $arg2 to the function below?
puts [::math_optimize_min_bound_1d F $lower_limit $upper_limit]
}
}
Is it possible in Tcl to pass/bound $arg1 and $arg2 to the function F in the example above so that I can get x when y is a minimum for each permutation of argN? Can I put all the arguments in a list and pass that?
Thanks!
Yes, provided you rearrange things a bit.
F needs to look like this, with the x variable placed last:
proc F {arg1 arg2 x} {
# Crunch numbers
return $result
}
(Or you can use a proxy command to rearrange argument order:)
proc F' {arg1 arg2 x} {
tailcall F $x $arg1 $arg2
}
Then, inside the foreach structures:
puts [::math::optimize::min_bound_1d [list F $arg1 $arg2] $lower_limit $upper_limit]
The [list F $arg1 $arg2] thingy is a kind of a poor man's lambda that quite a few higher-order function commands in Tcl accept. If, at evaluation, $arg1 is 1 and $arg2 is 12, the "function" {F 1 12} is passed into ::math::optimize::min_bound_1d and evaluated as [concat {F 1 12} $x].
A "real" lambda could look like this:
[list x [concat F \$x $arg1 $arg2]]
but this construct can only be used if the higher-order function command is prepared to evaluate it using apply $func $x.
Documentation: apply, join, list, math::optimize package, package, proc, puts, return, tailcall
Assuming that the code in question doesn't try to do funky things like parsing the source of the function to optimise, the easiest way is to make a proxy function:
proc proxy {value} {
global A1 A2
F $value $A1 $A2
}
Then just optimize the proxy.
Though you're going to have to deal with the fact that optimizing in higher dimensions is much more complex than in the single-dimension case.

How to pass arguments to tcl scripts when using tclsh [duplicate]

This is the code in TCL that is meant to produce factorial of a number given as parameter by the user.
if {$argc !=1}{
puts stderr "Error! ns called with wrong number of arguments! ($argc)"
exit 1
} else
set f [lindex $argv 0]
proc Factorial {x}{
for {set result 1} {$x>1}{set x [expr $x - 1]}{
set result [expr $result * $x]
}
return $result
}
set res [Factorial $f]
puts "Factorial of $f is $res"
There is a similar SO question, but it does not appear to directly address my problem. I have double-checked the code for syntax errors, but it does not compile successfully in Cygwin via tclsh producing the error:
$ tclsh ext1-1.tcl
extra characters after close-brace
while executing
"if {$argc !=1}{
puts stderr "Error! ns called with wrong number of arguments! ($argc)"
exit 1
} else
set f [lindex $argv 0]
proc Factorial {x}{..."
(file "ext1-1.tcl" line 3)
TCL Code from: NS Simulator for Beginners, Sophia-Antipolis, 2003-2004
Tcl is a little bit more sensitive about whitespace than most languages (though not as much as, say, Python). For instance, you can't add unescaped newlines except between commands as command separators. Another set of rules are that 1) every command must be written in the same manner as a proper list (where the elements are separated by whitespace) and 2) a command invocation must have exactly the number of arguments that the command definition has specified.
Since the invocation must look like a proper list, code like
... {$x>1}{incr x -1} ...
won't work: a list element that starts with an open brace must end with a matching close brace, and there can't be any text immediately following the close brace that matches the initial open brace. (This sounds more complicated than it is, really.)
The number-of-arguments requirement means that
for {set result 1} {$x>1}{incr x -1}{
set result [expr $result * $x]
}
won't work because the for command expects four arguments (start test next body) and it's only getting two, start and a mashup of the rest of other three (and actually not even that, since the mashup is illegal).
To make this work, the arguments need to be separated:
for {set result 1} {$x>1} {incr x -1} {
set result [expr {$result * $x}]
}
Putting in spaces (or tabs, if you want) makes the arguments legal and correct in number.

How to define a variable with argument expansion

The following command runs as expected:
lappend {*}{arr 1}
puts [lindex $arr 0]
Now I am trying to make a variable of "{*}{arr 1}" like this:
set X "{*}{arr 1}"
lappend $X
But this does not work, seems $X is taken as one whole value, argument expansion is not effective.
So is it a requirement that argument expansion can not be through variable?
The {*} is a syntactic feature of Tcl (from Tcl 8.5 onwards) just as […], "…" or $ is. You have to write it in the script in order for it to count as argument expansion; otherwise it's just a sequence of three characters.
If you want something like
set X "{*}{arr 1}"
lappend $X
to work, you need to pass it through eval:
set X "{*}{arr 1}"
eval lappend $X
Note that this then means that X actually contains a script fragment; this can have all sort of “interesting” consequences. Try this for size:
set X "{*}{arr 1};puts hiya"
eval lappend $X
Use of eval in modern Tcl is usually a sign that you're going about stuff the wrong way; the key use in old scripts was for doing things similar to that which we'd use {*} for now.
No, within double quotes, { and } actually lose their meaning, so will {*}. Notice that puts "{}" and puts {} are different.
The closest I can think of to do what you're trying to do would be to use something like this:
set X {arr 1}
lappend {*}$X
So if you now execute puts [lindex $arr 0], you get 1 as output.

how to pass variable arguments from one function to other in tcl

I want to pass variable arguments obtained in one function to other function but I am not able to do so. Function gets even number of variable arguments and then it has to be converted in array. Below is the example.
Procedure abc1 gets two arguments (k k) and not form abc1 procedure these have to be passed to proc abc where list to array conversion it to be done. List to array conversion works in proc1 i.e. abc1 but not in second proc i.e. abc
Error obtained is given below
proc abc {args} {
puts "$args"
array set arg $args
}
proc abc1 {args} {
puts "$args"
array set arg $args
set l2 [array get arg]
abc $l2
}
abc1 k k
abc k k
Output:
k k
{k k}
list must have an even number of elements
while executing
"array set arg $l1"
(procedure "abc" line 8)
invoked from within
"abc $l2"
(procedure "abc1" line 5)
invoked from within
"abc1 k k"
(file "vfunction.tcl" line 18)
Best Solution: Expansion Substitution
The right approach is to ensure that the outer procedure (in stack terms) calls the inner one correctly; if multiple arguments are expected, that's what should be supplied. With the advent of Tcl 8.5, that's trivially done with a little language syntax called an expansion substitution:
proc abc1 {args} {
puts "$args"
array set arg $args
set l2 [array get arg]
abc {*}$l2
# Or combine the two lines above into: abc {*}[array get arg]
}
All the {*} does is say that the rest of the word should be broken up (using list syntax rules) and used as multiple arguments instead of Tcl's default “one visual word forms a single word” rules. It's ideal for this.
Old Solution: Eval Command
If you're still using old versions of Tcl for some reason (i.e., Tcl 8.4 or before) then you use the eval command instead of the above syntax:
eval abc $l2
There are some somewhat-more-efficient approaches to the above eval, which you might see in older code; for example:
eval [linsert $l2 0 abc]
eval [list abc] [lrange $l2 0 end]
# ... etc ...
But really they're all rendered obsolete by abc {*}$l2 which is shorter, simpler to write, and faster. (It's just not available in 8.4 or before, and too many deployments have yet to upgrade.) Use the expansion syntax if you can. Indeed, idiomatic Tcl code for 8.5 and later hardly ever needs eval; the extent to which this has proved true has even been rather surprising to the language's maintainers.
There's a big difference between
abc k k
and
abc [array get arg]
In the first case, you're passing two arguments, each of which is k. In the second case you're passing a list of things - in your example, a list of two k's: k k.
Nir's answer knowingly hacks around this problem, but the better solution is to write abc1 so that it calls abc properly:
proc abc1 {args} {
array set arg $args
set l2 [array get arg]
eval abc $l2
# or just
# eval abc $args
}
When you pass abc $l2 you are passing abc1's args as a single argument to abc. So in abc args holds a list with a single item ({k k}).
You can do something like this:
proc abc {args} {
#the next line assumes that if $args has only a single item, then it is
#a list in itself. It's a risky decision to make but should work here.
if { [llength $args] == 1 } { set args [lindex $args 0] }
puts "$args"
array set arg $args
}