我的笨办法学 Ruby

在学习新语言这个事情上,笨办法往往是最高效的办法。


开始之前

很早之前学习 Python 的时候就跟过『笨办法学 Python』这本教材,那么这次要学的就是 Ruby 了,在上一篇日志中我们大概对 Ruby 有了基本的认识,不过还不够系统,也远达不到掌握。所以就通过简单的例子和大量的编码练习来快速入门吧!

关键点有三个:实践、能力培养和习惯养成。这些可能要比语言的语法本身更加重要!网上目前的简体中文版本基本已经失效,所以这里我基于 Ruby 2.1 和繁体中文版本,自己把整个教程『重写』一次(其实就是术语转换和对应 ruby 版本的细微变化)

感谢前人的辛勤劳动,如果看完觉得好,可以去官方网站买一本英文版,也算是支持作者(当然也可以顺手支持我一下,文末有二维码)

习题 0 准备工作

简单来说就是安装 Ruby,具体的安装过程这里就不赘述了,这里只记下一些比较常用的小技巧:

  • 使用 ruby -v 查看本机的 Ruby 版本,我的是 ruby 2.1.4p265 (2014-10-27 revision 48166) [x86_64-darwin14.0]
  • 输入 irb 进入 ruby 环境,ctrl+z 可以退出

习题 1 第一个程序

新建一个名为 ex1.rb 的文件,内容为:

puts "Hello World!"
puts "大家好"
puts "My name is Da Wang."
puts "Visit wdxtub.com for more!"
puts 'Just visit it!'
puts "Don't hesitate."
puts 'I "said" just do it.'

然后使用 ruby ex1.rb 即可执行,结果为:

$ ruby ex1.rb
Hello World!
大家好
My name is Da Wang.
Visit wdxtub.com for more!
Just visit it!
Don't hesitate.
I "said" just do it.

这就是这一节的内容了!如果想要注释掉某一行,在开头添加 # 符号即可,简单吧!

习题 2 注释和井号

注释在上一讲也有提到,这里我们新建一个 ex2.rb,来试试看具体注释的用法:

# 这里是注释
# This is a comment
puts 'HaHaHa!'
# 这里还是注释
# This is another comment
puts 'HoHoHo!'

运行一下,结果是:

$ ruby ex2.rb
HaHaHa!
HoHoHo!

习题 3 数字和数学运算

这一次我们来练习基本的数学运算,注意整型和浮点数的区别,文件名为 ex3.rb

puts "I will now count my chickens:"
puts "Hens", 25 + 30 / 6
puts "Roosters", 100 - 25 * 3 % 4
puts "Now I will count the eggs:"
# 如果想用浮点数,需要使用 1.0,而不能用 1
puts 3 + 2 + 1 - 5 + 4 % 2 - 1.0 / 4 + 6
puts "Is it true that 3 + 2 < 5 - 7?"
puts 3 + 2 < 5 - 7
puts "What is 3 + 2?", 3 + 2
puts "What is 5 - 7?", 5 - 7
puts "Oh, that's why it's false."
puts "How about some more."
puts "Is it greater?", 5 > -2
puts "Is it greater or equal?", 5 >= -2
puts "Is it less or equal?", 5 <= -2

结果为

$ ruby ex3.rb
I will now count my chickens:
Hens
30
Roosters
97
Now I will count the eggs:
6.75
Is it true that 3 + 2 < 5 - 7?
false
What is 3 + 2?
5
What is 5 - 7?
-2
Oh, that's why it's false.
How about some more.
Is it greater?
true
Is it greater or equal?
true
Is it less or equal?
false

习题 4 变量和命名

用具体的例子介绍变量的定义,还是比较基础的,文件为 ex4.rb:

cars = 100
space_in_a_car = 4.0
drivers = 30
passengers = 90
cars_not_driven = cars - drivers
cars_driven = drivers
carpool_capacity = cars_driven * space_in_a_car
average_passengers_per_car = passengers / cars_driven
puts "There are #{cars} cars available."
puts "There are only #{drivers} drivers available."
puts "There will be #{cars_not_driven} empty cars today."
puts "We can transport #{carpool_capacity} people today."
puts "We have #{passengers} passengers to carpool today."
puts "We need to put about #{average_passengers_per_car} in each car."

注意这里使用 #{变量名} 来进行值替换,结果为

$ ruby ex4.rb
There are 100 cars available.
There are only 30 drivers available.
There will be 70 empty cars today.
We can transport 120.0 people today.
We have 90 passengers to carpool today.
We need to put about 3 in each car.

习题 5 变量与格式化输出

注意不同类型用的不同占位符号,对应文件为 ex5.rb:

my_name = 'Da Wang'
my_age = 25 # not a lie
my_height = 74 # inches
my_weight = 180 # lbs
my_eyes = 'Black'
my_teeth = 'White'
my_hair = 'Black'
puts "Let's talk about %s." % my_name
puts "He's %d inches tall." % my_height
puts "He's %d pounds heavy." % my_weight
puts "Actually that's not too heavy."
puts "He's got %s eyes and %s hair." % [my_eyes, my_hair]
puts "His teeth are usually %s depending on the coffee." % my_teeth
# this line is tricky, try to get it exactly right
puts "If I add %d, %d, and %d I get %d." % [
my_age, my_height, my_weight, my_age + my_height + my_weight]

注意如果有多个变量,需要用方括号包含起来,结果为

Let's talk about Da Wang.
He's 74 inches tall.
He's 180 pounds heavy.
Actually that's not too heavy.
He's got Black eyes and Black hair.
His teeth are usually White depending on the coffee.
If I add 25, 74, and 180 I get 279.

习题 6 字符串和文字

字符串有很多很直观的操作,比方说可以用 + 来拼接字符串,或者用 #{变量名}%s 格式化输出,我们在这里都尝试一下,文件为 ex6.rb:

x = "There are #{10} types of people."
binary = "binary"
do_not = "don't"
y = "Those who know #{binary} and those who #{do_not}."
puts x
puts y
puts "I said: #{x}."
puts "I also said: '#{y}'."
hilarious = false
joke_evaluation = "Isn't that joke so funny?! #{hilarious}"
puts joke_evaluation
w = "This is the left side of..."
e = "a string with a right side."
puts w + e

结果为

$ ruby ex6.rb
There are 10 types of people.
Those who know binary and those who don't.
I said: There are 10 types of people..
I also said: 'Those who know binary and those who don't.'.
Isn't that joke so funny?! false
This is the left side of...a string with a right side.

习题 7 更多输出

这道题是复习输出相关的知识,对应 ex7.rb:

puts "Mary had a little lamb."
puts "Its fleece was white as %s." % 'snow'
puts "And everywhere that Mary went."
puts "." * 10 # what'd that do?
end1 = "C"
end2 = "h"
end3 = "e"
end4 = "e"
end5 = "s"
end6 = "e"
end7 = "B"
end8 = "u"
end9 = "r"
end10 = "g"
end11 = "e"
end12 = "r"
# notice how we are using print instead of puts here. change it to puts
# and see what happens.
puts end1 + end2 + end3 + end4 + end5 + end6
puts end7 + end8 + end9 + end10 + end11 + end12

结果为

$ ruby ex7.rb
Mary had a little lamb.
Its fleece was white as snow.
And everywhere that Mary went.
..........
Cheese
Burger

习题 8 输出 输出

这道题还是复习输出相关的知识,对应 ex8.rb:

formatter = "%s %s %s %s"
puts formatter % [1, 2, 3, 4]
puts formatter % ["one", "two", "three", "four"]
puts formatter % [true, false, false, true]
puts formatter % [formatter, formatter, formatter, formatter]
puts formatter % [
"I had this thing.",
"That you could type up right.",
"But it didn't sing.",
"So I said goodnight."
]

结果为

$ ruby ex8.rb
1 2 3 4
one two three four
true false false true
%s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s
I had this thing. That you could type up right. But it didn't sing. So I said goodnight.

习题 9 输出 输出 再输出

这道题仍然是复习输出相关的知识,对应 ex9.rb:

# Here's some new strange stuff, remember type it exactly.
days = "Mon Tue Wed Thu Fri Sat Sun"
months = "Jan\nFeb\nMar\nApr\nMay\nJun\nJul\nAug"
puts "Here are the days: ", days
puts "Here are the months: ", months
puts <<PARAGRAPH
There's something going on here.
With the three double-quotes.
We'll be able to type as much as we like.
Even 4 lines if we want, or 5, or 6.
PARAGRAPH

这里注意最后输出一段话的语法,结果为

$ ruby ex9.rb
Here are the days:
Mon Tue Wed Thu Fri Sat Sun
Here are the months:
Jan
Feb
Mar
Apr
May
Jun
Jul
Aug
There's something going on here.
With the three double-quotes.
We'll be able to type as much as we like.
Even 4 lines if we want, or 5, or 6.

习题 10 那是什么?

上一道题中出现了两个知识点,一个是转义符号,另一个是输出段落,具体参考 ex10.rb:

tabby_cat = "\tI'm tabbed in."
persian_cat = "I'm split\non a line."
backslash_cat = "I'm \\ a \\ cat."
fat_cat = <<MY_HEREDOC
I'll do a list:
\t* Cat food
\t* Fishies
\t* Catnip\n\t* Grass
MY_HEREDOC
puts tabby_cat
puts persian_cat
puts backslash_cat
puts fat_cat

结果为

$ ruby ex10.rb
I'm tabbed in.
I'm split
on a line.
I'm \ a \ cat.
I'll do a list:
* Cat food
* Fishies
* Catnip
* Grass

习题 11 输入

这一题我们来尝试让用户输入点什么,参考 ex11.rb:

print "How old are you? "
age = gets.chomp()
print "How tall are you? "
height = gets.chomp()
print "How much do you weigh? "
weight = gets.chomp()
puts "So, you're #{age} old, #{height} tall and #{weight} heavy."

这里用 print 是为了不换行,结果为:

$ ruby ex11.rb
How old are you? 25
How tall are you? 173
How much do you weigh? 65
So, you're 25 old, 173 tall and 65 heavy.

习题 12 模块 Module

require 关键字用于包含文件(只加载一次,可以不加后缀名),include 用于包含模块,具体可以参考 ex12.rb:

require 'open-uri'
open("http://www.ruby-lang.org/en") do |f|
f.each_line {|line| p line}
puts f.base_uri # <URI::HTTP:0x40e6ef2 URL:http://www.ruby-lang.org/en/>
puts f.content_type # "text/html"
puts f.charset # "iso-8859-1"
puts f.content_encoding # []
puts f.last_modified # Thu Dec 05 02:45:02 UTC 2002
end

结果为(省略后面):

$ ruby ex12.rb
"<!DOCTYPE html>\n"
"<html>\n"
" <head>\n"
" \n"
" <title>Ruby Programming Language</title>\n"
" \n"
"\n"

注意,查找的时候不会查找相对路径,只会查找系统的文件。

习题 13 参数与解包

这次我们来看看如何处理命令行参数,参考 ex13.rb:

first, second, third = ARGV
puts "The script is called: #{$0}"
puts "Your first variable is: #{first}"
puts "Your second variable is: #{second}"
puts "Your third variable is: #{third}"

这里 $0 是脚本本身的名称,后面是具体的参数,来看具体执行的例子:

$ ruby ex13.rb first second third
The script is called: ex13.rb
Your first variable is: first
Your second variable is: second
Your third variable is: third

习题 14 提示和传递

这里我们结合前面的内容,在传入命令行参数的同时,让用户输入内容,注意,这里一定要使用 STDIN.gets,不然会把命令行参数也当做输入,具体见 ex14.rb:

user = ARGV.first
prompt = '> '
puts "Hi #{user}, I'm the #{$0} script."
puts "I'd like to ask you a few questions."
puts "Do you like me #{user}?"
print prompt
likes = STDIN.gets.chomp()
puts "Where do you live #{user}?"
print prompt
lives = STDIN.gets.chomp()
puts "What kind of computer do you have?"
print prompt
computer = STDIN.gets.chomp()
puts <<MESSAGE
Alright, so you said #{likes} about liking me.
You live in #{lives}. Not sure where that is.
And you have a #{computer} computer. Nice.
MESSAGE

具体使用的效果是:

$ ruby ex14.rb wdxtub
Hi wdxtub, I'm the ex14.rb script.
I'd like to ask you a few questions.
Do you like me wdxtub?
> Very Much
Where do you live wdxtub?
> China
What kind of computer do you have?
> Mac
Alright, so you said Very Much about liking me.
You live in China. Not sure where that is.
And you have a Mac computer. Nice.

习题 15 读取文件

这次我们来练习读取文件,我们的内容保存在 ex15_content.txt 中:

我有一只小毛驴
我从来都不骑
小邋遢,真呀真邋遢

具体代码在 ex15.rb 中,我们从命令行获取文件名:

filename = ARGV.first
prompt = "> "
txt = File.open(filename)
puts "Here's your file: #{filename}"
puts txt.read()
puts "Type the filename again:"
print prompt
file_again = STDIN.gets.chomp()
txt_again = File.open(file_again)
puts txt_again.read()

具体测试为

$ ruby ex15.rb ex15_content.txt
Here's your file: ex15_content.txt
我有一只小毛驴
我从来都不骑
小邋遢,真呀真邋遢
Type the filename again:
> ex15_content.txt
我有一只小毛驴
我从来都不骑
小邋遢,真呀真邋遢

习题 16 读写文件

基本看代码也能明白具体的操作,和 Python 还是很类似的,具体参见 ex16.rb:

filename = ARGV.first
script = $0
puts "We're going to erase #{filename}."
puts "If you don't want that, hit CTRL-C (^C)."
puts "If you do want that, hit RETURN."
print "? "
STDIN.gets
puts "Opening the file..."
target = File.open(filename, 'w')
puts "Truncating the file. Goodbye!"
target.truncate(target.size)
puts "Now I'm going to ask you for three lines."
print "line 1: "; line1 = STDIN.gets.chomp()
print "line 2: "; line2 = STDIN.gets.chomp()
print "line 3: "; line3 = STDIN.gets.chomp()
puts "I'm going to write these to the file."
target.write(line1)
target.write("\n")
target.write(line2)
target.write("\n")
target.write(line3)
target.write("\n")
puts "And finally, we close it."
target.close()

用起来是

$ ruby ex16.rb test.txt
We're going to erase test.txt.
If you don't want that, hit CTRL-C (^C).
If you do want that, hit RETURN.
?
Opening the file...
Truncating the file. Goodbye!
Now I'm going to ask you for three lines.
line 1: This is line 1
line 2: This is line 2
line 3: This is line 3
I'm going to write these to the file.
And finally, we close it.
# dawang at wdxtub.local
$ cat test.txt
This is line 1
This is line 2
This is line 3

习题 17 更多文件操作

这里我们把一个文件中的内容拷贝到另一个文件中,参见 ex17.rb:

from_file, to_file = ARGV
script = $0
puts "Copying from #{from_file} to #{to_file}"
# we could do these two on one line too, how?
input = File.open(from_file)
indata = input.read()
puts "The input file is #{indata.length} bytes long"
puts "Does the output file exist? #{File.exists? to_file}"
puts "Ready, hit RETURN to continue, CTRL-C to abort."
STDIN.gets
output = File.open(to_file, 'w')
output.write(indata)
puts "Alright, all done."
output.close()
input.close()

这里我们使用 File.exists? 来判断 to_file 是否存在,这是一个很方便的用法。具体的结果是

$ ruby ex17.rb test.txt copied.txt
Copying from test.txt to copied.txt
The input file is 45 bytes long
Does the output file exist? false
Ready, hit RETURN to continue, CTRL-C to abort.
Alright, all done.

习题 18 命名 变量 函数

这一次我们来接触函数,具体的用法也很简单,参考 ex18.rb:

# this one is like your scripts with argv
def puts_two(*args)
arg1, arg2 = args
puts "arg1: #{arg1}, arg2: #{arg2}"
end
# ok, that *args is actually pointless, we can just do this
def puts_two_again(arg1, arg2)
puts "arg1: #{arg1}, arg2: #{arg2}"
end
# this just takes one argument
def puts_one(arg1)
puts "arg1: #{arg1}"
end
# this one takes no arguments
def puts_none()
puts "I got nothin'."
end
puts_two("Da","Wang")
puts_two_again("Da","Wang")
puts_one("First!")
puts_none()

运行一下,我们可以发现结果如下

$ ruby ex18.rb
arg1: Da, arg2: Wang
arg1: Da, arg2: Wang
arg1: First!
I got nothin'.

习题 19 函数和变量

函数中的变量,和程序中的变量是不一样,具体参见 ex19.rb:

def cheese_and_crackers(cheese_count, boxes_of_crackers)
puts "You have #{cheese_count} cheeses!"
puts "You have #{boxes_of_crackers} boxes of crackers!"
puts "Man that's enough for a party!"
puts "Get a blanket."
puts # a blank line
end
puts "We can just give the function numbers directly:"
cheese_and_crackers(20, 30)
puts "OR, we can use variables from our script:"
amount_of_cheese = 10
amount_of_crackers = 50
cheese_and_crackers(amount_of_cheese, amount_of_crackers)
puts "We can even do math inside too:"
cheese_and_crackers(10 + 20, 5 + 6)
puts "And we can combine the two, variables and math:"
cheese_and_crackers(amount_of_cheese + 100, amount_of_crackers + 1000)

得到的结果为

$ ruby ex19.rb
We can just give the function numbers directly:
You have 20 cheeses!
You have 30 boxes of crackers!
Man that's enough for a party!
Get a blanket.
OR, we can use variables from our script:
You have 10 cheeses!
You have 50 boxes of crackers!
Man that's enough for a party!
Get a blanket.
We can even do math inside too:
You have 30 cheeses!
You have 11 boxes of crackers!
Man that's enough for a party!
Get a blanket.
And we can combine the two, variables and math:
You have 110 cheeses!
You have 1050 boxes of crackers!
Man that's enough for a party!
Get a blanket.

习题 20 函数与文件

这一次我们结合函数和文件操作,具体参考 ex20.rb:

input_file = ARGV[0]
def print_all(f)
puts f.read()
end
def rewind(f)
f.seek(0, IO::SEEK_SET)
end
def print_a_line(line_count, f)
puts "#{line_count} #{f.readline()}"
end
current_file = File.open(input_file)
puts "First let's print the whole file:"
puts # a blank line
print_all(current_file)
puts "Now let's rewind, kind of like a tape."
rewind(current_file)
puts "Let's print three lines:"
current_line = 1
print_a_line(current_line, current_file)
current_line = current_line + 1
print_a_line(current_line, current_file)
current_line = current_line + 1
print_a_line(current_line, current_file)

结果为

$ ruby ex20.rb test.txt
First let's print the whole file:
This is line 1
This is line 2
This is line 3
Now let's rewind, kind of like a tape.
Let's print three lines:
1 This is line 1
2 This is line 2
3 This is line 3

习题 21 函数返回值

在函数中,最后得到的结果,会变成返回值,可以直接使用,具体参考 ex21.rb:

def add(a, b)
puts "ADDING #{a} + #{b}"
a + b
end
def subtract(a, b)
puts "SUBTRACTING #{a} - #{b}"
a - b
end
def multiply(a, b)
puts "MULTIPLYING #{a} * #{b}"
a * b
end
def divide(a, b)
puts "DIVIDING #{a} / #{b}"
a / b
end
puts "Let's do some math with just functions!"
age = add(30, 5)
height = subtract(78,4)
weight = multiply(90, 2)
iq = divide(100, 2)
puts "Age: #{age}, Height: #{height}, Weight: #{weight}, IQ: #{iq}"
# A puzzle for the extra credit, type it in anyway.
puts "Here is a puzzle."
what = add(age, subtract(height, multiply(weight, divide(iq, 2))))
puts "That becomes: #{what} Can you do it by hand?"

结果为

$ ruby ex21.rb
Let's do some math with just functions!
ADDING 30 + 5
SUBTRACTING 78 - 4
MULTIPLYING 90 * 2
DIVIDING 100 / 2
Age: 35, Height: 74, Weight: 180, IQ: 50
Here is a puzzle.
DIVIDING 50 / 2
MULTIPLYING 180 * 25
SUBTRACTING 74 - 4500
ADDING 35 + -4426
That becomes: -4391 Can you do it by hand?

习题 22 目前我们学了什么?

前面每一节的内容都比较简单,但是结合起来内容还是不少的。来看看到目前为止我们应该掌握的内容:

  • 添加注释
  • 进行基本数学运算
  • 使用变量
  • 格式化输出
  • 输入
  • 模块
  • 参数
  • 读写文件
  • 函数与返回值

习题 23 阅读代码

一些比较好的代码来源

  • github.com
  • heroku.com
  • rubygems.org
  • bitbucket.org

习题 24 复习一下

仔细阅读这一部分的代码,复习之前学过的内容,参考 ex24.rb:

puts "Let's practice everything."
puts "You\'d need to know \'bout escapes with \\ that do \n newlines and \t tabs."
poem = <<MULTI_LINE_STRING
\tThe lovely world
with logic so firmly planted
cannot discern \n the needs of love
nor comprehend passion from intuition
and requires an explanation
\n\t\twhere there is none.
MULTI_LINE_STRING
puts "--------------"
puts poem
puts "--------------"
five = 10 - 2 + 3 - 6
puts "This should be five: #{five}"
def secret_formula(started)
jelly_beans = started * 500
jars = jelly_beans / 1000
crates = jars / 100
return jelly_beans, jars, crates
end
start_point = 10000
beans, jars, crates = secret_formula(start_point)
puts "With a starting point of: #{start_point}"
puts "We'd have #{beans} beans, #{jars} jars, and #{crates} crates."
start_point = start_point / 10
puts "We can also do that this way:"
puts "We'd have %s beans, %s jars, and %s crates." % secret_formula(start_point)

习题 25 导入自定义模块

这一次我们把代码导入到 irb 中,交互执行,代码部分参加 ex25.rb:

module Ex25
def self.break_words(stuff)
# This function will break up words for us.
words = stuff.split(' ')
words
end
def self.sort_words(words)
# Sorts the words.
words.sort()
end
def self.print_first_word(words)
# Prints the first word and shifts the others down by one.
word = words.shift()
puts word
end
def self.print_last_word(words)
# Prints the last word after popping it off the end.
word = words.pop()
puts word
end
def self.sort_sentence(sentence)
# Takes in a full sentence and returns the sorted words.
words = break_words(sentence)
sort_words(words)
end
def self.print_first_and_last(sentence)
# Prints the first and last words of the sentence.
words = break_words(sentence)
print_first_word(words)
print_last_word(words)
end
def self.print_first_and_last_sorted(sentence)
# Sorts the words then prints the first and last one.
words = sort_sentence(sentence)
print_first_word(words)
print_last_word(words)
end
end

在交互模式下的操作为:

$ irb
irb(main):001:0> require './ex25'
=> true
irb(main):002:0> sentence = "All good things come to those who wait."
=> "All good things come to those who wait."
irb(main):003:0> words = Ex25.break_words(sentence)
=> ["All", "good", "things", "come", "to", "those", "who", "wait."]
irb(main):004:0> sorted_words = Ex25.sort_words(words)
=> ["All", "come", "good", "things", "those", "to", "wait.", "who"]
irb(main):005:0> Ex25.print_first_word(words)
All
=> nil
irb(main):006:0> Ex25.print_last_word(words)
wait.
=> nil
irb(main):007:0> Ex25.sort_sentence(sentence)
=> ["All", "come", "good", "things", "those", "to", "wait.", "who"]
irb(main):008:0>

习题 26 小测

这里复制代码并命名为 ex26.rb,然后修改错误,错误大多是拼写错误、括号没有匹配,如果有一个能够代码高亮的编辑器,就能很快找到错误所在,改完之后的代码

module Ex25
# This function will break up words for us.
def Ex25.break_words(stuff)
word = stuff.split(' ')
return word
end
# Sorts the words.
def Ex25.sort_words(words)
return words.sort
end
# Prints the first word after popping it off.
def Ex25.print_first_word(words)
word = words.pop(1)
puts word
end
# Prints the last word after popping it off.
def Ex25.print_last_word(words)
word = words.pop
puts word
end
# Takes in a full sentence and returns the sorted words.
def Ex25.sort_sentence(sentence)
words = Ex25.break_words(sentence)
return Ex25.sort_words(words)
end
# Prints the first and last words of the sentence.
def Ex25.print_first_and_last(sentence)
words = Ex25.break_words(sentence)
Ex25.print_first_word(words)
Ex25.print_last_word(words)
end
# Sorts the words then prints the first and last one.
def Ex25.print_first_and_last_sorted(sentence)
words = Ex25.sort_sentence(sentence)
Ex25.print_first_word(words)
Ex25.print_last_word(words)
end
end
def secret_formula(started)
jelly_beans = started * 500
jars = jelly_beans / 1000
crates = jars / 100
return jelly_beans, jars, crates
end
puts "Let's practice everything."
puts 'You\'d need to know \'bout escapes with \\ that do \n newlines and \t tabs.'
poem = <<END
\tThe lovely world
with logic so firmly planted
cannot discern \n the needs of love
nor comprehend passion from intuition
and requires an explanation
\n\t\twhere there is none.
END
puts "--------------"
puts poem
puts "--------------"
five = 10 - 2 + 3 - 6
puts "This should be five: #{five}"
start_point = 10000
beans, jars, crates = secret_formula(start_point)
puts "With a starting point of: #{start_point}"
puts "We'd have #{beans} beans, #{jars} jars, and #{crates} crates."
start_point = start_point / 10
sentence = "All good things come to those who wait."
words = Ex25.break_words(sentence)
sorted_words = Ex25.sort_words(words)
Ex25.print_first_word(words)
Ex25.print_last_word(words)
Ex25.print_first_word(sorted_words)
Ex25.print_last_word(sorted_words)
sorted_words = Ex25.sort_sentence(sentence)
Ex25.print_first_and_last(sentence)
Ex25.print_first_and_last_sorted(sentence)

运行结果为

$ ruby ex26.rb
Let's practice everything.
You'd need to know 'bout escapes with \ that do \n newlines and \t tabs.
--------------
The lovely world
with logic so firmly planted
cannot discern
the needs of love
nor comprehend passion from intuition
and requires an explanation
where there is none.
--------------
This should be five: 5
With a starting point of: 10000
We'd have 5000000 beans, 5000 jars, and 50 crates.
wait.
who
who
wait.
wait.
who
who
wait.

习题 27 逻辑关系

这一部分比较基本,记得 and, or, not 即可。如果不熟悉的话,网上也有很多相关,或者在 irb 中把各种可能试验一下,这里就不赘述了。

习题 28 布尔表达式

逻辑相关的基本练习,参见 ex28.rb:

true and true
false and true
1 == 1 and 2 == 1
"test" == "test"
1 == 1 or 2 != 1
true and 1 == 1
false and 0 != 0
true or 1 == 1
"test" == "testing"
1 != 0 and 2 == 1
"test" != "testing"
"test" == 1
not (true and false)
not (1 == 1 and 0 != 1)
not (10 == 1 or 1000 == 1000)
not (1 != 10 or 3 == 4)
not ("testing" == "testing" and "Zed" == "Cool Guy")
1 == 1 and not ("testing" == 1 or 1 == 0)
"chunky" == "bacon" and not (3 == 4 or 3 == 3)
3 == 3 and not ("testing" == "testing" or "Ruby" == "Fun")

把这些输入到 irb 中查看结果即可

习题 29 如果 IF

If 语句是分支中最常用的部分,参考 ex29.rb:

people = 20
cats = 30
dogs = 15
if people < cats
puts "Too many cats! The world is doomed!"
end
if people > cats
puts "Not many cats! The world is saved!"
end
if people < dogs
puts "The world is drooled on!"
end
if people > dogs
puts "The world is dry!"
end
dogs += 5
if people >= dogs
puts "People are greater than or equal to dogs."
end
if people <= dogs
puts "People are less than or equal to dogs."
end
if people == dogs
puts "People are dogs."
end

注意由 end 结尾,结果为:

$ ruby ex29.rb
Too many cats! The world is doomed!
The world is dry!
People are greater than or equal to dogs.
People are less than or equal to dogs.
People are dogs.

习题 30 Else 和 If

这一次我们继续来练习 If Else,具体参考 ex30.rb:

people = 30
cars = 40
buses = 15
if cars > people
puts "We should take the cars."
elsif cars < people
puts "We should not take the cars."
else
puts "We can't decide."
end
if buses > cars
puts "That's too many buses."
elsif buses < cars
puts "Maybe we could take the buses."
else
puts "We still can't decide."
end
if people > buses
puts "Alright, let's just take the buses."
else
puts "Fine, let's stay home then."
end

结果为

$ ruby ex30.rb
We should take the cars.
Maybe we could take the buses.
Alright, let's just take the buses.

习题 31 做出决定

我们可以结合 if, elsifelse 来完成更加复杂的逻辑,具体参考 ex31.rb:

def prompt
print "> "
end
puts "You enter a dark room with two doors. Do you go through door #1 or door #2?"
prompt; door = gets.chomp
if door == "1"
puts "There's a giant bear here eating a cheese cake. What do you do?"
puts "1. Take the cake."
puts "2. Scream at the bear."
prompt; bear = gets.chomp
if bear == "1"
puts "The bear eats your face off. Good job!"
elsif bear == "2"
puts "The bear eats your legs off. Good job!"
else
puts "Well, doing #{bear} is probably better. Bear runs away."
end
elsif door == "2"
puts "You stare into the endless abyss at Cthuhlu's retina."
puts "1. Blueberries."
puts "2. Yellow jacket clothespins."
puts "3. Understanding revolvers yelling melodies."
prompt; insanity = gets.chomp
if insanity == "1" or insanity == "2"
puts "Your body survives powered by a mind of jello. Good job!"
else
puts "The insanity rots your eyes into a pool of muck. Good job!"
end
else
puts "You stumble around and fall on a knife and die. Good job!"
end

可以自己尝试一下,其中一种可能是:

$ ruby ex31.rb
You enter a dark room with two doors. Do you go through door #1 or door #2?
> 2
You stare into the endless abyss at Cthuhlu's retina.
1. Blueberries.
2. Yellow jacket clothespins.
3. Understanding revolvers yelling melodies.
> 3
The insanity rots your eyes into a pool of muck. Good job!

习题 32 数组与循环

数组和循环是程序设计中再常用不过的元素,我们这次就来看一看,具体见 ex32.rb:

the_count = [1, 2, 3, 4, 5]
fruits = ['apples', 'oranges', 'pears', 'apricots']
change = [1, 'pennies', 2, 'dimes', 3, 'quarters']
# this first kind of for-loop goes through an array
for number in the_count
puts "This is count #{number}"
end
# same as above, but using a block instead
fruits.each do |fruit|
puts "A fruit of type: #{fruit}"
end
# also we can go through mixed arrays too
for i in change
puts "I got #{i}"
end
# we can also build arrays, first start with an empty one
elements = []
# then use a range object to do 0 to 5 counts
for i in (0..5)
puts "Adding #{i} to the list."
# push is a function that arrays understand
elements.push(i)
end
# now we can puts them out too
for i in elements
puts "Element was: #{i}"
end

注意不同的循环方式,结果为

$ ruby ex32.rb
This is count 1
This is count 2
This is count 3
This is count 4
This is count 5
A fruit of type: apples
A fruit of type: oranges
A fruit of type: pears
A fruit of type: apricots
I got 1
I got pennies
I got 2
I got dimes
I got 3
I got quarters
Adding 0 to the list.
Adding 1 to the list.
Adding 2 to the list.
Adding 3 to the list.
Adding 4 to the list.
Adding 5 to the list.
Element was: 0
Element was: 1
Element was: 2
Element was: 3
Element was: 4
Element was: 5

习题 33 While 循环

同样是循环的一种方式,但是因为可能死循环,所以使用的时候一定要小心,参见 ex33.rb:

i = 0
numbers = []
while i < 6
puts "At the top i is #{i}"
numbers.push(i)
i = i + 1
puts "Numbers now: #{numbers}"
puts "At the bottom i is #{i}"
end
puts "The numbers: "
for num in numbers
puts num
end

结果为

$ ruby ex33.rb
At the top i is 0
Numbers now: [0]
At the bottom i is 1
At the top i is 1
Numbers now: [0, 1]
At the bottom i is 2
At the top i is 2
Numbers now: [0, 1, 2]
At the bottom i is 3
At the top i is 3
Numbers now: [0, 1, 2, 3]
At the bottom i is 4
At the top i is 4
Numbers now: [0, 1, 2, 3, 4]
At the bottom i is 5
At the top i is 5
Numbers now: [0, 1, 2, 3, 4, 5]
At the bottom i is 6
The numbers:
0
1
2
3
4
5

习题 34 访问数组

下面直接用代码展示数组的常见操作,见 ex34.rb:

animals = ['bear', 'python', 'peacock', 'kangaroo', 'whale', 'platypus']
puts animals.first
puts animals.last
puts "size: %d" % animals.size
for i in (0..animals.size-1)
puts "The {%d} is %s" % [i, animals[i]]
end

结果为

$ ruby ex34.rb
bear
platypus
size: 6
The {0} is bear
The {1} is python
The {2} is peacock
The {3} is kangaroo
The {4} is whale
The {5} is platypus

习题 35 函数和分支

我们用函数和分支改写前面的游戏,参见 ex35.rb:

def prompt()
print "> "
end
def gold_room()
puts "This room is full of gold. How much do you take?"
prompt; next_move = gets.chomp
if next_move.include? "0" or next_move.include? "1"
how_much = next_move.to_i()
else
dead("Man, learn to type a number.")
end
if how_much < 50
puts "Nice, you're not greedy, you win!"
Process.exit(0)
else
dead("You greedy bastard!")
end
end
def bear_room()
puts "There is a bear here."
puts "The bear has a bunch of honey."
puts "The fat bear is in front of another door."
puts "How are you going to move the bear?"
bear_moved = false
while true
prompt; next_move = gets.chomp
if next_move == "take honey"
dead("The bear looks at you then slaps your face off.")
elsif next_move == "taunt bear" and not bear_moved
puts "The bear has moved from the door. You can go through it now."
bear_moved = true
elsif next_move == "taunt bear" and bear_moved
dead("The bear gets pissed off and chews your leg off.")
elsif next_move == "open door" and bear_moved
gold_room()
else
puts "I got no idea what that means."
end
end
end
def cthulu_room()
puts "Here you see the great evil Cthulu."
puts "He, it, whatever stares at you and you go insane."
puts "Do you flee for your life or eat your head?"
prompt; next_move = gets.chomp
if next_move.include? "flee"
start()
elsif next_move.include? "head"
dead("Well that was tasty!")
else
cthulu_room()
end
end
def dead(why)
puts "#{why} Good job!"
Process.exit(0)
end
def start()
puts "You are in a dark room."
puts "There is a door to your right and left."
puts "Which one do you take?"
prompt; next_move = gets.chomp
if next_move == "left"
bear_room()
elsif next_move == "right"
cthulu_room()
else
dead("You stumble around the room until you starve.")
end
end
start()

一个可能的结果为

$ ruby ex35.rb
You are in a dark room.
There is a door to your right and left.
Which one do you take?
> left
There is a bear here.
The bear has a bunch of honey.
The fat bear is in front of another door.
How are you going to move the bear?
> door
I got no idea what that means.
> honey
I got no idea what that means.
> open door
I got no idea what that means.
> taunt bear
The bear has moved from the door. You can go through it now.
> open door
This room is full of gold. How much do you take?
> 233
Man, learn to type a number. Good job!

习题 36 设计和测试

简单来说是从小的做起,然后利用 puts 检查值是否正确,当然这并不是最好的设计和测试方案,不过到目前为止,这样是足够的

习题 37 各种符号

这一次我们来了解 Ruby 中的各种符号,具体如下:

Keywords

  • alias
  • and
  • BEGIN
  • begin
  • break
  • case
  • class
  • def
  • defined?
  • do
  • else
  • elsif
  • END
  • end
  • ensure
  • false
  • for
  • if
  • in
  • module
  • next
  • nil
  • not
  • or
  • redo
  • rescue
  • retry
  • return
  • self
  • super
  • then
  • true
  • undef
  • unless
  • until
  • when
  • while
  • yield

数据类型

  • true
  • false
  • nil
  • constants
  • strings
  • numbers
  • ranges
  • arrays
  • hashes

字符串格式

  • \\
  • \'
  • \"
  • \a
  • \b
  • \f
  • \n
  • \r
  • \t
  • \v

操作符

  • ::
  • []
  • **
  • -(unary)
  • +(unary)
  • !
  • ~
  • *
  • /
  • %
  • +
  • -
  • <<
  • >>
  • &
  • |
  • >
  • >=
  • <
  • <=
  • <=>
  • ==
  • ===
  • !=
  • =~
  • !~
  • &&
  • ||
  • ..
  • ...

习题 38 再阅读代码

阅读的时候注意以下内容

  1. 函数及其功能
  2. 变量的初始值
  3. 不同的控制流程
  4. 不同的循环
  5. 记下来不懂的地方

习题 39 操作数组

数组是一个对象,拥有的方法可以令其当做不同的数据结构使用,参见 ex39.rb:

ten_things = "Apples Oranges Crows Telephone Light Sugar"
puts "Wait there's not 10 things in that list, let's fix that."
stuff = ten_things.split(' ')
more_stuff = %w(Day Night Song Frisbee Corn Banana Girl Boy)
while stuff.length != 10
next_one = more_stuff.pop()
puts "Adding: #{next_one}"
stuff.push(next_one)
puts "There's #{stuff.length} items now."
end
puts "There we go: #{stuff}"
puts "Let's do some things with stuff."
puts stuff[1]
puts stuff[-1] # whoa! fancy
puts stuff.pop()
puts stuff.join(' ') # what? cool!
puts stuff.values_at(3,5).join('#') # super stellar!

结果为

$ ruby ex39.rb
Wait there's not 10 things in that list, let's fix that.
Adding: Boy
There's 7 items now.
Adding: Girl
There's 8 items now.
Adding: Banana
There's 9 items now.
Adding: Corn
There's 10 items now.
There we go: ["Apples", "Oranges", "Crows", "Telephone", "Light", "Sugar", "Boy", "Girl", "Banana", "Corn"]
Let's do some things with stuff.
Oranges
Corn
Corn
Apples Oranges Crows Telephone Light Sugar Boy Girl Banana
Telephone#Sugar

习题 40 哈希

其实就是要学会用字典,注意后面代码中自定函数的部分,参加 ex40.rb

cities = {'CA' => 'San Francisco',
'MI' => 'Detroit',
'FL' => 'Jacksonville'}
cities['NY'] = 'New York'
cities['OR'] = 'Portland'
def find_city(map, state)
if map.include? state
return map[state]
else
return "Not found."
end
end
# ok pay attention!
cities[:find] = method(:find_city)
while true
print "State? (ENTER to quit) "
state = gets.chomp
break if state.empty?
# this line is the most important ever! study!
puts cities[:find].call(cities, state)
end

结果为

$ ruby ex40.rb
State? (ENTER to quit) NY
New York
State? (ENTER to quit)

习题 41 来自 Percal 25 星球的哥顿人(Gothons)

这一节更加复杂了,注意这里传递的都是函数,不明白的话要理解清楚,参见 ex41.rb

def prompt()
print "> "
end
def death()
quips = ["You died. You kinda suck at this.",
"Nice job, you died ...jackass.",
"Such a luser.",
"I have a small puppy that's better at this."]
puts quips[rand(quips.length())]
Process.exit(1)
end
def central_corridor()
puts "The Gothons of Planet Percal #25 have invaded your ship and destroyed"
puts "your entire crew. You are the last surviving member and your last"
puts "mission is to get the neutron destruct bomb from the Weapons Armory,"
puts "put it in the bridge, and blow the ship up after getting into an "
puts "escape pod."
puts "\n"
puts "You're running down the central corridor to the Weapons Armory when"
puts "a Gothon jumps out, red scaly skin, dark grimy teeth, and evil clown costume"
puts "flowing around his hate filled body. He's blocking the door to the"
puts "Armory and about to pull a weapon to blast you."
prompt()
action = gets.chomp()
if action == "shoot!"
puts "Quick on the draw you yank out your blaster and fire it at the Gothon."
puts "His clown costume is flowing and moving around his body, which throws"
puts "off your aim. Your laser hits his costume but misses him entirely. This"
puts "completely ruins his brand new costume his mother bought him, which"
puts "makes him fly into an insane rage and blast you repeatedly in the face until"
puts "you are dead. Then he eats you."
return :death
elsif action == "dodge!"
puts "Like a world class boxer you dodge, weave, slip and slide right"
puts "as the Gothon's blaster cranks a laser past your head."
puts "In the middle of your artful dodge your foot slips and you"
puts "bang your head on the metal wall and pass out."
puts "You wake up shortly after only to die as the Gothon stomps on"
puts "your head and eats you."
return :death
elsif action == "tell a joke"
puts "Lucky for you they made you learn Gothon insults in the academy."
puts "You tell the one Gothon joke you know:"
puts "Lbhe zbgure vf fb sng, jura fur fvgf nebhaq gur ubhfr, fur fvgf nebhaq gur ubhfr."
puts "The Gothon stops, tries not to laugh, then busts out laughing and can't move."
puts "While he's laughing you run up and shoot him square in the head"
puts "putting him down, then jump through the Weapon Armory door."
return :laser_weapon_armory
else
puts "DOES NOT COMPUTE!"
return :central_corridor
end
end
def laser_weapon_armory()
puts "You do a dive roll into the Weapon Armory, crouch and scan the room"
puts "for more Gothons that might be hiding. It's dead quiet, too quiet."
puts "You stand up and run to the far side of the room and find the"
puts "neutron bomb in its container. There's a keypad lock on the box"
puts "and you need the code to get the bomb out. If you get the code"
puts "wrong 10 times then the lock closes forever and you can't"
puts "get the bomb. The code is 3 digits."
code = "%s%s%s" % [rand(9)+1, rand(9)+1, rand(9)+1]
print "[keypad]> "
guess = gets.chomp()
guesses = 0
while guess != code and guesses < 10
puts "BZZZZEDDD!"
guesses += 1
print "[keypad]> "
guess = gets.chomp()
end
if guess == code
puts "The container clicks open and the seal breaks, letting gas out."
puts "You grab the neutron bomb and run as fast as you can to the"
puts "bridge where you must place it in the right spot."
return :the_bridge
else
puts "The lock buzzes one last time and then you hear a sickening"
puts "melting sound as the mechanism is fused together."
puts "You decide to sit there, and finally the Gothons blow up the"
puts "ship from their ship and you die."
return :death
end
end
def the_bridge()
puts "You burst onto the Bridge with the netron destruct bomb"
puts "under your arm and surprise 5 Gothons who are trying to"
puts "take control of the ship. Each of them has an even uglier"
puts "clown costume than the last. They haven't pulled their"
puts "weapons out yet, as they see the active bomb under your"
puts "arm and don't want to set it off."
prompt()
action = gets.chomp()
if action == "throw the bomb"
puts "In a panic you throw the bomb at the group of Gothons"
puts "and make a leap for the door. Right as you drop it a"
puts "Gothon shoots you right in the back killing you."
puts "As you die you see another Gothon frantically try to disarm"
puts "the bomb. You die knowing they will probably blow up when"
puts "it goes off."
return :death
elsif action == "slowly place the bomb"
puts "You point your blaster at the bomb under your arm"
puts "and the Gothons put their hands up and start to sweat."
puts "You inch backward to the door, open it, and then carefully"
puts "place the bomb on the floor, pointing your blaster at it."
puts "You then jump back through the door, punch the close button"
puts "and blast the lock so the Gothons can't get out."
puts "Now that the bomb is placed you run to the escape pod to"
puts "get off this tin can."
return :escape_pod
else
puts "DOES NOT COMPUTE!"
return :the_bridge
end
end
def escape_pod()
puts "You rush through the ship desperately trying to make it to"
puts "the escape pod before the whole ship explodes. It seems like"
puts "hardly any Gothons are on the ship, so your run is clear of"
puts "interference. You get to the chamber with the escape pods, and"
puts "now need to pick one to take. Some of them could be damaged"
puts "but you don't have time to look. There's 5 pods, which one"
puts "do you take?"
good_pod = rand(5)+1
print "[pod #]>"
guess = gets.chomp()
if guess.to_i != good_pod
puts "You jump into pod %s and hit the eject button." % guess
puts "The pod escapes out into the void of space, then"
puts "implodes as the hull ruptures, crushing your body"
puts "into jam jelly."
return :death
else
puts "You jump into pod %s and hit the eject button." % guess
puts "The pod easily slides out into space heading to"
puts "the planet below. As it flies to the planet, you look"
puts "back and see your ship implode then explode like a"
puts "bright star, taking out the Gothon ship at the same"
puts "time. You won!"
Process.exit(0)
end
end
ROOMS = {
:death => method(:death),
:central_corridor => method(:central_corridor),
:laser_weapon_armory => method(:laser_weapon_armory),
:the_bridge => method(:the_bridge),
:escape_pod => method(:escape_pod)
}
def runner(map, start)
next_one = start
while true
room = map[next_one]
puts "\n--------"
next_one = room.call()
end
end
runner(ROOMS, :central_corridor)

运行代码也是一样长,这里就不贴了。

习题 42 类

像上面那样访问函数,其实也有更加方便和规范的方法,就是类,具体参见 ex42.rb:

class TheThing
attr_reader :number
def initialize()
@number = 0
end
def some_function()
puts "I got called."
end
def add_me_up(more)
@number += more
return @number
end
end
# two different things
a = TheThing.new
b = TheThing.new
a.some_function()
b.some_function()
puts a.add_me_up(20)
puts a.add_me_up(20)
puts b.add_me_up(30)
puts b.add_me_up(30)
puts a.number
puts b.number

注意类中的各个方法和变量声明,因为本文重点在于实例,具体的理论就暂且略过,后面会细致说明,结果为:

$ ruby ex42.rb
I got called.
I got called.
20
40
30
60
40
60

习题 43 44 制作与评估游戏

时间所限,就不制作游戏了,这里列出一些需要注意的地方:

  • 类中使用驼峰命名法
  • 初始化函数中不应该做太多事情
  • 非类中的函数使用下划线命名法
  • 用一致的参数顺序组织参数
  • 不要覆盖全局变量和模块变量
  • 好的代码本身就是注释
  • 当然注释本身也是很重要的

习题 45 类的关系

主要说的是继承,看代码 ex45.rb:

## Animal is-a object (yes, sort of confusing) look at the extra credit
class Animal
end
class Dog < Animal
def initialize(name)
@name = name
end
end
class Cat < Animal
def initialize(name)
@name = name
end
end
class Person
attr_accessor :pet
def initialize(name)
@name = name
## Person has-a pet of some kind
@pet = nil
end
end
class Employee < Person
def initialize(name, salary)
## ?? hmm what is this strange magic?
super(name)
@salary = salary
end
end
class Fish
end
class Salmon < Fish
end
class Halibut < Fish
end
## rover is-a Dog
rover = Dog.new("Rover")
satan = Cat.new("Satan")
mary = Person.new("Mary")
mary.pet = satan
frank = Employee.new("Frank", 120000)
frank.pet = rover
flipper = Fish.new
crouse = Salmon.new
harry = Halibut.new

这里需要注意的就是尽量避免多重继承。

习题 46 项目结构

我们来看看如何来组织一个 Ruby 项目,先用下面的命令把需要的结构搭建起来

mkdir -p projects
cd projects/
mkdir skeleton
cd skeleton
mkdir bin lib lib/NAME test
# 配置文件
touch lib/NAME.rb
touch lib/NAME/version.rb

然后我们在 skeleton 文件夹下新建一个 NAME.gemspec,内容如下

# -*- encoding: utf-8 -*-
$:.push File.expand_path("../lib", __FILE__)
require "NAME/version"
Gem::Specification.new do |s|
s.name = "NAME"
s.version = NAME::VERSION
s.authors = ["Da Wang"]
s.email = ["dawang@wdxtub"]
s.homepage = ""
s.summary = %q{TODO: Write a gem summary}
s.description = %q{TODO: Write a gem description}
s.rubyforge_project = "NAME"
s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"]
end

然后在 test 文件下新建一个 test_NAME.rb 文件,内容为

require 'test/unit'
class MyUnitTests < Test::Unit::TestCase
def setup
puts "setup!"
end
def teardown
puts "teardown!"
end
def test_basic
puts "I RAN!"
end
end

完成之后我们需要安装 gem,也就是 Ruby 的包管理器,具体可能需要的组件有:

相关的使用可以查看文档,这里不赘述。

习题 47 自动化测试

有了项目,就要有测试,我们来看看具体要怎么进行自动化测试。仿照之前的 skeleton,我们新建一个名为 ex47 的项目。然后我们在 game.rb 中添加如下内容:

class Room
attr_accessor :name, :description, :paths
def initialize(name, description)
@name = name
@description = description
@paths = {}
end
def go(direction)
@paths[direction]
end
def add_paths(paths)
@paths.update(paths)
end
end

编写单元测试 test/test_ex47.rb

require 'test/unit'
require_relative '../lib/ex47'
class MyUnitTests < Test::Unit::TestCase
def test_room()
gold = Room.new("GoldRoom",
"""This room has gold in it you can grab. There's a
door to the north.""")
assert_equal(gold.name, "GoldRoom")
assert_equal(gold.paths, {})
end
def test_room_paths()
center = Room.new("Center", "Test room in the center.")
north = Room.new("North", "Test room in the north.")
south = Room.new("South", "Test room in the south.")
center.add_paths({:north => north, :south => south})
assert_equal(center.go(:north), north)
assert_equal(center.go(:south), south)
end
def test_map()
start = Room.new("Start", "You can go west and down a hole.")
west = Room.new("Trees", "There are trees here, you can go east.")
down = Room.new("Dungeon", "It's dark down here, you can go up.")
start.add_paths({:west => west, :down => down})
west.add_paths({:east => start})
down.add_paths({:up => start})
assert_equal(start.go(:west), west)
assert_equal(start.go(:west).go(:east), start)
assert_equal(start.go(:down).go(:up), start)
end
end

测试结果

$ ruby test_ex47.rb
Run options:
# Running tests:
Finished tests in 0.004635s, 647.2492 tests/s, 1510.2481 assertions/s.
3 tests, 7 assertions, 0 failures, 0 errors, 0 skips
ruby -v: ruby 2.1.4p265 (2014-10-27 revision 48166) [x86_64-darwin14.0]

写在最后

其实还剩下 48 - 52 五个习题,不过本文中我们的目标是简要了解 Ruby,就不深入网络框架的内容了。

需要注意的是,这里很多内容都是做了没说,遇到不懂的一定要仔细研究才是。

参考链接

捧个钱场?