Ruby 重温前面的例子

03:49下午 三月 18, 2008 in category Ruby语言 by Ruby-红宝石

返回前面的简单示例

现在让我们看看前面例子程序的部分代码。

下面的代码出现在前面的示例章节。

def fact(n)
  if n == 0
    1
  else
    n * fact(n-1)
  end
end
puts fact(ARGV[0].to_i)

因为这是第一次解释,我们将分别解释每一行。

Factorials(阶乘)

def fact(n)

在第一行, def 是定义一个函数的声明(或者说是一个方法; 在后面的某一章中我们将详细介绍方法)。这里,它声明了带一个参数的函数,参数是 n。

if n == 0

if用来检查一个条件。如果条件符合,下一段代码会被执行,否则 else后面的代码会被执行。

1

n为1时的值。

else

如果不为1,执行下面的代码。

n * fact(n-1)

如果条件不满足,值就是 n 乘 fact(n-1)。

end

第一个end是if条件结束。

end

第二个end是def声明的结束。

puts fact(ARGV[0].to_i)

这个调用我们的fact函数使用一个从命令行指定的值,然后打印结果。

ARGV 是一个包含命令行参数的数组。ARGV 数组的元素都是字符串,因此我们必须使用to_i把它转化成一个整数。Ruby不会像perl那样自动转化字符串成整数。

如果给这个函数传递一个负数会怎么样呢?你能看出问题吗?你能够修正这个问题吗?

字符串

下面我们将检查有关字符串中令人迷惑的问题。这里可能有点儿长,我标注了函数供参考。

01 words = ['foobar', 'baz', 'quux']
02 secret = words[rand(3)]
03
04 print "guess? "
05 while guess = STDIN.gets
06   guess.chop!
07   if guess == secret
08     puts "You win!"
09     break
10   else
11     puts "Sorry, you lose."
12   end
13   print "guess? "
14 end
15 puts "the word is ", secret, "."

在这个程序里,有个新的控制结构 while 。 在while和它对应的end之间的代码会被重复执行,直到指定的条件得到满足才退出。在这里,guess=STDIN.gets是个主动的声明 (搜集用户输入的一行字符串存储它作为程序的输入), 和一个条件(如果没有输入, guess, 它表示整个 guess=STDIN.gets 表达式的值, 如果值为 nil 循环将会推出)。

STDIN 是一个标准的输入对象。通常 guess=gets 和 guess=STDIN.gets 是一样的。

在第二行的rand(3)返回一个在0到2之间的随机数。这个随机数用来抽取数组中的一个单词。

在第5行,我们通过STDIN.gets方法从标准输入读入一行。如果 EOF (文件结束符) 在行中出现, gets返回nil。因此相关代码会重复执行直到出现 ^D (在DOS/Windows请尝试 ^Z 或者 F6), 表示输入结束。

在第六行的 guess.chop! 会去掉最后的一个字符;在这个例子中它是一个"新行"符,gets包括了反映用户的"换车"符,但是我们不感兴趣。

在15行我们输出在secret变量中的结果。我们已经写的puts (put string) 声明有两个参数,它们会被依次打印输出,但是也有使用一个参数的等效方式,把变量写成 #{secret}对于变量的计算更清晰,而不是一个可打印的单词:

puts "the word is #{secret}."

很多程序设计者感觉这样表达输出是一种很清晰的方法;它构建了一个单一的字符串和把它表达成一个单一参数传递给puts。

同样,我们现在有了使用puts作为标准的脚本输出的概念,但是这个脚本可以使用print替代,在行14和13。它们并不完全一样。 print 正确地输出给定的内容;puts也确认输出的行尾。在行14和13使用的print把光标停留在最后输出的地方,而不是移动到下一行的行首。这个为用户输入创建了一个可认识的提示。通常,下面的四个输出调用是相等的:

# 如果还没有新行,新行通过puts隐含地添加:
puts  "Darwin's wife, Esmerelda, died in a fit of penguins."

# 新行必须在print命令中明确地给出:
print "Darwin's wife, Esmerelda, died in a fit of penguins.\n"

# 你可以使用 + 连接一个新行:
print 'Darwin's wife, Esmerelda, died in a fit of penguins.'+"\n"

# 或者通过提供多于一个的字符串来连接:
print 'Darwin's wife, Esmerelda, died in a fit of penguins.', "\n"

One possible gotcha : sometimes a text window is programmed to buffer output for the sake of speed, collecting individual characters and displaying them only when it is given a newline character. So if the guessing game script misbehaves by not showing the prompt lines until after the user supplies a guess, buffering is the likely culprit. To make sure this doesn't happen, you can flush the output as soon as you have printed the prompt. It tells the standard output device (an object named STDOUT), "don't wait; display what you have in your buffer right now."

04 print "guess? "; STDOUT.flush
  ...
13 print "guess? "; STDOUT.flush

And in fact, we were more careful with this in the next script.
Regular expressions

Finally we examine this program from the chapter on regular expressions.

01 st = "\033[7m"
02 en = "\033[m"
03
04 puts "Enter an empty string at any time to exit."
05
06 while true
07   print "str> "; STDOUT.flush; str=gets.chop
08   break if str.empty?
09   print "pat> "; STDOUT.flush; pat=gets.chop
10   break if pat.empty?
11   re = Regexp.new(pat)
12   puts str.gsub(re, "#{st}\\&#{en}")
13 end

In line 6, the condition for while is hardwired to true, so it forms what looks like an infinite loop. However we put break statements in the 8th and 10th lines to escape the loop. These two breaks are also an example of "if modifiers." An if modifier executes the statement on its left hand side if and only if the specified condition is satisfied. This construction is unusual in that it operates logically from right to left, but it is provided because for many people it mimics a similar pattern in natural speech. It also has the advantage of brevity, as it needs no end statement to tell the interpreter how much of the following code is supposed to be conditional. An if modifier is conventionally used in situations where a statement and condition are short enough to fit comfortably together on one script line.

Note the difference in the user interface compared to the string-guessing script. This one lets the user quit by hitting the Return key on an empty line. We testing for emptiness of the input string, not for its nonexistence.

In lines 7 and 9 we have a "non-destructive" chop; again, we're getting rid of the unwanted newline character we always get from gets. Add the explanation point, and we have a "destructive" chop. What's the difference? In ruby, we conventionally attach '!' or '?' to the end of certain method names. The exclamation point (!, sometimes pronounced aloud as "bang!") indicates something potentially destructive, that is to say, something that can change the value of what it touches. chop! affects a string directly, but chop gives you a chopped copy without damaging the original. Here is an illustration of the difference.

ruby> s1 = "forth"
  "forth"
ruby> s1.chop!       # This changes s1.
  "fort"
ruby> s2 = s1.chop   # This puts a changed copy in s2,
  "for"
ruby> s1             # ... without disturbing s1.
  "fort"

You'll also sometimes see chomp and chomp! used. These are more selective: the end of a string gets bit off only if it happens to be a newline. So for example, "XYZ".chomp! does nothing. If you need a trick to remember the difference, think of a person or animal tasting something before deciding to take a bite, as opposed to an axe chopping indiscriminately.

The other method naming convention appears in lines 8 and 10. A question mark (?, sometimes pronounced aloud as "huh?") indicates a "predicate" method, one that can return either true or false.

Line 11 creates a regular expression object out of the string supplied by the user. The real work is finally done in line 12, which uses gsub to globally substitute each match of that expression with itself, but surrounded by ansi markups; also the same line outputs the results.

We could have broken up line 12 into separate lines like this:

highlighted = str.gsub(re,"#{st}\\&#{en}")
puts highlighted

or in "destructive" style:

str.gsub!(re,"#{st}\\&#{en}")
puts str

Look again at the last part of line 12. st and en were defined in lines 1-2 as the ANSI sequences that make text color-inverted and normal, respectively. In line 12 they are enclosed in #{} to ensure that they are actually interpreted as such (and we do not see the variable names printed instead). Between these we see \\&. This is a little tricky. Since the replacement string is in double quotes, the pair of backslashes will be interpreted as a single backslash; what gsub actually sees will be \&, and that happens to be a special code that refers to whatever matched the pattern in the first place. So the new string, when displayed, looks just like the old one, except that the parts that matched the given pattern are highlighted in inverse video.

评论[0]

评论:

发表一条评论:
  • HTML语法: 启用