为什么在调用“set”命令时,Tcl变量名前不使用美元符号?

8

我这里只是拿Perl作为比较:

$foo = 5;
print $foo;

将变量$foo设置为5,然后打印该变量的内容(请注意,$foo始终作为$foo访问)。

在Tcl中:

set foo 5
puts $foo

和Perl的对应物做的事情是一样的。

为什么Tcl不用"$"来设置变量,而是需要"$"来访问变量呢? 为什么过程也是这样的(例如proc bar {spam eggs} {...})? 对我来说,Tcl代码看起来像这样(伪代码):

"foo" = 5 # Setting a string?
puts $foo # "$foo" is not defined.

我只是表达了我所看到的情况,而不是实际发生的情况。

我想再补充一点,那就是这个问题的明确性:

set foo foo

是的,我可以始终执行set foo "foo",但是set $foo foo更加一致吗?

据我所知,“foo”可以是变量或字符串,具体取决于情况,就像我上一个例子中看到的(set foo foo = set var string),但是我不理解这种语法(可能是因为我习惯了Python...)

4个回答

14

我认为最初的Tcl仅有set命令,因此获取变量"foo"的内容的唯一方法是调用set foo。但随着Tcl逐渐成为通用脚本语言(记住Tcl被设想为一种可嵌入语言,你可以在C中使用薄层Tcl覆盖复杂组件,因此人们不会使用大量的变量),$varname这个语法糖是有用的,于是被添加了进去。

换句话说,Tcl并不像Perl那样使用"$"来表示"解释后面的内容作为标量","$"在Tcl中也不代表一个变量,而只是一个语法糖,表示"给我一个由紧跟其后的字词所指定名称的变量的值"。


5
这是正确的答案。$ 的引入只是为了简化对 [set] 特定用法的表示。 - Eric Melski
1
我认为基本的$语法是在Tcl 2中添加的;changes文件并没有记录那么久远的历史(尽管我可以说数组是6.0版本的新增功能,而set在3.0之前被称为var),所以我只能凭记忆操作。 - Donal Fellows

13

我决定评论一下原问题的最后部分,但是评论超出限制了,所以我将其作为答案发布,即使它没有回答指定的问题。希望它能澄清一些在问题中隐含的事情。

关于 set foo foo 的一致性说明:你对 Tcl 的处理方式有点不正确。Tcl 与 Perl 或 Python 根本不同,它几乎没有语法(就像 Lisp 一样)。"set foo foo" 不是设置变量的语法,就像在其他语言中一样,它是调用一个命令当前使用名称 "set" 并传递两个参数 - "foo" 和 "foo";由注册在“set”命令名称下的内容决定变量名称和要将其设置为什么值。同样,循环命令也不是语法,而是命令。实际上,值得停下来思考。

for {set i 0} {$i < 10} {incr i} {
  puts $i
}
Tcl使用花括号而非C中的普通括号并非仅仅是一个异想天开的语言设计师的想法。在Tcl解析器中,它会解析出五个单词,将第一个单词视为命令名称进行查找,并传递其余的单词;正是for的实现解释了这些{set i 0}{$i < 0}等内容并相应地执行它们。这样的语法只是为了说明如何从字符输入流中解析出这些单词。由于所有东西都是用命令实现的,你的命令能够与内置命令完全一样,使你能够创建像forswitch一样强大的自定义命令(类似LISP中的宏)。在其他语言中,这相当于以任意方式扩展语法的能力。

当你理解这个思想后,Tcl将不再显得奇怪。这就是使得Tcl非常易于扩展(和嵌入)的特性:因为你从C端提供的命令与所有内置命令都表现得完全相同。


6
有趣的事实:set set set 是合法和有意义的;它只是为 set 字面量提供了三个不同的解释。第一个是命令名称,第二个是变量名称,第三个是(可能是)普通字符串。 - Donal Fellows

4
$foo视为变量foo,即它上次被设置的文本。现在,如果foo当前具有值x,那么set $foo y就意味着set x y,这显然不是预期的结果。这种方式比其他语言更一致,因为其他语言中foo有时表示变量foo的值,有时表示变量foo本身。

2
如果有的话,真正的答案只能由原语言设计者(John Ousterhout)给出。所以其他的只能是猜测或者(有根据的)猜测。关于TCL的历史可以在这里找到一些资料here,但快速阅读后发现没有直接的答案。
为什么Tcl不用“$”设置变量,但需要“$”来访问变量?
我认为这样更接近(UNIX)shell语言。 TCL诞生于伯克利,可以说是一个强大的UNIX(或BSD)环境。
UNIX shell也不使用$符号(或相应shell的等效符号)来声明或赋值变量,但在引用变量时需要它:
# bourn shell like shells (sh, bash, ksh, ...)
foo=foo
echo "The value of the variable foo is $foo."

即使是不受祝福的Windows CMD.EXE处理器也使用了类似的方法(尽管我猜想TCL设计师们并没有考虑到这一点)。
REM DOS COMMAND.COM / Windows CMD.EXE
set foo=foo
echo The value of the variable is %foo%.

此外,即使shell是弱类型语言,"字符串值"通常不需要引号,如果字符串值中没有空格。
“是的,我可以始终运行set foo” foo”,但是set $foo foo更一致吗?”
嗯,这并不是TCL设计者/创造者的初衷 ;-)
编辑我几乎忘了:您实际上可以在TCL中执行以下操作:
set foo bar
set $foo something
puts $bar

这实际上会输出“something”。第二行将字符串“something”设置为变量“foo”的值,从而设置一个名为“bar”的变量并将其赋值为“something”。

这是一个有趣的技巧,但我不能确切地说我会使用它。事实上,set foo foo; set $foo bar; puts $foo 打印出 "bar",更让我感到困惑了,因为它表明变量本质上就是字符串。我猜我只是习惯了 Python 的 setattrgettatr。不过,他们为什么要以这种方式设计它,是否有任何逻辑上的原因呢? - D K
2
当然,那个例子没有实际价值。然而,这个特性本身非常重要且相当有用。你可能想查看 upvar 命令以获取更好的使用示例。 - Christian.K
2
变量不是字符串。变量名称是字符串。在Tcl中,您必须区分名称和实际的内容。 (这一般会在多年内给哲学家带来很多麻烦;计算只是他们洞察力的一个非常实用的应用。) - Donal Fellows
上面的例子在Python中更加令人困惑。想象一下以下的Python代码y = 'bar'; x = y; y = 'foo'; print x,然后再看看y=['bar']; x = y; y[0]('foo'); print x。这两者之间的区别在于Python具有引用和可变值,而Tcl的值是不可变的(就像Python的字符串)。 - schlenk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接