『Ruby基础教程 第5版』代码清单

|第 1 章| |第 2 章| |第 3 章| |第 4 章| |第 5 章| |第 6 章| |第 7 章| |第 8 章| |第 9 章| |第 10 章| |第 11 章| |第 12 章| |第 13 章| |第 14 章| |第 15 章| |第 16 章| |第 17 章| |第 18 章| |第 19 章| |第 20 章| |第 21 章| |第 22 章| |第 23 章|

第 1 章

代码清单 1.1:helloruby.rb

1
print("Hello, Ruby.\n")

代码清单 1.2:put_and_p.rb

1
2
puts "Hello,\n\tRuby."
p "Hello,\n\tRuby."

代码清单 1.3:kiritsubo.rb

1
2
print "话说从前某一朝天皇时代,后宫妃嫔甚多,\n"
print "其中有一更衣,出身并不十分高贵,却蒙皇上特别宠爱\n"

代码清单 1.4:area_volume.rb

1
2
3
4
5
6
7
x = 10
y = 20
z = 30
area = (x*y + y*z + z*x) * 2
volume = x * y * z
print "表面积=", area, "\n"
print "体积=", volume, "\n"

代码清单 1.5:comment_sample.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
=begin
《Ruby基础教程》第5版例子
注释的使用例子
2006/06/16 创建
2006/07/01 追加一部分注释
2013/04/01 第4版更新
2015/10/01 第5版更新
=end
x = 10  # 宽
y = 20  # 长
z = 30  # 高
# 计算表面积和体积
area = (x*y + y*z + z*x) * 2
volume = x * y * z
# 输出
print "表面积=", area, "\n"
print "体积=", volume, "\n"

代码清单 1.6:greater_smaller.rb

1
2
3
4
5
6
7
a = 20
if a >= 10 then
  print "greater\n"
end
if a <= 9 then
  print "smaller\n"
end

代码清单 1.7:greater_smaller_else.rb

1
2
3
4
5
6
a = 20
if a >= 10
  print "greater\n"
else
  print "smaller\n"
end

第 2 章

代码清单 2.1:print_lin.rb

1
2
3
4
5
6
7
names = ["小林", "林", "高野", "森冈"]
["小林", "林", "高野", "森冈"]
names.each do |name|
  if /林/ =~ name
    puts name
  end
end

第 3 章

代码清单 3.1:print_argv.rb

1
2
3
4
5
puts "首个参数: #{ARGV[0]}"
puts "第2个参数: #{ARGV[1]}"
puts "第3个参数: #{ARGV[2]}"
puts "第4个参数: #{ARGV[3]}"
puts "第5个参数: #{ARGV[4]}"

代码清单 3.2:happy_birth.rb

1
2
name = ARGV[0]
print "Happy Birthday, ", name, "!\n"

代码清单 3.3:arg_arith.rb

1
2
3
4
5
6
num0 = ARGV[0].to_i
num1 = ARGV[1].to_i
puts "#{num0} + #{num1} = #{num0 + num1}"
puts "#{num0} - #{num1} = #{num0 - num1}"
puts "#{num0} * #{num1} = #{num0 * num1}"
puts "#{num0} / #{num1} = #{num0 / num1}"

代码清单 3.4:read_text.rb

1
2
3
4
5
filename = ARGV[0]
file = File.open(filename) # ① 打开文件
text = file.read           # ② 读取文本
print text                 # ③ 输出文本
file.close                 # ④ 关闭文件

代码清单 3.5:read_text_simple.rb

1
2
3
filename = ARGV[0]
text = File.read(filename)
print text

代码清单 3.6:read_text_oneline.rb

1
print File.read(ARGV[0])

代码清单 3.7:read_line.rb

1
2
3
4
5
6
filename = ARGV[0]
file = File.open(filename)
file.each_line do |line|
  print line
end
file.close

代码清单 3.8:simple_grep.rb

1
2
3
4
5
6
7
8
9
10
pattern = Regexp.new(ARGV[0])
filename = ARGV[1]

file = File.open(filename)
file.each_line do |line|
  if pattern =~ line
    print line
  end
end
file.close

代码清单 3.9:hello_ruby2.rb

1
2
3
4
5
def hello
  puts "Hello, Ruby"
end

hello()

代码清单 3.10:grep.rb

1
2
3
4
5
6
7
8
9
def simple_grep(pattern, filename)
  file = File.open(filename)
  file.each_line do |line|
    if pattern =~ line
      print line
    end
  end
  file.close
end

代码清单 3.11:use_grep.rb

1
2
3
4
5
require_relative "grep"         # 读取grep.rb(省略“.rb”)

pattern = Regexp.new(ARGV[0])
filename = ARGV[1]
simple_grep(pattern, filename)  # 调用simple_grep方法

代码清单 专栏:p_and_pp.rb

1
2
3
4
5
6
7
8
9
require "pp"

books = [
  { title: "猫街", author: "萩原朔太郎" },
  { title: "猫的事务所", author: "宫泽贤治" },
  { title: "猫语教科书", author: "Paul Gallico" },
]
p books
pp books

第 4 章

代码清单 4.1:scopetest.rb

1
2
3
4
5
6
7
$x = 0
x = 0

require_relative "sub"

p $x   #=> 1
p x    #=> 0

代码清单 4.2:sub.rb

1
2
$x = 1  ## 对全局变量赋值
x = 1   ## 对局部变量赋值

第 5 章

代码清单 5.1:ad2pingcheng.rb

1
2
3
4
5
#将西历转换为平成纪年

ad = ARGV[0].to_i
pingcheng = ad - 1988
puts pingcheng

代码清单 5.2:if_elsif.rb

1
2
3
4
5
6
7
8
9
a = 10
b = 20
if a > b
  puts "a比b大"
elsif a < b
  puts "a比b小"
else
  puts "a与b相等"
end

代码清单 5.3:unless.rb

1
2
3
4
5
a = 10
b = 20
unless a > b
  puts "a不比b大"
end

代码清单 5.4:case.rb

1
2
3
4
5
6
7
8
9
10
11
tags = [ "A", "IMG", "PRE" ]
tags.each do |tagname|
  case tagname
  when "P","A","I","B","BLOCKQUOTE"
    puts "#{tagname} has child."
  when "IMG", "BR"
    puts "#{tagname} has no child."
  else
    puts "#{tagname} cannot be used."
  end
end

代码清单 5.5:case_class.rb

1
2
3
4
5
6
7
8
9
10
11
array = [ "a", 1, nil ]
array.each do |item|
  case item
  when String
    puts "item is a String."
  when Numeric
    puts "item is a Numeric."
  else
    puts "item is something."
  end
end

第 6 章

代码清单 6.1:times.rb

1
2
3
7.times do
  puts "满地油菜花"
end

代码清单 6.2:times2.rb

1
2
3
5.times do |i|
  puts "第#{i}次的循环。"
end

代码清单 6.3:times3.rb

1
2
3
5.times do |i|
  puts "第#{i+1}次的循环。"
end

代码清单 6.4:for.rb

1
2
3
4
5
sum = 0
for i in 1..5
  sum = sum + i
end
puts sum

代码清单 6.5:for_names.rb

1
2
3
4
names = ["awk", "Perl", "Python", "Ruby"]
for name in names
  puts name
end

代码清单 6.6:while.rb

1
2
3
4
5
i = 1
while i < 3
  puts i
  i += 1
end

代码清单 6.7:while2.rb

1
2
3
4
5
6
7
sum = 0
i = 1
while i <= 5
  sum += i
  i += 1
end
puts sum

代码清单 6.8:while3.rb

1
2
3
4
5
6
7
sum = 0
i = 1
while sum < 50
  sum += i
  i += 1
end
puts sum

代码清单 6.9:until.rb

1
2
3
4
5
6
7
sum = 0
i = 1
until sum >= 50
  sum += i
  i+= 1
end
puts sum

代码清单 6.10:while_not.rb

1
2
3
4
5
6
7
sum = 0
i = 1
while !(sum >= 50)
  sum += i
  i += 1
end
puts sum

代码清单 6.11:each_names.rb

1
2
3
4
names = ["awk","Perl","Python","Ruby"]
names.each do |name|
  puts name
end

代码清单 6.12:each.rb

1
2
3
4
5
sum = 0
(1..5).each do |i|
  sum= sum + i
end
puts sum

代码清单 6.13:break_next.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
puts "break的例子:"
i = 0
["Perl", "Python", "Ruby", "Scheme"].each do |lang|
  i += 1
  if i == 3
    break
  end
  p [i,lang]
end

puts "next的例子:"
i = 0
["Perl", "Python", "Ruby", "Scheme"].each do |lang|
  i += 1
  if i == 3
    next
  end
  p [i,lang]
end

代码清单 6.14:ten_lines_grep.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pattern = Regexp.new(ARGV[0])
filename = ARGV[1]
max_matches = 10      # 输出的最大行数
matches = 0           # 已匹配的行数
file = File.open(filename)
file.each_line do |line|
  if matches >= max_matches
    break
  end
  if pattern =~ line
    matches += 1
    puts line
  end
end
file.close

代码清单 6.15:strip.rb

1
2
3
4
5
6
7
file = File.open(ARGV[0])
file.each_line do |line|
  next if /^\s*$/ =~ line  # 空行
  next if /^#/ =~ line     # 以“#”开头的行
  puts line
end
file.close

代码清单 6.16:hello.rb

1
2
3
4
5
6
7
8
# Hello, World
puts("hello, world")

# 日语
puts("こんにちは世界")

# 汉语
puts("你好,世界")

代码清单 6.17:stripped_hello.rb

1
2
3
puts("hello, world")
puts("こんにちは世界")
puts("你好,世界")

第 7 章

代码清单 7.1:times_with_param.rb

1
2
3
5.times do |i|
  puts "第#{i}次循环。"
end

代码清单 7.2:hello_with_name.rb

1
2
3
4
5
def hello(name)
  puts "Hello, #{name}."
end

hello("Ruby")

代码清单 7.3:hello_with_default.rb

1
2
3
4
5
6
def hello(name="Ruby")
  puts "Hello, #{name}."
end

hello()         # 省略参数的调用
hello("Newbie") # 指定参数的调用

代码清单 7.4:myloop.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
def myloop
  while true
    yield               # 执行块
  end
end

num = 1                 # 初始化num
myloop do
  puts "num is #{num}"  # 输出num
  break if num > 10     # num超过10时跳出循环
  num *= 2              # num乘2
end

第 8 章

代码清单 8.1:hello_class.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class HelloWorld                   # class语句
  def initialize(myname = "Ruby")  # initialize方法
    @name = myname                 # 初始化实例变量
  end

  def hello                        # 实例方法
    puts "Hello, world. I am #{@name}."
  end
end

bob = HelloWorld.new("Bob")
alice = HelloWorld.new("Alice")
ruby = HelloWorld.new

bob.hello

代码清单 8.2:hello_class2(部分).rb

1
2
3
4
5
6
7
8
9
10
11
class HelloWorld
  ...
  def name          # 获取@name
    @name
  end

  def name=(value)  # 修改@name
    @name = value
  end
  ...
end

代码清单 8.3:hello_class3(部分).rb

1
2
3
4
5
6
7
8
class HelloWorld
  attr_accessor :name
  ...
  def greet
    puts "Hi, I am #{self.name}."
  end
end
  ...

代码清单 8.4:hello_count.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class HelloCount
  @@count = 0           # 调用hello方法的次数

  def HelloCount.count  # 读取调用次数的类方法
    @@count
  end

  def initialize(myname="Ruby")
    @name = myname
  end

  def hello
    @@count += 1        # 累加调用次数
    puts "Hello, world. I am #{@name}.\n"
  end
end

bob = HelloCount.new("Bob")
alice = HelloCount.new("Alice")
ruby = HelloCount.new

p HelloCount.count      #=> 0
bob.hello
alice.hello
ruby.hello
p HelloCount.count      #=> 3

代码清单 8.5:acc_test.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class AccTest
  def pub
    puts "pub is a public method."
  end

  public :pub   # 把pub方法设定为public(可省略)

  def priv
    puts "priv is a private method."
  end

  private :priv # 把priv方法设定为private
end

acc = AccTest.new
acc.pub
acc.priv

代码清单 8.6:point.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Point
  attr_accessor :x, :y   # 定义存取器
  protected :x=, :y=     # 把x=与y=设定为protected

  def initialize(x=0.0, y=0.0)
    @x, @y = x, y
  end

  def swap(other)        # 置换x、y值的方法
    tmp_x, tmp_y = @x, @y
    @x, @y = other.x, other.y
    other.x, other.y = tmp_x, tmp_y   # 在同一个类中
                                      # 可以被调用
    return self
  end
end

p0 = Point.new
p1 = Point.new(1.0, 2.0)
p [ p0.x, p0.y ]         #=> [0.0, 0.0]
p [ p1.x, p1.y ]         #=> [1.0, 2.0]

p0.swap(p1)
p [ p0.x, p0.y ]         #=> [1.0, 2.0]
p [ p1.x, p1.y ]         #=> [0.0, 0.0]

p0.x = 10.0              #=> 错误(NoMethodError)

代码清单 8.7:ext_string.rb

1
2
3
4
5
6
7
8
9
10
class String
  def count_word
    ary = self.split(/\s+/) # 用空格分割self
                            # 分解成数组
    return ary.size         # 返回分割后的数组的元素总数
  end
end

str = "Just Another Ruby Newbie"
p str.count_word            #=> 4

代码清单 8.8:ring_array.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
class RingArray < Array  # 指定父类
  def [](i)              # 重定义运算符[]
    idx = i % size       # 计算新索引值
    super(idx)           # 调用父类中同名的方法
  end
end

wday = RingArray["日", "月", "火", "水", "木", "金", "土"]
wday = RingArray["日", "月", "火", "水", "木", "金", "土"]
p wday[6]   #=> "土"
p wday[11]  #=> "木"
p wday[15]  #=> "月"
p wday[-1]  #=> "土"

代码清单 8.9:alias_sample.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class C1                    # 定义C1
  def hello                 # 定义hello
    "Hello"
  end
end

class C2 < C1               # 定义继承了C1 的子类C2
  alias old_hello hello     # 设定别名old_hello
  
  def hello               # 重定义hello
  "#{old_hello}, again"
  end
end

obj = C2.new
p obj.old_hello             #=> "Hello"
p obj.hello                 #=> "Hello, again

代码清单 8.10:mixin_sample.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
module MyModule
  # 希望提供的共通方法等
end

class MyClass1
  include MyModule
  # MyClass1中特有的方法等
end

class MyClass2
  include MyModule
  # MyClass2中特有的方法等
end

代码清单 8.11:hello_module.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module HelloModule          # module关键字
  Version = "1.0"           # 定义常量

  def hello(name)           # 定义方法
    puts "Hello, #{name}."
  end  
  module_function :hello    # 指定hello方法为模块函数
end

p HelloModule::Version      #=> "1.0"
HelloModule.hello("Alice")  #=> Hello, Alice.

include HelloModule         # 包含模块
p Version                   #=> "1.0"
hello("Alice")              #=> Hello, Alice.

代码清单 8.12:mixin_test.rb

1
2
3
4
5
6
7
8
9
10
11
12
module M
  def meth
    "meth"
  end
end

class C
  include M  # 包含M模块
end

c = C.new
p c.meth     #=> meth

代码清单 8.13:fetch_and_downcase.rb

1
2
3
4
5
6
7
8
def fetch_and_downcase(ary, index)
  if str = ary[index]
    return str.downcase
  end
end

ary = ["Boo", "Foo", "Woo"]
p fetch_and_downcase(ary, 1)  #=> "foo"

代码清单 8.14:http_get.rb

1
2
3
4
5
6
require "net/http"
require "uri"
url = URI.parse("http://www.ruby-lang.org/ja/")
http = Net::HTTP.start(url.host, url.port)
doc = http.get(url.path)
puts puts doc.body

第 9 章

代码清单 9.1:point.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Point
  attr_reader :x, :y

  def initialize(x=0, y=0)
    @x, @y = x, y
  end

  def inspect  # 用于显示
    "(#{x}, #{y})"
  end

  def +(other)  # x、y分别进行加法运算
    self.class.new(x + other.x, y + other.y)
  end

  def -(other)  # x、y分别进行减法运算
    self.class.new(x - other.x, y - other.y)
  end
end

point0 = Point.new(3, 6)
point1 = Point.new(1, 8)

p point0           #=> (3, 6)
p point1           #=> (1, 8)
p point0 + point1  #=> (4, 14)
p point0 - point1  #=> (2, -2)

代码清单 9.2:point1(部分).rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Point
  ...
  def +@
    dup                     # 返回自己的拷贝
  end

  def -@
    self.class.new(-x, -y)  # 颠倒x、y各自的正负
  end

  def ~@
    self.class.new(-y, x)   # 使坐标翻转90度
  end
end

point = Point.new(3, 6)
p +point  #=> (3, 6)
p -point  #=> (-3, -6)
p ~point  #=> (-6, 3)

代码清单 9.3:point2(部分).rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Point
  ...
  def [](index)
    case index
    when 0
      x
    when 1
      y
    else
      raise ArgumentError, "out of range `#{index}'"
    end
  end

  def []=(index, val)
    case index
    when 0
      self.x = val
    when 1
      self.y = val
    else
      raise ArgumentError, "out of range `#{index}'"
    end
  end
end

point = Point.new(3, 6)
p point[0]           #=> 3
p point[1] = 2       #=> 2
p point[1]           #=> 2
p point[2]           #=>错误(ArgumentError)

第 10 章

代码清单 10.1:wc.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
ltotal=0                             # 行数合计
wtotal=0                             # 单词数合计
ctotal=0                             # 字数合计
ARGV.each do |file|
  begin
    input = File.open(file)          # 打开文件(A)
    l=0                              # file内的行数
    w=0                              # file内的单词数
    c=0                              # file内的字数
    input.each_line do |line|
      l += 1
      c += line.size
      line.sub!(/^\s+/, "")          # 删除行首的空白符
      ary = line.split(/\s+/)        # 用空白符分解
      w += ary.size
    end
    input.close                      # 关闭文件
    printf("%8d %8d %8d %s\n", l, w, c, file)  # 整理输出格式
    ltotal += l
    wtotal += w
    ctotal += c
  rescue => ex
    print ex.message, "\n"           # 输出异常信息(B)
  end
end

printf("%8d %8d %8d %s\n", ltotal, wtotal, ctotal, "total")

代码清单 copy:cp.rb

1
2
3
4
5
6
7
8
9
10
11
    def copy(from, to)
      src = File.open(from)         # 打开原文件from(A)
      begin
        dst = File.open(to, "w")    # 打开目标文件to(B)
        data = src.read
        dst.write(data)
        dst.close
      ensure
        src.close                   # (C)
      end
    end

第 11 章

代码清单 11.1:hash_each.rb

1
2
3
4
5
6
sum = 0
outcome = {"参加费"=>1000, "挂件费用"=>1000, "联欢会费用"=>4000}
outcome.each do |pair|
  sum += pair[1]  # 指定值
end
puts "合计:#{sum}"

代码清单 11.2:hash_each2.rb

1
2
3
4
5
6
sum = 0
outcome = {"参加费"=>1000, "挂件费用"=>1000, "联欢会费用"=>4000}
outcome.each do |item, price|
  sum += price
end
puts "合计:#{sum}"

代码清单 11.3:file_each.rb

1
2
3
4
5
file = File.open("sample.txt")
file.each_line do |line|
  print line
end
file.close

代码清单 11.4:file_open.rb

1
2
3
4
5
File.open("sample.txt") do |file|
  file.each_line do |line|
    print line
  end
end

代码清单 11.5:file_open_no_block.rb

1
2
3
4
5
6
7
8
file = File.open("sample.txt")
begin
  file.each_line do |line|
    print line
  end
ensure
  file.close
end

代码清单 11.6:sort_comp_count.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# %w(...)是一个用于生成一个以各单词作为元素的数组的字面量
ary = %w(
  Ruby is a open source programming language with a focus
  on simplicity and productivity. It has an elegant syntax
  that is natural to read and easy to write
)

call_num = 0    # 块的调用次数
sorted = ary.sort do |a, b|
  call_num += 1 # 累加块的调用次数
  a.length <=> b.length
end

puts "排序结果 #{sorted}"
puts "数组的元素数量 #{ary.length}"
puts "调用块的次数 #{call_num}"

代码清单 11.7:myloop.rb

1
2
3
4
5
6
7
8
9
10
11
12
def myloop
  while true
    yield               # 执行块
  end
end

num = 1                 # 初始化num
myloop do
  puts "num is #{num}"  # 输出num
  break if num > 100    # num超过100后跳出循环
  num *= 2              # num乘2
end

代码清单 11.8:total.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def total(from, to)
  result = 0                # 合计值
  from.upto(to) do |num|    # 处理从from到to的值
    if block_given?         #   如果有块的话
      result += yield(num)  #     累加经过块处理的值
    else                    #   如果没有块的话
      result += num         #     直接累加
    end
  end
  return result             # 返回方法的结果
end

p total(1, 10)                  # 从1到10的和 => 55
p total(1, 10){|num| num ** 2 } # 从1到10的2次冥的和 => 385

代码清单 11.9:block_args_test.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def block_args_test
  yield()             # 0个块变量
  yield(1)            # 1个块变量
  yield(1, 2, 3)      # 3个块变量
end

puts "通过|a|接收块变量"
block_args_test do |a|
  p [a]
end
puts

puts "通过|a, b, c|接收块变量"
block_args_test do |a, b, c|
  p [a, b, c]
end
puts

puts "通过|*a|接收块变量"
block_args_test do |*a|
  p [a]
end
puts

代码清单 11.10:proc1.rb

1
2
3
4
5
6
hello = Proc.new do |name|
  puts "Hello, #{name}."
end

hello.call("World")
hello.call("Ruby")

代码清单 11.11:total2.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def total2(from, to, &block)
  result = 0               # 合计值
  from.upto(to) do |num|   # 处理从from到to的值
    if block               #   如果有块的话
      result +=            #     累加经过块处理的值
           block.call(num)
    else                   #   如果没有块的话
      result += num        #     直接累加
    end
  end
  return result            # 返回方法的结果
end

p total2(1, 10)                   # 从1到10的和 => 55
p total2(1, 10){|num| num ** 2 }  # 从1到10的2次冥的和 => 385

代码清单 11.12:call_each.rb

1
2
3
4
5
6
7
def call_each(ary, &block)
  ary.each(&block)
end

call_each [1, 2, 3] do |item|
  p item
end

代码清单 11.13:local_and_block.rb

1
2
3
4
5
6
7
8
9
x = 1            # 初始化x
y = 1            # 初始化y
ary = [1, 2, 3]

ary.each do |x|  # 将x作为块变量使用
  y = x          # 将x赋值给y
end

p [x, y]         # 确认x与y的值

代码清单 11.14:local_and_block2.rb

1
2
3
4
5
6
7
8
9
x = y = z = 0       # 初始化x、y、z
ary = [1, 2, 3]
ary.each do |x; y|  # 使用块变量x,块局部变量y
  y = x             # 代入块局部变量y
  z = x             # 代入不是块局部变量的变量z
  p [x, y, z]       # 确认块内的x、y、z的值
end
puts
p [x, y, z]         # 确认x、y、z的值

第 12 章

无代码清单。

第 13 章

代码清单 13.1:list.rb

1
2
3
4
list = ["a", "b", "c", "d"]
for i in 0..3
  print "第", i+1,"个元素是",list[i],"。\n"
end

代码清单 13.2:sum_list.rb

1
2
3
4
5
6
list = [1, 3, 5, 7, 9]
sum = 0
for i in 0..4
  sum += list[i]
end
print "合计:",sum,"\n"

代码清单 13.3:sum_list2.rb

1
2
3
4
5
6
list = [1, 3, 5, 7, 9]
sum = 0
list.each do |elem|
  sum += elem
end
print "合计:",sum,"\n"

代码清单 13.4:list2.rb

1
2
3
4
list = ["a", "b", "c", "d"]
list.each_with_index do |elem, i|
  print "第", i+1,"个元素是",elem,"。\n"
end

代码清单 13.5:sum_with_each.rb

1
2
3
4
5
6
7
8
9
10
11
ary1 = [1, 2, 3, 4, 5]
ary2 = [10, 20, 30, 40, 50]
ary3 = [100, 200, 300, 400, 500]

i = 0
result = []
while i < ary1.length
  result << ary1[i] + ary2[i] + ary3[i]
  i += 1
end
p result  #=> [111, 222, 333, 444, 555]

代码清单 13.6:sum_with_zip.rb

1
2
3
4
5
6
7
8
9
ary1 = [1, 2, 3, 4, 5]
ary2 = [10, 20, 30, 40, 50]
ary3 = [100, 200, 300, 400, 500]

result = []
ary1.zip(ary2, ary3) do |a, b, c|
  result << a + b + c
end
p result  #=> [111, 222, 333, 444, 555]

第 14 章

代码清单 14.1:frozen_string.rb

1
2
3
4
# frozen-string-literal: true

str = "Ruby"
p str.upcase!

第 15 章

代码清单 15.1:word_count.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 计算单词数量
count = Hash.new(0)

## 统计单词
File.open(ARGV[0]) do |f|
  f.each_line do |line|
    words = line.split
    words.each do |word|
      count[word] += 1
    end
  end
end

## 输出结果
count.sort{|a, b|
  a[1] <=> b[1]
}.each do |key, value|
  print "#{key}: #{value}\n"
end

第 16 章

代码清单 16.1:scan1.rb

1
2
3
"abracatabra".scan(/.a/) do |matched|
  p matched
end

代码清单 16.2:scan2.rb

1
2
3
"abracatabra".scan(/(.)(a)/) do |matched|
  p matched
end

代码清单 16.3:scan3.rb

1
2
3
"abracatabra".scan(/(.)(a)/) do |a, b|
  p a+"-"+b
end

代码清单 16.4:url_match.rb

1
2
3
str = "http://www.ruby-lang.org/ja/"
%r|http://([^/]*)/| =~ str
print "server address: ", $1, "\n"

第 17 章

代码清单 17.1:out.rb

1
2
3
4
3.times do |i|
  $stdout.puts "#{Random.rand}\n"  # 标准输出
  $stderr.puts "已经输出了#{i+1}\n"  # 标准错误输出
end

代码清单 17.2:tty.rb

1
2
3
4
5
if $stdin.tty?
  print "Stdin is a TTY.\n"
else
  print "Stdin is not a TTY.\n"
end

代码清单 17.3:stdout_put.rb

1
$stdout.puts "String", :Symbol, 1/100r

代码清单 17.4:stdout_putc.rb

1
2
3
$stdout.putc(82)  # 82是“R”的ASCII码
$stdout.putc("Ruby")
$stdout.putc("\n")

代码清单 17.5:test_buffering1.rb

1
2
3
4
5
6
7
8
9
10
filename = "buffering.txt"
File.open(filename, "w") do |file|
  3.times do |i|
    # 检查每5个字节写入后文件的大小
    file.write("a" * 5)
    puts "第#{i+1}次: #{File.size(filename)}"
  end
end
puts "结束: #{File.size(filename)}"  #=> 再检查一次
p File.read(filename)                #=> 确认输出

代码清单 17.6:test_buffering2.rb

1
2
3
4
5
6
7
8
9
10
11
filename = "buffering.txt"
File.open(filename, "w") do |file|
  3.times do |i|
    # 检查每5个字节写入后文件的大小
    file.write("a" * 5)
    file.flush                       # 写入数据
    puts "第#{i+1}次: #{File.size(filename)}"
  end
end
puts "结束后: #{File.size(filename)}"  # 再检查一次
p File.read(filename)                # 确认输出

代码清单 17.7:test_buffering3.rb

1
2
3
4
5
6
7
8
9
10
11
filename = "buffering.txt"
File.open(filename, "w") do |file|
  file.sync = true                   # 同步写入
  3.times do |i|
    # 检查每写入5个字节后文件的大小
    file.write("a" * 5)
    puts "第#{i+1}次: #{File.size(filename)}"
  end
end
puts "结束后: #{File.size(filename)}"  # 再检查一次
p File.read(filename)                # 确认输出

代码清单 17.8:simple_grep_gz.rb

1
2
3
4
5
6
7
8
9
10
11
12
pattern = Regexp.new(ARGV[0])
filename = ARGV[1]
if /.gz$/ =~ filename
  file = IO.popen("zcat #{filename}")
else
  file = File.open(filename)
end
file.each_line do |text|
  if pattern =~ text
    print text
  end
end

代码清单 17.9:read_uri.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
require "open-uri"

# 通过HTTP读取数据
open("http://www.ruby-lang.org/zh_cn/") do |io|
  puts io.read  # 将Ruby的官方网页输出到控制台
end

# 通过FTP读取数据
filename = "ruby-2.3.0.tar.gz"
url = "ftp://www.ruby-lang.org/pub/ruby/2.3/#{filename}"
open(url) do |io|
  File.binwrite(filename, io.read)  # 写入文件
end

代码清单 17.10:read_uri_ja.rb

1
2
3
4
5
6
7
8
require "open-uri"

options = {
  "Accept-Language" => "ja, en;q=0.5",
}
open("http://www.ruby-lang.org", options){|io|
  puts io.read
}

代码清单 17.11:stringio_puts.rb

1
2
3
4
5
6
7
8
require "stringio"

io = StringIO.new
io.puts("A")
io.puts("B")
io.puts("C")
io.rewind
p io.read  #=> "A\nB\nC\n"

代码清单 17.12:stringio_gets.rb

1
2
3
4
5
6
require "stringio"

io = StringIO.new("A\nB\nC\n")
p io.gets  #=> "A\n"
p io.gets  #=> "B\n"
p io.gets  #=> "C\n"

第 18 章

代码清单 18.1:traverse.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def traverse(path)
  if File.directory?(path)  # 如果是目录
    dir = Dir.open(path)
    while name = dir.read
      next if name == "."   # ※
      next if name == ".."  # ※
      traverse(path + "/" + name)
    end
    dir.close
  else
    process_file(path)      # 处理文件
  end
end

def process_file(path)
  puts path                 # 输出结果
end

traverse(ARGV[0])

代码清单 18.2:traverse_by_glob.rb

1
2
3
4
5
6
7
def traverse(path)
  Dir.glob(["#{path}/**/*", "#{path}/**/.*"]).each do |name|
    unless File.directory?(name)
      process_file(name)
    end
  end
end

代码清单 18.3:listdir.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
require 'find'

IGNORES = [ /^\./, /^CVS$/, /^RCS$/ ]

def listdir(top)
  Find.find(top) do |path|
    if FileTest.directory?(path)  # 如果path是目录
      dir, base = File.split(path)
      IGNORES.each do |re|
        if re =~ base             # 需要忽略的目录
          Find.prune              # 忽略该目录下的内容的查找
        end
      end
      puts path                   # 输出结果
    end
  end
end

listdir(ARGV[0])

第 19 章

无代码清单。

第 20 章

无代码清单。

第 21 章

代码清单 21.1:power_of.rb

1
2
3
4
5
6
7
8
def power_of(n)
  lambda do |x|
    return x ** n
  end
end

cube = power_of(3)
p cube.call(5)  #=> 125

代码清单 21.2:prefix.rb

1
2
3
4
5
6
7
8
9
10
11
12
def prefix(ary, obj)
  result = []               # 初始化结果数组
  ary.each do |item|        # 逐个检查元素
    result << item          # 将元素追加到结果数组中
    if item == obj          # 如果元素与条件一致
      return result         # 返回结果数组
    end
  end
  return result             # 所有元素检查完毕的时候
end

prefix([1, 2, 3, 4, 5], 3)  #=> [1, 2, 3]

代码清单 21.3:total2.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def total2(from, to, &block)
  result = 0               # 合计值
  from.upto(to) do |num|   # 处理从from到to的值
    if block               #   如果有块的话
      result +=            #     累加经过块处理的值
           block.call(num)
    else                   #   如果没有块的话
      result += num        #     直接累加
    end
  end
  return result            # 返回方法的结果
end

p total2(1, 10)                   # 从1到10的和 => 55
p total2(1, 10){|num| num ** 2 }  # 从1到10的2次冥的和 => 385

代码清单 21.4:counter_proc.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def counter
  c = 0          # 初始化计数器
  Proc.new do    # 每调用1次call方法,计数器加1
    c += 1       # 返回加1后的Proc对象
  end
end

# 创建计数器c1并计数
c1 = counter
p c1.call      #=> 1
p c1.call      #=> 2
p c1.call      #=> 3

# 创建计数器c2并计数
c2 = counter   # 创建计数器c2
p c2.call      #=> 1
p c2.call      #=> 2

# 再次用c1计数
p c1.call      #=> 4

代码清单 21.5:proc_source_location.rb

1
2
3
4
5
prc0 = Proc.new{ nil }
prc1 = Proc.new{|a| a }

p prc0.source_location
p prc1.source_location

第 22 章

代码清单 22.1:get_kongyiji.rb

1
2
3
4
5
6
7
8
9
10
require "open-uri"

url = "http://www.ituring.com.cn/article/274457"
filename = "kongyiji.html"

File.open(filename, "wb") do |f|
  text = open(url, "r:utf-8").read
  f.write text # UTF8环境使用此段代码
  #f.write text.encode("GB18030") # 简体中文环境(中文简体版Windows)使用此段代码
end

代码清单 22.2:cut_kongyiji.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
htmlfile = "kongyiji.html"
textfile = "kongyiji.txt"

html = File.read(htmlfile) #html = File.read(htmlfile, mode: "rb:GB18030") # 译者注:简体中文环境(中文简体版Windows)使用此段代码

File.open(textfile, "w") do |f|
  in_header = true
  html.each_line do |line|
    if in_header && /<div class="post-text">/ !~ line
      next
    else
      in_header = false
    end
    break if /<div class="copyright-announce">/ =~ line
    f.write line
  end
end

代码清单 22.3:cut_kongyiji2.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
require 'cgi/util'
htmlfile = "kongyiji.html"
textfile = "kongyiji.txt"

html = File.read(htmlfile) #html = File.read(htmlfile, mode: "rb:GB18030") # 译者注:简体中文环境(中文简体版Windows)使用此段代码

File.open(textfile, "w") do |f|
  in_header = true
  html.each_line do |line|
    if in_header && /<div class="post-text">/ !~ line
      next
    else
      in_header = false
    end
    break if /<div class="copyright-announce">/ =~ line
    line.gsub!(/<[^>]+>/, '')
    esc_line = CGI.unescapeHTML(line)
    f.write esc_line
  end
end

代码清单 22.4:simple_grep.rb

1
2
3
4
5
6
7
8
9
10
pattern = Regexp.new(ARGV[0])
filename = ARGV[1]

File.open(filename) do |file|
  file.each_line do |line|
    if pattern =~ line
      print line
    end
  end
end

代码清单 22.5:simple_scan.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pattern = Regexp.new(ARGV[0])
filename = ARGV[1]

count = 0
File.open(filename) do |file|
  file.each_line do |line|
    if pattern =~ line
      line.scan(pattern) do |s|
        count += 1
      end
      print line
    end
  end
end
puts "count: #{count}"

代码清单 22.6:simple_count.rb

1
2
3
4
5
6
7
8
pattern = Regexp.new(ARGV[0])
filename = ARGV[1]

count = 0
File.read(filename).scan(pattern) do |s|
  count += 1
end
puts "count: #{count}"

代码清单 22.7:simple_match.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pattern = Regexp.new(ARGV[0])
filename = ARGV[1]

count = 0
File.open(filename) do |file|
  file.each_line do |line|
    if pattern =~ line
      line.scan(pattern) do |s|
        count += 1
      end
      print line.gsub(pattern){|str| "<<#{str}>>"}
    end
  end
end
puts "count: #{count}"

代码清单 22.8:simple_match2.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
pattern = Regexp.new("(.{10})("+ARGV[0]+")(.{10})")
filename = ARGV[1]

count = 0
File.open(filename) do |file|
  file.each_line do |line|
    line.scan(pattern) do |s|
      puts "#{s[0]}<<#{s[1]}>>#{s[2]}"
      count += 1
    end
  end
end
puts "count: #{count}"

代码清单 22.9:simple_match3.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pattern = Regexp.new(ARGV[0])
filename = ARGV[1]

count = 0
File.open(filename) do |file|
  file.each_line do |line|
    line.scan(pattern) do |s|
      pre = $`
      post = $'
      puts "#{pre[-10,10]}<<#{s}>>#{post[0,10]}"
      count += 1
    end
  end
end
puts "count: #{count}"

代码清单 22.10:simple_match4.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pattern = Regexp.new(ARGV[0])
filename = ARGV[1]
len = ARGV[2].to_i

count = 0
File.open(filename) do |file|
  file.each_line do |line|
    line.scan(pattern) do |s|
      pre = $`
      post = $'
      puts "#{pre[-len,len]}<<#{s}>>#{post[0,len]}"
      count += 1
    end
  end
end
puts "count: #{count}"

第 23 章

代码清单 23.1:read_csv.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
require "csv"             # 使用csv库

code = ARGV[0]            # 读取参数
start_time = Time.now     # 获取操作开始时间

# 将Shift_jis转换为UTF-8后打开CSV文件
CSV.open("KEN_ALL.CSV", "r:Shift_jis:UTF-8") do |csv|
  csv.each do |record|
    # 邮政编码与参数指定的一致则输出该记录
    puts record.join(" ") if record[2] == code
  end
end
p Time.now - start_time   # 输出结束时间与开始时间之差

代码清单 23.2:jzipcode.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
require 'sqlite3'
require "csv"

class JZipCode
  CSV_COLUMN = {code: 2, pref: 6, city: 7, addr: 8}

  def initialize(dbfile)
    @dbfile = dbfile
  end

  def create(zipfile)
    return if File.exist?(@dbfile)
    SQLite3::Database.open(@dbfile) do |db|
      db.execute(<<-SQL)
        CREATE TABLE IF NOT EXISTS zip_codes
          (code TEXT, pref TEXT, city TEXT, addr TEXT, alladdr TEXT)
      SQL
      db.execute("BEGIN TRANSACTION")
      CSV.open(zipfile, "r:Shift_JIS:UTF-8") do |csv|
        csv.each do |rec|
          data = Hash.new
          CSV_COLUMN.each{|key, index| data[key] = rec[index] }
          data[:alladdr] = data[:pref] + data[:city] + data[:addr]
          db.execute(<<-SQL, data)
            INSERT INTO zip_codes VALUES
              (:code, :pref, :city, :addr, :alladdr)
          SQL
        end
      end
      db.execute("COMMIT TRANSACTION")
    end
    return true
  end

  def find_by_code(code)
    ret = []
    SQLite3::Database.open(@dbfile) do |db|
      db.execute(<<-SQL, code){|row| ret << row.join(" ") }
        SELECT code, alladdr
          FROM zip_codes
         WHERE code = ?
      SQL
    end
    return ret.map{|line| line + "\n"}.join
  end

  def find_by_address(addr)
    ret = []
    SQLite3::Database.open(@dbfile) do |db|
      like = "%#{addr}%"
      db.execute(<<-SQL, like){|row| ret << row.join(" ") }
        SELECT code, alladdr
          FROM zip_codes
         WHERE alladdr LIKE ?
      SQL
    end
    return ret.map{|line| line + "\n"}.join
  end
end

if __FILE__ == $0
  dbfile = "jzipcode.db"
  jzipcode = JZipCode.new(dbfile)
  jzipcode.create("KEN_ALL.CSV")
  puts jzipcode.find_by_code("3210202")
  puts jzipcode.find_by_address("猫屋敷")
end