2023-02-17 08:52:21 +08:00
|
|
|
|
[#]: subject: "Learn Tcl by writing a simple game"
|
|
|
|
|
[#]: via: "https://opensource.com/article/23/2/learn-tcl-writing-simple-game"
|
|
|
|
|
[#]: author: "James Farrell https://opensource.com/users/jamesf"
|
|
|
|
|
[#]: collector: "lkxed"
|
|
|
|
|
[#]: translator: "geekpi"
|
2023-02-23 14:01:06 +08:00
|
|
|
|
[#]: reviewer: "wxy"
|
|
|
|
|
[#]: publisher: "wxy"
|
|
|
|
|
[#]: url: "https://linux.cn/article-15567-1.html"
|
2023-02-17 08:52:21 +08:00
|
|
|
|
|
2023-02-23 14:01:06 +08:00
|
|
|
|
通过“猜数字”游戏学习 Tcl
|
2023-02-17 08:52:21 +08:00
|
|
|
|
======
|
|
|
|
|
|
2023-02-23 14:01:06 +08:00
|
|
|
|
![][0]
|
|
|
|
|
|
|
|
|
|
> 探索 Tcl 的基本语言结构,包括用户输入、输出、变量、条件评估、循环和简单函数。
|
|
|
|
|
|
|
|
|
|
我的 Tcl 之旅始于最近需要将一个困难的基于 Java 的命令行配置工具自动化。我使用 Ansible 做了一些自动化编程,偶尔也会使用 `expect` 模块。坦率地说,我发现这个模块的作用有限,原因包括:难以对相同的提示进行排序,难以捕捉到额外步骤的值,控制逻辑的灵活性有限,等等。有时你可以用 `shell` 模块来代替。但有时你会遇到那种特立独行、过于复杂的命令行程序,似乎无法实现自动化。
|
2023-02-17 08:52:21 +08:00
|
|
|
|
|
|
|
|
|
就我而言,我正在自动安装我公司的一个程序。最后的配置步骤只能通过命令行来完成,通过几个不规范的、重复的提示和需要捕捉的数据输出。好在传统的 Expect 是唯一的答案。要使用 Expect 的基本功能,并不需要对 Tcl 有很深的了解,但你了解的越多,你就能从它那里得到更多的力量。这是后续文章的话题。现在,我探讨一下 Tcl 的基本语言结构,包括用户输入、输出、变量、条件判断、循环和简单函数。
|
|
|
|
|
|
|
|
|
|
### 安装 Tcl
|
|
|
|
|
|
|
|
|
|
在 Linux 系统上,我使用这个:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
# dnf install tcl
|
|
|
|
|
# which tclsh
|
|
|
|
|
/bin/tclsh
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
在 macOS 上,你可以使用 [Homebrew][1] 来安装最新的 Tcl:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
$ brew install tcl-tk
|
|
|
|
|
$ which tclsh
|
|
|
|
|
/usr/local/bin/tclsh
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 在 Tcl 中猜数字
|
|
|
|
|
|
|
|
|
|
从创建基本的可执行脚本 `numgame.tcl` 开始:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
$ touch numgame.tcl
|
|
|
|
|
$ chmod 755 numgame.tcl
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
接着在你的文件中开始编码,标题是通常的 #!:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
#!/usr/bin/tclsh
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
这里有一些关于 Tcl 的简单介绍,以便与本文一起追踪。
|
|
|
|
|
|
2023-02-23 14:01:06 +08:00
|
|
|
|
第一点是,Tcl 处理的都是字符串。变量通常被当作字符串处理,但可以自动切换类型和内部表示(这一点你通常无法看到)。函数可以把它们的字符串参数解释为数字(`expr`),并且只通过值传递。字符串通常使用双引号或大括号来划分。双引号允许变量扩展和转义序列,而大括号则完全没有扩展。
|
2023-02-17 08:52:21 +08:00
|
|
|
|
|
2023-02-23 14:01:06 +08:00
|
|
|
|
第二点是 Tcl 语句可以用分号隔开,但通常不这样。语句行可以用反斜杠字符来分割,然而,典型的做法是将多行语句放在大括号内,以避免需要这样做。大括号只是更简单,下面的代码格式也反映了这一点。大括号允许对字符串进行延迟求值。在 Tcl 进行变量替换之前,值被传递给函数。
|
2023-02-17 08:52:21 +08:00
|
|
|
|
|
|
|
|
|
最后,Tcl 使用方括号进行命令替换。方括号之间的任何东西都会被送到 Tcl 解释器的一个新的递归调用中进行求值。这对于在表达式中间调用函数或为函数生成参数是很方便的。
|
|
|
|
|
|
|
|
|
|
### 过程
|
|
|
|
|
|
|
|
|
|
虽然在这个游戏中没有必要,但我先举一个在 Tcl 中定义函数的例子,你可以在以后使用:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
proc used_time {start} {
|
|
|
|
|
return [expr [clock seconds] - $start]
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
使用 `proc` 将其设定为一个函数(或过程)定义。接下来是函数的名称。然后是一个包含参数的列表;在本例中是一个参数 `{start}` ,然后是函数主体。注意,主体的大括号在这一行开始,它不能在下面一行。该函数返回一个值。返回值是一个复合求值(方括号),它从读取系统时钟 `[clock seconds]` 开始,并进行数学运算以减去 `$start` 参数。
|
|
|
|
|
|
|
|
|
|
### 设置、逻辑和完成
|
|
|
|
|
|
|
|
|
|
你可以在这个游戏的其余部分增加更多的细节,进行一些初始设置,对玩家的猜测进行迭代,然后在完成后打印结果:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
set num [expr round(rand()*100)]
|
|
|
|
|
set starttime [clock seconds]
|
|
|
|
|
set guess -1
|
|
|
|
|
set count 0
|
|
|
|
|
|
|
|
|
|
puts "Guess a number between 1 and 100"
|
|
|
|
|
|
|
|
|
|
while { $guess != $num } {
|
|
|
|
|
incr count
|
|
|
|
|
puts -nonewline "==> "
|
|
|
|
|
flush stdout
|
|
|
|
|
gets stdin guess
|
|
|
|
|
|
|
|
|
|
if { $guess < $num } {
|
|
|
|
|
puts "Too small, try again"
|
|
|
|
|
} elseif { $guess > $num } {
|
|
|
|
|
puts "Too large, try again"
|
|
|
|
|
} else {
|
|
|
|
|
puts "That's right!"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set used [used_time $starttime]
|
|
|
|
|
|
|
|
|
|
puts "You guessed value $num after $count tries and $used elapsed seconds"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
前面的 `set` 语句建立变量。前两个求值表达式用于识别 1 到 100 之间的随机数,下一个保存系统时钟启动时间。
|
|
|
|
|
|
|
|
|
|
`puts` 和 `gets` 命令用于来自玩家的输出和输入。我使用的 `puts` 暗示输出是标准输出。`gets` 需要定义输入通道,所以这段代码指定 `stdin` 作为用户的终端输入源。
|
|
|
|
|
|
|
|
|
|
当 `puts` 省略行末终止符时,需要 `flush stdout` 命令,因为 Tcl 缓冲了输出,在需要下一个 I/O 之前可能不会被显示。
|
|
|
|
|
|
|
|
|
|
从这里开始,`while` 语句说明了循环控制结构和条件逻辑,需要给玩家反馈并最终结束循环。
|
|
|
|
|
|
|
|
|
|
最后的 `set` 命令调用我们的函数来计算游戏的耗时秒数,接着是收集到的统计数字来结束游戏。
|
|
|
|
|
|
|
|
|
|
### 玩吧!
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
$ ./numgame.tcl
|
|
|
|
|
Guess a number between 1 and 100
|
|
|
|
|
==> 100
|
|
|
|
|
Too large, try again
|
|
|
|
|
==> 50
|
|
|
|
|
Too large, try again
|
|
|
|
|
==> 25
|
|
|
|
|
Too large, try again
|
|
|
|
|
==> 12
|
|
|
|
|
Too large, try again
|
|
|
|
|
==> 6
|
|
|
|
|
Too large, try again
|
|
|
|
|
==> 3
|
|
|
|
|
That's right!
|
|
|
|
|
You guessed value 3 after 6 tries and 20 elapsed seconds
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 继续学习
|
|
|
|
|
|
2023-02-23 14:01:06 +08:00
|
|
|
|
当我开始这个练习时,我怀疑回到 90 年代末的流行语言对我有多大的帮助。一路走来,我发现 Tcl 有几处让我非常喜欢的地方,我最喜欢的是方括号内的命令求值。与其他许多过度使用复杂闭包结构的语言相比,它似乎更容易阅读和使用。我以为它是一种 [已消亡的语言][2],但实际上它仍在蓬勃发展,并在多个平台上得到支持。我学到了一些新的技能,并对这种古老的语言有了新的认识。
|
2023-02-17 08:52:21 +08:00
|
|
|
|
|
|
|
|
|
在 [https://www.tcl-lang.org][3] 上查看官方网站。你可以找到最新的源代码、二进制发行版、论坛、文档,以及仍在进行的会议信息的参考。
|
|
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
via: https://opensource.com/article/23/2/learn-tcl-writing-simple-game
|
|
|
|
|
|
|
|
|
|
作者:[James Farrell][a]
|
|
|
|
|
选题:[lkxed][b]
|
|
|
|
|
译者:[geekpi](https://github.com/geekpi)
|
2023-02-23 14:01:06 +08:00
|
|
|
|
校对:[wxy](https://github.com/wxy)
|
2023-02-17 08:52:21 +08:00
|
|
|
|
|
|
|
|
|
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
|
|
|
|
|
|
|
|
|
[a]: https://opensource.com/users/jamesf
|
|
|
|
|
[b]: https://github.com/lkxed/
|
|
|
|
|
[1]: https://opensource.com/article/20/6/homebrew-mac
|
|
|
|
|
[2]: https://opensource.com/article/19/6/favorite-dead-language
|
|
|
|
|
[3]: https://www.tcl-lang.org
|
2023-02-23 14:01:06 +08:00
|
|
|
|
[0]: https://img.linux.net.cn/data/attachment/album/202302/23/140000ilg3oa8khgrhr1r3.jpg
|