Ruby Quick Start (4) -- Array, String, Closure

《Ruby语言入门教程v1.0》笔记(4)


第八章 Ruby中的数组、字符串、闭包

1. Ruby中的数组

1.1 建立数组

arr1 = []
arr2 = Array.new
arr3 = ['4 ', '5 ', '6 ']
print arr1, "\n"
print arr2, "\n"
print arr3, "\n"


1.2 访问数组元素

arr = [3, 4, 5, 6, 7, 8, 9]
puts arr[0]    # 3
puts arr.first    # 3
puts arr[arr.length-1]    # 9
puts arr[arr.size-1]    # 9
puts arr.last    # 9
puts arr[-1]    # 9
puts arr[-2]    # 8
print arr[1..3] ,"\n"    # 456
print arr[-3, 2] ,"\n"    # 78

数组的索引从 0 开始,一直到数组的长度减去 1

负数表示从数组末尾开始的索引;

用一对数字来索引数组,第一个数字表示开始位置,第二数字表示从开始位置起的元素数目。



1.3 增加、删除数组元素

Ruby 的数组大小是动态的,你能够随时增加、删除数组元素。

print arr.join(", "), "\n" 意思是:将数组 arr 转换成字符串输出,用”,“隔开每个元素,并且换行。

arr = [4, 5, 6]
print arr.join(", "), "\n"    # 4, 5, 6
arr[4] = "m"        # 把 4 号索引位置元素赋值为"m"
print arr.join(", "), "\n"    # 4, 5, 6, , m
print arr[3], "\n"    # 打印 3 号索引位置元素 #nil
arr.delete_at(3)    # 删除 3 号索引位置元素
print arr.join(", "), "\n"    # 4, 5, 6, m
arr[2] = ["a","b","c"]        # 把 2 号索引位置元素赋值为["a","b","c"]
print arr.join(", "), "\n"    # 4, 5, a, b, c, m
print arr[2], "\n"        # 打印 2 号索引位置元素 #abc
arr[0..1] = [7, "h", "b"]        # 把 0..1 号元素替换为 7, "h", "b"
print arr.join(", "), "\n"        # 7, h, b, a, b, c, m
arr.push("b" )        # 加入元素"b"
print arr.join(", "), "\n"        # 7, h, b, a, b, c, m, b
arr.delete(["a", "b", "c"])        # 删除元素["a","b","c"]
print arr.join(", "), "\n"        # 7, h, b, m, b
arr.delete("b")            # 删除所有元素"b"
print arr.join(", "), "\n"    # 7, h, m
arr.insert(3, "d")        # 在 3 号索引位置插入元素"d"
print arr.join(", "), "\n"        # 7, h, m, d
arr << "f" << 2            # 加入元素"f";加入元素 2
print arr.join(", "), "\n"    # 7, h, m, d, f, 2
arr.pop            # 删除尾元素
print arr.join(", "), "\n"    # 7, h, m, d, f
arr.shift            # 删除首元素
print arr.join(", "), "\n"    # h, m, d, f
arr.clear            # 清空数组 arr
print arr.join(", "), "\n"    #


1.4 数组运算

aaaa = [" aa ", 4, 5, " bb "]
bbbb = [4, 1, 3, 2, 5]
print aaaa + bbbb, "\n"    #  aa 45 bb 41325
print aaaa * 2, "\n"        #  aa 45 bb aa 45 bb
print bbbb - aaaa, "\n"    # 132
# 并运算;交运算
print aaaa | bbbb, "\n"    #  aa 45 bb 132
print aaaa & bbbb, "\n"    # 45
# 排序;倒置
print bbbb.sort, "\n"        # 12345
print aaaa.reverse, "\n"        #  bb 54 aa



2. Ruby中的字符串

2.1 字符串的创建

字符串是 String 类的对象,一般使用字面值来创建。

str1 = 'this is str1'
str2 = "this is str2"
str3 = %q/this is str3/
str4 = %Q/this is str4/
str5 = << OK_str
    Here is string document, str5
        line one;
        line two;
        line three.
    OK
OK_str
puts str3
puts str4
puts str5

输出结果:

this is str3
this is str4
Here is string document, str5
line one;
line two;
line three.
OK

%q 用来生成单引号字符串;%Q 用来生成双引号字符串。

%q 或者 %Q 后面跟着的是分隔符,可以是配对的! !/ /< >( )[ ]{ };等等。

str5 是一个字符串文档,从 << 和文档结束符的下一行开始,直到遇到一个放置在行首的文档结束符,结束整个字符串文档。

一个数组可以用 join 方法转换成字符串,join() 内的参数也是一个字符串,用来分隔数组的每个元素,例如:arr.join(", ")



2.2 字符串操作

字符串既然是 String 类的对象, String 类的方法你都可以使用在字符串变量上,String 类的方法非常多,下面略举几例。

str = ' this' + " is"
str += " you"
str << " string" << " ."
puts str*2    # this is you string . this is you string .
puts str[-12, 12]    # you string .


2.3 字符串转义

双引号括起来的字符串会有转义,例如:”\n“ 表示换行。还有一些其它的转义符号,比如制表符之类。

str = " this is you string ."
puts str * 2        # this is you string . this is you string .
str = " this is you string .\n"
puts str*2        # this is you string .
this is you string .
str = " \tthis is you string ."
puts str        # this is you string .
str = ' this\'s you string .\n'
puts str        # this's you string .\n

单引号括起来的字符串并不会对字符串作任何解释,你看到的是什么便是什么,有一个例外:单引号字符串里的 单引号 需要转义。



2.4 字符串内嵌表达式

在双引号扩起来的字符串中, 不仅可以使用各种转义符, 而且可以放置任意的Ruby 表达式在 #{ } 之中,这些表达式在使用这个字符串的时候被计算出值,然后放入字符串。

def hello(name)
  " Welcome, #{name} !"
end
puts hello("Yilin")    # Welcome, Yilin !
puts hello("Ben")        # Welcome, Ben !

字符串内嵌表达式,使得你能够更加灵活地组织代码,表现出更强、更多的动态特性。



3. Ruby中的正则表达式(暂时略过)

Ruby是一种强烈而灵活地支持正则表达式的语言。

正则表达式强大,但是枯燥。有一个办法,就是等你需要用的时候再来学习



4. 迭代器、代码块、闭包

(1..9).each {|i| print i if i < 7}

迭代器 each 是数组类的一个方法;大括号{ }里的代码是代码块,简称

你可以用大括号{ }将代码组织成块,也可以用 do...end 将代码组织成块。

大括号{ }的优先级高于 do...end

我们来写一个最简单的块:

def one_block
  yield
  yield
  yield
end
one_block { puts "This is a block. " }

输出结果:

This is a block.
This is a block.
This is a block.

从上面的程序可以看到调用一个块要用关键字 yield。每一次 yield,块就被调用一次。

yield 还可以带参数调用块:

def one_block
  for num in 1..3
    yield(num)
  end
end
one_block do |i|
  puts "This is block #{i}. "
end

输出结果:

This is block 1.
This is block 2.
This is block 3.

一个块可以接收 yield 传来的参数,还可以将结果返回给调用它的方法。

到目前为止,实在看不出使用代码块的优势,可以把块里的代码直接写在方法中。

如果我们还没有决定块里写什么代码, 又或者块里的代码会随着不同的情形而变化, 那么就看出代码块的灵活性了。

def do_something
  yield
end
do_something do
  (1..9).each {|i| print i if i<5}
  puts
end
do_something do
  3.times { print "Hi!" }
  puts
end

输出结果:

1234
Hi!Hi!Hi!

其中的技巧:先写出方法的大致框架,调用方法的时候才告诉方法要做什么。

虽然与代码块有关联的方法不都是迭代器,但是, 迭代器确实是一个与代码块有关联的方法。让我们为数组类增加一个迭代器 one_by_one

class Array
  def one_by_one
    for i in 0...size
      yield(self[i])
    end
    puts
  end
end
arr = [1, 3, 5, 7, 9]
arr.one_by_one {|k| print k, ", "}    # 1, 3, 5, 7, 9,
arr.one_by_one {|h| print h * h, ", "}    # 1, 9, 25, 49, 81,

代码块是一段代码,相当于一个匿名方法,被调用它的方法所调用。

如果我们不仅仅想调用代码块,还想把代码块作为参数传递给其它方法,就要使用闭包了。

闭包也是一段代码,一个代码块,而且能够共享其它方法的局部变量

def method(pr)
  puts pr.call(7)
end
oneProc = proc{|k| k *= 3 }
method(oneProc)

输出结果:

21

来看一个闭包共享其它方法局部变量的例子:

def method(n)
  return proc{|i| n += i }
end
oneProc = method(3)
puts oneProc.call(9)    # 12
puts oneProc.call(5)    # 17, 注意不是8!

方法 method 返回一个 Proc 对象,这个对象引用了这个函数的参数:n 。即使 n 这个参数在闭包被调用时已经不在自己的作用域里了,这个闭包还是可以访问 n 这个参数,并且和方法 method 共同拥有变量 n 。开始的时候,方法 method 的变量 n3oneProc.call(9)的时候,oneProc 更新了变量 n,把 n=12 传回给方法 methodoneProc.call(5)的时候,oneProc 取出方法 method 的变量 n=12,更新为 n=17,传回给方法 method 的同时,也把 n=17 作为自己的返回值输出。