---
author: James Farrell
category: 软件开发
comments_data: []
count:
  commentnum: 0
  favtimes: 0
  likes: 0
  sharetimes: 0
  viewnum: 1604
date: '2023-02-23 14:00:03'
editorchoice: false
excerpt: 探索 Tcl 的基本语言结构，包括用户输入、输出、变量、条件评估、循环和简单函数。
fromurl: https://opensource.com/article/23/2/learn-tcl-writing-simple-game
id: 15567
islctt: true
largepic: /images/p/202302/23/140000ilg3oa8khgrhr1r3.jpg
permalink: /article-15567-1.html
pic: /images/p/202302/23/140000ilg3oa8khgrhr1r3.jpg.thumb.jpg
related: []
reviewer: wxy
selector: lkxed
summary: 探索 Tcl 的基本语言结构，包括用户输入、输出、变量、条件评估、循环和简单函数。
tags:
- Tcl
thumb: false
title: 通过“猜数字”游戏学习 Tcl
titlepic: true
translator: geekpi
updated: '2023-02-23 14:00:03'
---

![](/images/p/202302/23/140000ilg3oa8khgrhr1r3.jpg)



> 
> 探索 Tcl 的基本语言结构，包括用户输入、输出、变量、条件评估、循环和简单函数。
> 
> 
> 


我的 Tcl 之旅始于最近需要将一个困难的基于 Java 的命令行配置工具自动化。我使用 Ansible 做了一些自动化编程，偶尔也会使用 `expect` 模块。坦率地说，我发现这个模块的作用有限，原因包括：难以对相同的提示进行排序，难以捕捉到额外步骤的值，控制逻辑的灵活性有限，等等。有时你可以用 `shell` 模块来代替。但有时你会遇到那种特立独行、过于复杂的命令行程序，似乎无法实现自动化。


就我而言，我正在自动安装我公司的一个程序。最后的配置步骤只能通过命令行来完成，通过几个不规范的、重复的提示和需要捕捉的数据输出。好在传统的 Expect 是唯一的答案。要使用 Expect 的基本功能，并不需要对 Tcl 有很深的了解，但你了解的越多，你就能从它那里得到更多的力量。这是后续文章的话题。现在，我探讨一下 Tcl 的基本语言结构，包括用户输入、输出、变量、条件判断、循环和简单函数。


### 安装 Tcl


在 Linux 系统上，我使用这个：



```
# dnf install tcl
# which tclsh
/bin/tclsh

```

在 macOS 上，你可以使用 [Homebrew](https://opensource.com/article/20/6/homebrew-mac) 来安装最新的 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 的简单介绍，以便与本文一起追踪。


第一点是，Tcl 处理的都是字符串。变量通常被当作字符串处理，但可以自动切换类型和内部表示（这一点你通常无法看到）。函数可以把它们的字符串参数解释为数字（`expr`），并且只通过值传递。字符串通常使用双引号或大括号来划分。双引号允许变量扩展和转义序列，而大括号则完全没有扩展。


第二点是 Tcl 语句可以用分号隔开，但通常不这样。语句行可以用反斜杠字符来分割，然而，典型的做法是将多行语句放在大括号内，以避免需要这样做。大括号只是更简单，下面的代码格式也反映了这一点。大括号允许对字符串进行延迟求值。在 Tcl 进行变量替换之前，值被传递给函数。


最后，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

```

### 继续学习


当我开始这个练习时，我怀疑回到 90 年代末的流行语言对我有多大的帮助。一路走来，我发现 Tcl 有几处让我非常喜欢的地方，我最喜欢的是方括号内的命令求值。与其他许多过度使用复杂闭包结构的语言相比，它似乎更容易阅读和使用。我以为它是一种 [已消亡的语言](https://opensource.com/article/19/6/favorite-dead-language)，但实际上它仍在蓬勃发展，并在多个平台上得到支持。我学到了一些新的技能，并对这种古老的语言有了新的认识。


在 <https://www.tcl-lang.org> 上查看官方网站。你可以找到最新的源代码、二进制发行版、论坛、文档，以及仍在进行的会议信息的参考。




---


via: <https://opensource.com/article/23/2/learn-tcl-writing-simple-game>


作者：[James Farrell](https://opensource.com/users/jamesf) 选题：[lkxed](https://github.com/lkxed/) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)


本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出