通过编写“猜数字”游戏学习 awk

编程语言往往有很多共同的特性。学习一门新语言的一个好方法是创建一个熟悉的程序。在本文中,我将使用 awk 创建一个“猜数字”游戏,以演示熟悉的概念。
72 位读者喜欢这个。
question mark in chalk

TeroVesalainen via Pixabay CC0

当您学习一门新的编程语言时,最好专注于大多数编程语言的共同点

  • 变量 – 存储信息的地方
  • 表达式 – 计算事物的方法
  • 语句 – 在程序中表达状态变化的方式

这些概念是大多数编程语言的基础。

一旦您理解了这些概念,您就可以开始弄清楚其余部分。例如,大多数语言都有其设计支持的“做事方式”,而这些方式可能因程序而异。这些方式包括模块化(将相关功能组合在一起)、声明式与命令式、面向对象、低级与高级语法特性等等。许多程序员熟悉的一个例子是“仪式”,即在解决问题之前设置场景所需的工作量。据说 Java 编程语言具有重要的仪式要求,这源于其设计,该设计要求所有代码都必须在类中定义。

但回到基础知识。编程语言通常有相似之处。一旦您了解了一种编程语言,就可以从学习另一种语言的基础知识开始,以欣赏新语言的差异。

一个好的方法是创建一组基本的测试程序。有了这些程序,学习就从这些相似之处开始。

您可以使用的一个测试程序是“猜数字”程序。计算机选择一个介于 1 到 100 之间的数字,并要求您猜这个数字。程序循环直到您猜对为止。

“猜数字”程序练习了编程语言中的几个概念

  • 变量
  • 输入
  • 输出
  • 条件评估
  • 循环

这是一个学习新编程语言的绝佳实践实验。

注意:本文改编自 Moshe Zadka 关于在 Julia 中使用这种方法的文章和 Jim Hall 关于在 Bash 中使用这种方法的文章。

用 awk 猜数字

让我们编写一个“猜数字”游戏作为 Awk 程序。

Awk 是动态类型的,是一种面向数据转换的脚本语言,并且对交互式使用具有出乎意料的良好支持。Awk 自 1970 年代以来就存在了,最初是 Unix 操作系统的一部分。如果您不了解 Awk 但喜欢电子表格,这是一个信号……去学习 Awk

您可以从编写一个“猜数字”游戏的版本开始探索 Awk。

这是我的实现(带有行号,以便我们可以回顾一些具体功能)

     1    BEGIN {
     2        srand(42)
     3        randomNumber = int(rand() * 100) + 1
     4        print "random number is",randomNumber
     5        printf "guess a number between 1 and 100\n"
     6    }
     7    {
     8        guess = int($0)
     9        if (guess < randomNumber) {
    10            printf "too low, try again:"
    11        } else if (guess > randomNumber) {
    12            printf "too high, try again:"
    13        } else {
    14            printf "that's right\n"
    15            exit
    16        }
    17    }

我们可以立即看到 Awk 控制结构与 C 或 Java 的控制结构之间的相似之处,但与 Python 不同。在诸如if-then-elsewhile 之类的语句中,thenelsewhile 部分采用语句或用 {} 括起来的语句组。但是,关于 AWk 有一个很大的区别需要从一开始就理解

按照设计,Awk 是围绕数据管道构建的。

那是什么意思?大多数 Awk 程序都是代码片段,这些代码片段接收一行输入,对数据执行某些操作,然后将其写入输出。认识到对这种转换管道的需求,Awk 默认情况下提供了所有转换管道。让我们通过询问一个基本问题来探索上述程序:'从控制台读取数据'结构在哪里?

答案是 – 它是内置的。特别是,第 7 – 17 行告诉 Awk 如何处理读取的每一行。鉴于这种情况,很容易看出第 1 – 6 行在读取任何内容之前执行。

更具体地说,第 1 行的 BEGIN 关键字是一种“模式”,在这种情况下,它指示 Awk 在读取任何数据之前,应执行 BEGIN 后面的 { … } 中的内容。类似的 END 关键字(本程序中未使用)指示 Awk 在读取完所有内容后应执行的操作。

回到第 7 – 17 行,我们看到它们创建了一个代码块 { … },该代码块类似,但前面没有关键字。因为 { 前面没有任何 Awk 可以匹配的内容,所以它会将此行应用于接收到的每一行输入。每行输入都将作为用户猜测输入。

让我们看一下正在执行的代码。首先是在读取任何输入之前发生的前言。

在第 2 行中,我们使用数字 42 初始化随机数生成器(如果我们不提供参数,则使用系统时钟)。42?当然是 42。第 3 行计算一个介于 1 和 100 之间的随机数,第 4 行出于调试目的打印该数字。第 5 行邀请用户猜一个数字。请注意,此行使用 printf,而不是 print。与 C 类似,printf 的第一个参数是用于格式化输出的模板。

既然用户知道程序期望输入,她可以在控制台上键入一个猜测。如前所述,Awk 将此猜测提供给第 7 – 17 行中的代码。第 18 行将输入记录转换为整数;$0 表示整个输入记录,而 $1 表示输入记录的第一个字段,$2 表示第二个字段,依此类推。是的,Awk 使用预定义的分隔符(默认为空格)将输入行拆分为组成字段。第 9 – 15 行将猜测与随机数进行比较,打印适当的响应。如果猜测正确,则第 15 行过早地退出输入行处理管道。

简单!

鉴于 Awk 程序的非常规结构,即代码片段对特定的输入行配置做出反应并使用数据执行操作,让我们看一下另一种结构,只是为了了解过滤部分是如何工作的

     1    BEGIN {
     2        srand(42)
     3        randomNumber = int(rand() * 100) + 1
     4        print "random number is",randomNumber
     5        printf "guess a number between 1 and 100\n"
     6    }
     7    int($0) < randomNumber {
     8        printf "too low, try again: "
     9    }
    10    int($0) > randomNumber {
    11        printf "too high, try again: "
    12    }
    13    int($0) == randomNumber {
    14        printf "that's right\n"
    15        exit
    16    }

第 1 – 6 行没有改变。但是现在我们看到第 7 – 9 行是当行的整数值小于随机数时执行的代码,第 10 – 12 行是当行的整数值大于随机数时执行的代码,第 13 – 16 行是当两者匹配时执行的代码。

这应该看起来“酷但怪异” – 例如,为什么我们要重复计算 int($0)?当然,这将是解决问题的一种奇怪方式。但是,这些模式可能是分离条件处理的非常好的方法,因为它们可以采用正则表达式或 Awk 支持的任何其他结构。

为了完整起见,我们可以使用这些模式将通用计算与仅适用于特定情况的事物分开。这是第三个版本来说明

     1    BEGIN {
     2        srand(42)
     3        randomNumber = int(rand() * 100) + 1
     4        print "random number is",randomNumber
     5        printf "guess a number between 1 and 100\n"
     6    }
     7    {
     8        guess = int($0)
     9    }
    10    guess < randomNumber {
    11        printf "too low, try again: "
    12    }
    13    guess > randomNumber {
    14        printf "too high, try again: "
    15    }
    16    guess == randomNumber {
    17        printf "that's right\n"
    18        exit
    19    }

认识到无论输入什么值,都需要将其转换为整数,我们创建了第 7 – 9 行来执行此操作。现在,三组行,10 – 12、13 – 15 和 16 – 19,引用了已定义的变量 guess,而不是每次都转换输入行。

让我们回到我们想要学习的事物列表

  • 变量 – 是的,Awk 有这些;我们可以推断输入数据以字符串形式出现,但可以在需要时转换为数值
  • 输入 – Awk 只是通过其“数据转换管道”方法发送输入以读取内容
  • 输出 – 我们使用了 Awk 的 printprintf 过程来将内容写入输出
  • 条件评估 – 我们了解了 Awk 的 if-then-else 和响应特定输入行配置的输入过滤器
  • 循环 – 哼,想象一下!我们在这里不需要循环,再次感谢 Awk 采用的“数据转换管道”方法;循环“自然发生”。请注意,用户可以通过向 Awk 发送文件结束信号(在使用 Linux 终端窗口时为 CTRL-D)来过早退出管道

非常值得考虑不需要循环来处理输入的重要性。Awk 长期以来一直可行的原因之一是 Awk 程序很紧凑,它们紧凑的原因之一是无需样板代码即可从控制台或文件读取。

让我们运行程序

$ awk -f guess.awk
random number is 25
guess a number between 1 and 100: 50
too high, try again: 30
too high, try again: 10
too low, try again: 25
that's right
$

我们没有涵盖的一件事是注释。Awk 注释以 # 开头,以行尾结尾。

总结

Awk 非常强大,这个“猜数字”游戏是入门的好方法。但这不应该是您旅程的终点。您可以阅读有关 Awk 和 Gawk(GNU Awk)的历史,Gawk 是 Awk 的扩展版本,如果您运行的是 Linux,则可能是您计算机上的版本,或者 阅读有关其初始开发人员的原始版本的所有信息

您还可以下载我们的速查表,以帮助您跟踪您学到的一切。

接下来阅读什么
标签
Chris Hermansen portrait Temuco Chile
自从 1978 年毕业于不列颠哥伦比亚大学以来,我就一直使用各种计算机,自 2005 年以来一直是全职 Linux 用户,1986 年至 2005 年一直是全职 Solaris 和 SunOS 用户,在此之前是 UNIX System V 用户。

4 条评论

当我研究某种编程语言时,我不是通过创建游戏来学习的。知道如何输入信息和打印出一些结果是好的,但在实践层面,我通常需要能够打开一些文件,从中获取信息,然后在其中找到一些东西,也许编辑它,然后保存原始文件或一些新文件。
通过搜索您可以在某处找到的小程序(无论是在书本上还是在网上),然后修改它们以了解是什么破坏了它们,然后了解如何修复它们,您可以节省大量时间。失败是您的朋友,只要您坚持到某个积极的结果。(嗯,这几乎像一首诗的片段)

没错,当学习一种新的工具/编程语言/技术时,失败是你最好的朋友。了解事物为什么不起作用以及如何修复它们是最有价值的教训。

回复 作者 Greg P

随着世界的发展,
很多事情变得复杂。
而那些能够简化它们的人,往往会成功。
准备好经历数千次的失败才能获得一次成功。

无论如何,感谢 Chris Hermansen 的完美解释。

知识共享许可协议本作品根据知识共享署名-相同方式共享 4.0 国际许可协议获得许可。
© . All rights reserved.