From: Fredrik Karlsson on
Dear group,

First of all, apologies to the members of #tcl - I posted this
yesterday and got some answers, but not the whole story, so people may
have seen this before.

I would like to construct a list of lists (possibly containing lists..
and so on), and evaluate them as a command call.
So, in this example:

% set a [list puts [list string range "hejsan" 0 2]]
puts {string range hejsan 0 2}
% eval $a
string range hejsan 0 2

I would instead like to get just "hej".

How do I do this?
And, why does the example above not work? Please enlighten me..

/Fredrik
From: Arjen Markus on
On 6 jul, 15:29, Fredrik Karlsson <dargo...(a)gmail.com> wrote:
> Dear group,
>
> First of all, apologies to the members of #tcl - I posted this
> yesterday and got some answers, but not the whole story, so people may
> have seen this before.
>
> I would like to construct a list of lists (possibly containing lists..
> and so on), and evaluate them as a command call.
> So, in this example:
>
> % set a [list puts [list string range "hejsan" 0 2]]
> puts {string range hejsan 0 2}
> % eval $a
> string range hejsan 0 2
>
> I would instead like to get just "hej".
>
> How do I do this?
> And, why does the example above not work? Please enlighten me..
>
> /Fredrik

The command that would do that is:

puts [string range hejsan 0 2]

- so [] instead of {}.

So, if I understand you correctly, then each first element of
a list is a command, all remaining elements are also commands
in themselves?

In that case, something like:

proc recursive_eval {list} {
if { [llength $list] <= 1 } {
return $list
} else {
set arguments [recursive_eval [lrange $list 1 end]]
return [eval [lindex $list 0] $arguments]
}
}

(Note: untested)

Regards,

Arjen
From: Aric Bills on
On Jul 6, 7:29 am, Fredrik Karlsson <dargo...(a)gmail.com> wrote:
> Dear group,
>
> First of all, apologies to the members of #tcl - I posted this
> yesterday and got some answers, but not the whole story, so people may
> have seen this before.
>
> I would like to construct a list of lists (possibly containing lists..
> and so on), and evaluate them as a command call.
> So, in this example:
>
> % set a [list puts [list string range "hejsan" 0 2]]
> puts {string range hejsan 0 2}
> % eval $a
> string range hejsan 0 2
>
> I would instead like to get just "hej".
>
> How do I do this?
> And, why does the example above not work? Please enlighten me..
>
> /Fredrik

You want $a to contain the script

puts [string range hejsan 0 2]

but the way you have constructed it, it contains the script

puts {string range hejsan 0 2}

which behaves very differently. As you can read about in the "Tcl"
man page (http://www.tcl.tk/man/tcl8.5/TclCmd/Tcl.htm), square
brackets trigger command substitution, while curly braces prevent
substitution (note that some commands [for example, if, foreach,
switch] evaluate some of their arguments as Tcl code; that's different
from command substitution).

If you want to use [eval] on the string you construct, you need a way
to construct it so that it contains square brackets where you want
command substitution to occur. You could create a convenience command
that would return a string surrounded by square brackets:

proc bracket {args} {
return "\[$args\]"
}

Then, you could construct your command with

set a [list puts [bracket string range hejsan 0 2]]
From: Donal K. Fellows on
On 6 July, 15:15, Aric Bills <aric.bi...(a)gmail.com> wrote:
> If you want to use [eval] on the string you construct, you need a way
> to construct it so that it contains square brackets where you want
> command substitution to occur.  You could create a convenience command
> that would return a string surrounded by square brackets:
>
>     proc bracket {args} {
>         return "\[$args\]"
>     }
>     set a [list puts [bracket string range hejsan 0 2]]

That doesn't work, since it creates a value like:

puts {[string range hejsan 0 2]}

If you're going this way, you need to just do this:

set a "puts [bracket string range hejsan 0 2]"

But really if you're getting into this, you need to use a different
way to construct your code that avoids such substitution complexities!
For example, you use a helper procedure like this:

proc printRange {string from to} {
puts [string range $string $from $to]
}
set a [list printRange hejsan 0 2]

With 8.5, you can use a lambda application instead (this cheats a
little and uses args/{*} to shorten the code):

set a [list apply {args {puts [string range {*}$args]}} hejsan 0
2]

If the *real* code is getting one or more of those values at runtime,
put the reading of it inside the helper/lambda. If not, you can
actually store the script with the pre-substituted argument:

set a [list puts [string range hejsan 0 2]]

I guess from the fact that you're not doing this that you're needing
to do some of this at the time of the [eval $a]...

Donal.
From: Aric Bills on
On Jul 6, 3:20 pm, "Donal K. Fellows"
<donal.k.fell...(a)manchester.ac.uk> wrote:
> On 6 July, 15:15, Aric Bills <aric.bi...(a)gmail.com> wrote:
>
> > If you want to use [eval] on the string you construct, you need a way
> > to construct it so that it contains square brackets where you want
> > command substitution to occur.  You could create a convenience command
> > that would return a string surrounded by square brackets:
>
> >     proc bracket {args} {
> >         return "\[$args\]"
> >     }
> >     set a [list puts [bracket string range hejsan 0 2]]
>
> That doesn't work, since it creates a value like:
>
>     puts {[string range hejsan 0 2]}

Oops. Good point.