1.4 改进第一个程序

我们应该对上述程序的功能稍作改进。因为它现在只能向 Julia 打招呼,不论执行它的人是谁。我们需要让它根据执行人给定的参数值来自定义它打招呼的对象。顺便说一句,我会把这一程序的改进版本放在 src/ch01/args 路径下。

首先,我们要改变一下调用println函数时传给它的那个参数。修改后的调用表达式如下:

  1. println("Hey, $(name)!")

我只改动了几个字符,即:把Julia改成了$(name)。后者代表了一个插值(interpolation)。对于插值来说,前缀$(和后缀)之间的内容可以是一个变量的名称,也可以是一个表达式。在这里,我放入的是变量name的名字。在println函数向目的地输送内容之前,它会把name替换成该变量在那一刻时的值。

当然,变量name现在还不存在。我们还需要在这行代码的前面添加一些东西。

  1. name, _ = MyArgs.get_parameter("name", true)
  2. if name == ""
  3. name = "handsome"
  4. end

上面这 4 行代码的主要功能是定义name变量,并为它赋予合适的值。第一行中的MyArgs代表了一个模块。而表达式MyArgs.get_parameter("name", true)则代表了对这个模块下的函数get_parameter的一次调用。在调用时,我们传给了它两个参数值,即:String类型(也称字符串类型)的值"name"Bool类型(或者说布尔类型)的值true

这里的模块代表的是一块程序。原则上讲,这块程序可以包含任意行的代码。我一会儿再说怎么定义一个模块。现在你只需要知道,在我们调用MyArgs.get_parameter函数后,它会返回两个结果值。在同一行中,我把这两个结果值分别赋给了变量name_

符号=代表了“赋值”这个动作。这意味着,它右边的表达式所产生的结果值会被赋给左边的变量。注意,右边的结果值的数量和左边的变量的数量必须相同。在这里,它们的数量都是两个。

你可能会问:_是什么?它实际上是一个占位符。你可以把它想象成一个垃圾桶。当我们不再需要某个值的时候,就可以把它扔进(赋给)这个垃圾桶。这么做一般有两个原因。其一,让 Julia 的垃圾收集器及时地回收这个不再被使用的值所占用的内存空间。其二,保持程序的完整性和可读性。换句话说,即使一个值或者一个变量不再有用了,我们也要进行妥善的处理。

所谓的垃圾收集器是指,用于自动地收集和清扫内存垃圾的内置程序。大多数现代编程语言都会提供垃圾收集器。简单来说,当用户程序中的一些代码以及它们产生的数据不再有用的时候,垃圾收集器就会识别它们,并把它们所占用的内存空间收回,以便重复利用或者还给操作系统。

再说回我们的程序。我们在第一行代码中调用了MyArgs.get_parameter函数,并用它返回的第一个结果值为name变量赋了值。紧随其后的,是一个由if语句构成的代码块。

  1. if name == ""
  2. name = "handsome"
  3. end

if语句用于有条件地执行代码。它以关键字if作为起始,并以关键字end作为末尾。在if右边的就是一个条件,即:name == ""。这是一个判等表达式。它判断的是变量name的值是否与空字符串""相等。

在我们的设计中,如果我们在执行这个程序的时候没有通过参数指定一个问候对象的名字,那么这里的name变量的值就会是""。因此,这个if语句代表的逻辑就是,如果我们没有指定问候对象的名字,那么就把name变量的值设置成"handsome"

记住,在if关键字和条件代码的下一行就是满足条件时需要执行的代码。当然,这样的代码可以有多行。但不管怎样,这些代码都需要被缩进。也就是说,它们要比包含了if关键字的那一行代码更靠右一些。一般情况下,我们需要在这些代码的左边插入固定数量(比如 4 个)的空格或者一个制表位。

还要注意,关键字end与关键字if是左对齐的。并且end是需要独占一行的。这个关键字在 Julia 程序中是非常常用的。它通常作为一个代码块的结束标识。比如,我们在写模块、函数、if语句、for语句、try...catch语句等等代码块时都会用到它。

再来说MyArgs模块。我在改进版本的main.jl的开始处定义了这个模块。代码如下:

  1. module MyArgs
  2. include("args.jl")
  3. end

它由关键字module、模块名称MyArgs、关键字end,以及被夹在中间的代码组成。注意,在模块的定义中,首尾两行之间的那些代码可以不缩进。这主要是考虑到整个文件的内容只包含一个(或寥寥几个)模块定义的情况。在这种情况下,如果中间的代码都缩进,那么就等于几乎所有的代码都要缩进了。这个工作量往往是没必要的,而且这样的代码也是不美观的。

在我们的MyArgs模块的中间只有一行代码。它也是一个表达式,代表了对include函数的调用。这个include函数接受一个字符串类型的参数,而且这个参数的值需要代表一个 Julia 源码文件的路径名。简单来说,它会把参数值指定的源码文件的内容复制、黏贴到调用它的代码那里。也可以说,它会用我们指定的源码替换掉调用它的那行代码。

我们在这里传给include函数的参数值是"args.jl"。这个源码文件与改进版本的main.jl处在同一个目录下,即:src/ch01/args。我在该文件中定义了一个名叫get_parameter的函数。这个函数的定义细节我就不讲了,你可以自己去看。其中只包含了寥寥几行代码,用于变量定义、函数调用、条件求值等。

那里有一些语法我们还没有讲,所以你看不懂也没关系。但是,我希望你在看过之后记录下自己有疑问的地方,并且带着这些疑问阅读本教程的后续部分。

现在让我们执行一下改进版本的main.jl文件吧:

  1. $ julia --startup-file=no main.jl --name=Robert
  2. Hey, Robert!

我在这次执行julia命令的时候给了它三个参数。这些参数之间是由空格分隔开的。第一个参数是我们已经熟知的--startup-file。而第二个参数是我要执行的源码文件。在它之后就是我为源码文件提供的参数了。

在第三个参数中,我以--为前缀。这主要是为了遵从julia命令的参数形式。紧跟在--之后的就是参数名name、等于号=和参数值Robert。因此,程序回显的内容就是Hey, Robert!。这是正确的。你可以改变一下第三个参数的值,然后看看效果。