你在大多数编程语言中都会看到相同的基本概念,例如:
- 变量:存储信息的地方
- 表达式:计算事物的方式
- 语句:程序中状态变化的表达方式
一旦你理解了这些概念,你就可以开始弄清楚一种语言与其他语言的不同之处。例如,大多数语言都有一种由其设计支持的“做事方式”,而这些方式可能彼此大相径庭。这些包括模块化(将相关功能组合在一起)、声明式与命令式、面向对象、低级与高级语法特性等等。
许多程序员熟悉的一个例子是“仪式”,即在解决问题之前设置场景所需的工作量。 据说 Java 编程语言的设计要求所有代码都必须在类中定义,因此具有重要的仪式要求。
但回到基础。编程语言通常具有上述类似的相似之处,因此一旦你掌握了一种语言,你就可以开始学习另一种语言的基础知识,以欣赏它的差异。 扩展你所掌握的语言的一种方法是使用一个基本的测试程序来开始学习。 这就是我们在本系列文章中所做的事情,使用“猜数字”程序,其中计算机选择一个介于 1 和 100 之间的数字,并要求你猜测它。 该程序循环,直到你猜出正确的答案。
“猜数字”程序练习了编程语言中的几个概念
- 变量
- 输入
- 输出
- 条件评估
- 循环
这也是学习一门新编程语言的一个很棒的实践实验。
用 Groovy 猜数字
Groovy 是动态类型的,感觉像一种脚本语言,并且对交互式使用有很好的支持。 它旨在利用 Java 虚拟机 (JVM) 生态系统,并且与 Java 具有很高的源代码兼容性,以及其精简和高级语法特性。
这是我在 Groovy 中实现的“猜数字”游戏(带有行号,以便我可以查看一些特定功能)
1 int randomNumber = (new Random()).nextInt(100) + 1
2 println "the secret number is $randomNumber"
3 print 'Guess a number: '
4 System.in.withReader { reader ->
5 String response
6 while (response = reader.readLine()) {
7 if (! response.isNumber())
8 print "that's not a number! try again: "
9 else {
10 int guess = response as Integer
11 if (guess < randomNumber)
12 print 'too low, try again: '
13 else if (guess > randomNumber)
14 print 'too high, try again: '
15 else {
16 println "that's right"
17 break
18 }
19 }
20 }
21 }
对于那些熟悉 Java 或 C 的人来说,语法会显得非常熟悉(例如,与 Python 不同)。 在 Groovy 中,if-then-else
或 while
等语句,then
、else
和 while
部分采用语句或包含在 {
和 }
中的一组语句。 Java 程序员会注意到 Groovy 为脚本编写提供精简的支持——无需将程序包装在类定义中。
Groovy 是面向对象的,并且可以访问 Java 标准库。 在我的代码的第 1 行中,为了生成一个随机数,它创建了 Random 类的新实例,并使用参数 100 调用它的 nextInt() 方法,这将给出一个介于 0 和 99 之间的数字; 因此我们在这个结果上加 1。
还有一件事需要注意:Groovy 不需要使用分号来终止行,当它可以从换行的存在中确定语句的结尾时。
第二个语句打印出生成的数字,用于调试目的。 值得在此提及的是 Groovy 为隐藏 Java 仪式所做的积极努力; 在 Java 中,这将写成:
System.out.println("The secret number is " + randomNumber);
Groovy 编译器消除了在 println
前面加上 System.out
的需要。 只要可以清楚地识别参数,Groovy 还使方法调用中的括号成为可选的(但是不带参数的方法仍然需要在方法名称后面加上 ()
)。
另一个很酷的 Groovy 功能,在第 2 行中可见,是 GString
的使用。 请注意字符串中 randomNumber
前面的 $
,这会导致变量 randomNumber
的值插入到字符串中。 可以使用 ${ }
插入表达式。
在第 3 行中,Groovy 允许使用单引号而不是双引号来划定 String
文本。 GString
需要双引号。 这一行打印出运行脚本的人的提示。
我希望能够从控制台(通常是终端窗口)读取输入。 Java 定义了 System
静态类和 in
静态字段,作为获取用户在控制台中键入的内容的一种方式。 语句 4 在 System.in
字段上调用 withReader
方法来设置和拆除面向行的字符串读取器——这是 Groovy 简化的另一个例子。 withReader
将闭包作为其参数——一种“匿名代码块”,类似于 Java 中的 lambda。 当 withReader
调用提供的闭包时,它将读取器作为参数传递给闭包。 相应的参数在闭包中使用片段 reader ->
定义。
这个闭包业务是 Groovy 中一个非常有趣的功能。 虽然它类似于 lambda,但它没有 lambda 的限制,例如只能引用封闭范围内的 final 或等效于 final 的变量,因此无需过多担心 randomNumber
在闭包外部的定义。
从第 4 行的 { reader ->
开始到第 21 行的 }
结束的闭包是处理用户输入的代码块。
在该闭包中,代码看起来大多很熟悉。 第 5 行声明了 String
变量 response
(它是闭包的局部变量)。 第 6 行启动一个 while
循环,只要用户继续猜测,该循环就会迭代,如果该行为空或用户发出文件结束信号(例如,在 Linux 终端窗口中使用 Ctrl+D),该循环将终止。 这个 while
循环以第 20 行的 }
结束。
请注意,while
循环测试的表达式是:
response = reader.readLine()
Groovy 对“真”有扩展的定义。 产生 true
的表达式为真,但产生非零数值、非空字符串值或非空值的表达式也为真。 如果用户按回车键,则此表达式将产生一个空字符串,如果用户键入 Ctrl+D,则此表达式将产生一个 null
,System.in
会将其转换为 null
。
第 7 行和第 8 行检查用户是否输入了一个可以转换为数字的 String
值,如果不是,则会报错。 Groovy String
类(即所有 Strings)的实例都有一个名为 isNumber()
的方法,可用于这种测试。 将此用法与典型的 Java 方法(使用 try-catch
块)进行比较。
第 9 行到第 19 行处理用户输入的值。 第 10 行使用 Groovy 的 as
关键字将输入的 String
值转换为 Integer
,它可以处理许多强制转换和转换。 代码的其余部分测试转换后的整数值是太低、太高还是恰好。 再次注意单引号与双引号的用法,特别是在第 19 行的 println
调用中,由于字符串中有单引号,因此使用双引号来分隔 String
文本。
示例输出
在演示我之前概述的概念方面,它的效果如何?
- 变量:是的,Groovy 有这些;请参阅
int
变量和String
变量。 - 输入:我使用了
System.in
作为预定义的控制台输入,并使用读取器从中获取值。 - 输出:我使用 Groovy 的
println
(Java 的System.out.println
)在控制台上打印消息。 - 条件评估:我探索了 Groovy 的
if-then-else
以及 Groovy 的真值。 - 循环:我在 Groovy 的
while
循环中使用了这些。
我还使用了一些 Java 标准库功能,例如 String
实例上的 isNumeric()
方法和 System.in
上的 withReader()
方法。 我使用了一个闭包,有点像 Java lambda。
运行程序来玩游戏
$ groovy guess.groovy
the secret number is 29
Guess a number: 50
too high, try again: 25
too low, try again: 37
too high, try again: 31
too high, try again: 28
too low, try again: 29
that's right
$
我觉得我应该提一下,玩这个游戏的最佳方法是使用二分查找策略:在你的脑海中计算最近的“太高”和最近的“太低”的平均值,并将其用作你的下一个猜测。
我没有介绍的一件事是注释。 像 Java 一样,Groovy 注释可以以 //
开头并运行到行尾,或者可以包装在 /* */
中。 我可以在调试 println
前面加上一个 //
来注释掉它,使其更难作弊。
了解更多
如果您想学习 Groovy,请从官方 Groovy 网站开始。 我发现 Hubert A. Klein Ikkink 对 Groovy、Grails 和 Java 的思考非常值得。 还有许多关于 Groovy 的优秀书籍; 即使它们有些过时,仍然值得一读。 我喜欢 Venkat Subramaniam 的 Programming Groovy 2:Java 开发人员的动态生产力。
评论已关闭。