🦄 2024 独立开发者训练营,一起创业!查看介绍 / 立即报名(剩余10个优惠名额) →

Day 9:Ruby 集合与容器对象

数组(array),散列(hash)。array 里的项目有顺序,你可以用索引号得到对应的项目。hash 里的项目是成对的,一对里有个 key 还有个对应的 value。得到项目的 value 可以使用项目的 key 。

任何的 Ruby 对象都可以作为 hash 的 key 或者 value ,不过注意 key 在 hash 里一定是唯一的。hash 在其它的语言上也叫 dictionaries 或者 associative arrays  。

array 与 hash 很像。在某种意义上 array 就是 hash,只不过它的 key 是连续的整数。hash 有某种意义上也是 array,只不过索引可以是任何东西。

做个实验:

array = ["ruby", "diamond", "emerald"]
hash = { 0 => "ruby", 1 => "diamond", 2 => "emerald" }
puts array[0] # ruby
puts hash[0] # ruby

再做个实验:

hash = { "red" => "ruby", "white" => "diamond", "green" => "emerald" }
hash.each.with_index {|(key,value),i|
  puts "Pair #{i} is: #{key}/#{value}"
}

输出的是:

Pair 0 is: red/ruby
Pair 1 is: white/diamond
Pair 2 is: green/emerald

数组

创建新数组

有几种方法:

  • Array.new 方法
  • 字面构造器:[]
  • Array 顶级方法
  • %w{...},%i{...}

Array.new

a = Array.new

设置数组的尺寸与内容:

>> Array.new(3)
=> [nil, nil, nil]
>> Array.new(3, "abc")
=> ["abc", "abc", "abc"]

提供一个代码块:

>> n = 0
=> 0
>> Array.new(3) { n += 1; n * 10 }
=> [10, 20, 30]

再做个实验:

>> a = Array.new(3, "abc")
=> ["abc", "abc", "abc"]
>> a[0] << "def"
=> "abcdef"
>> puts a[1]
abcdef
=> nil

上面的 abc 是同一个东西。想不同的话需要这样:

Array.new(3) { "abc" }

字面构造器

数组字面构造器是:[]

a = []

创建的时候可以直接往数组里添加对象:

a = [1,2,"three",4,[]]

Array 方法

>> string = 'hello'
=> "hello"
>> string.respond_to?(:to_ary)
=> false
>> string.respond_to?(:to_a)
=> false
>> Array(string)
=> ["hello"]
>> def string.to_a
>>   split(//)
>> end
=> :to_a
>> Array(string)
=> ["h", "e", "l", "l", "o"]

%w 与 %W

>> %w{ I Love You }
=> ["I", "Love", "You"]

%W 可以用双引号解析。

%i 与 %I

符号数组。

>> %i{ a b c }
=> [:a, :b, :c]
>> w = "love"
=> "love"
>> %I{"#{w}"}
=> [:"\"love\""]

try_convert

>> obj = Object.new
=> #<Object:0x007fa7e39e0ce0>
>> Array.try_convert(obj)
=> nil
>> def obj.to_ary
>> [1,2,3]
>> end
=> :to_ary
>> Array.try_convert(obj)
=> [1, 2, 3]
>> def obj.to_ary
>> "not an array!"
>> end
=> :to_ary
>> Array.try_convert(obj)
TypeError: can't convert Object to Array (Object#to_ary gives String)
 from (irb):199:in `try_convert'
 from (irb):199
 from /usr/local/bin/irb:11:in `<main>'

插入,取回,删除数组项目

插入项目:

a = []
a[0] = "first"

a[0] 是一种语法糖, 实际上是一个方法的调用,脱了它的糖衣应该是:

a.[]=(0, "first")

取回项目:

>> a = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]
>> p a[2]
3
=> 3

a[2] 也是语法糖,脱了糖衣应该是:

a.[](2)

一次多个项目

>> a = ["red", "orange", "yellow", "purple", "gray", "indigo", "violet"]
=> ["red", "orange", "yellow", "purple", "gray", "indigo", "violet"]
>> a[3,2]
=> ["purple", "gray"]
>> a[3,2] = "green", "blue"
=> ["green", "blue"]
>> a
=> ["red", "orange", "yellow", "green", "blue", "indigo", "violet"]

slice 方法跟 [] 一样用。

>> a.values_at(0,3)
=> ["red", "green"]

数组的开头与结尾

unshift 在数组的最前面添加个项目:

>> a = [1,2,3]
=> [1, 2, 3]
>> a.unshift(0)
=> [0, 1, 2, 3]

push 会在数组最后添加项目:

>> a = [1,2,3]
=> [1, 2, 3]
>> a.push(4)
=> [1, 2, 3, 4]

也可以用 << ,像这样:

>> a << 5
=> [1, 2, 3, 4, 5]

push 可以添加多个项目。

用 shift 与 pop 删除数组里的项目:

>> a = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]
>> popped = a.pop
=> 5
>> puts popped
5
=> nil
>> p a
[1, 2, 3, 4]
=> [1, 2, 3, 4]
>> shifted = a.shift
=> 1
>> puts shifted
1
=> nil
>> p a
[2, 3, 4]
=> [2, 3, 4]

合并数组

先试试 concat:

>> [1,2,3].concat([4,5,6])
=> [1, 2, 3, 4, 5, 6]

concat 会改变源数组,如果你想得到一个新的合并以后的数组,可以使用 + :

>> a = [1,2,3]
=> [1, 2, 3]
>> b = a + [4,5,6]
=> [1, 2, 3, 4, 5, 6]
>> a
=> [1, 2, 3]

用 replace 替换:

>> a = [1,2,3]
=> [1, 2, 3]
>> a.replace([4,5,6])
=> [4, 5, 6]
>> a
=> [4, 5, 6]

再做个实验理解一下 = 与 replace 的区别:

>> a = [1,2,3]
=> [1, 2, 3]
>> b = a
=> [1, 2, 3]
>> a.replace([4,5,6])
=> [4, 5, 6]
>> b
=> [4, 5, 6]
>> a = [7,8,9]
=> [7, 8, 9]
>> b
=> [4, 5, 6]

数组转型

flatten,去掉数组里的层级,在参数里可以指定要去掉的层级数:

>> array = [1,2,[3,4[5]],[6,[7,8]]]
=> [1, 2, [3, 0], [6, [7, 8]]]
>> array.flatten
=> [1, 2, 3, 0, 6, 7, 8]
>> array.flatten(1)
=> [1, 2, 3, 0, 6, [7, 8]]
>> array.flatten(2)
=> [1, 2, 3, 0, 6, 7, 8]

reverse 颠倒:

>> [1,2,3].reverse
=> [3, 2, 1]

join:

>> ["abc", "def", 123].join
=> "abcdef123"

join 的参数:

>> ["abc", "def", 123].join(", ")
=> "abc, def, 123"

还有种合并的方法:

>> a = %w{ one two three }
=> ["one", "two", "three"]
>> a * "-"
=> "one-two-three"

去掉重复:

>> [1,2,3,3].uniq
=> [1, 2, 3]

去掉 nil:

>> zip_codes = ["06511", "08902", "08902", nil, "10027",
"08902", nil, "06511"]
=> ["06511", "08902", "08902", nil, "10027", "08902", nil, "06511"]
>> zip_codes.compact
=> ["06511", "08902", "08902", "10027", "08902", "06511"]

数组查询

  • a.size,a.length:项目的数量
  • a.empty?:空白吗?
  • a.include?(item):有某个项目吗?
  • a.count(item):这个项目有多少?
  • a.first(n=1):前几个项目
  • a.last(n=1):后几个项目
  • a.sample(n=1):随机项目

实验:

>> a
=> ["one", "two", "three"]
>> a.first(n=1)
=> ["one"]
>> a.first(n=2)
=> ["one", "two"]
>> a.last(n=1)
=> ["three"]
>> a.sample(n=1)
=> ["two"]
>> a.sample(n=1)
=> ["one"]

hash

先做个实验,创建一个 hash,根据用户的输入,得到地区的缩写:

p_hash = {
  "北京" => "BJ",
  "上海" => "SH",
  "广州" => "GZ"
}

print "输入地区的名字:"
p = gets.chomp
abbr = p_hash[p]
puts "地区的缩写是 #{abbr}"

创建 hash

几个方法可以创建 hash:

  • 字面构造器:{}
  • Hash.new 方法
  • Hash.[] 方法
  • Hash 顶级方法

创建字面 hash

大括号里面是 hash 项目,每个项目都有 key ,还有对应的 value,它们中间用 => 符号连接,项目与项目之间用逗号分隔开。

Hash.new

Hash.[] 类方法

>> Hash["北京", "BJ", "上海", "SH"]
=> {"北京"=>"BJ", "上海"=>"SH"}
>> Hash[ [[1,2], [3,4], [5,6]] ]
=> {1=>2, 3=>4, 5=>6}

Hash 方法

Hash 方法有点特别的行为。调用的时候给它一个空白的数组([ ])或 nil,它会返回一个空白的 hash。不然它会在参数上调用 to_hash ,如果数组没 to_hash 方法,就会触发错误。

插入,取回,删除 hash 项目

添加新项目到 hash 里

用 []= 方法:

p_hash["山东"] = "SD"

脱掉糖衣应该是这样的:

p_hash.[]=("山东", "SD")

store 也可以:

p_hash.store("山东", "SD")

做个实验:

>> h = Hash.new
=> {}
>> h["a"] = 1
=> 1
>> h["a"] = 2
=> 2
>> puts h["a"]
2
=> nil

获取值

用 [] 方法:

>> p_hash["北京"]
=> "BJ"

用 fetch 方法也行:

>> p_hash.fetch("北京")
=> "BJ"

[] 与 fetch 的区别是,fetch 不存在的 key 的时候会出现异常,而 [] 会返回 nil 。

>> p_hash.fetch("云南", "Unknown state") 
=> "Unknown state"

多个 key :

>> p_hash.values_at("北京", "上海")
=> ["BJ", "SH"]

默认 hash 值与行为

这是默认的情况:

>> h = Hash.new
=> {}
>> h["no such key!"]
=> nil

设置默认的值:

>> h = Hash.new(0)
=> {}
>> h["no such key!"]
=> 0

这样试一下:

h = Hash.new {|hash,key| hash[key] = 0 }

再这样:

>> h["new key!"]
=> 0
>> h
=> {"new key!"=>0}

合并 hash

update,有破坏性的合并:

>> h1 = {"Smith" => "John", "Jones" => "Jane" }
=> {"Smith"=>"John", "Jones"=>"Jane"}
>> h2 = {"Smith" => "Jim"}
=> {"Smith"=>"Jim"}
>> h1.update(h2)
=> {"Smith"=>"Jim", "Jones"=>"Jane"}
>> puts h1["Smith"]
Jim
=> nil

merge,不具破坏性的合并:

>> h1 = {"Smith" => "John", "Jones" => "Jane" }
=> {"Smith"=>"John", "Jones"=>"Jane"}
>> h2 = {"Smith" => "Jim"}
=> {"Smith"=>"Jim"}
>> h3 = h1.merge(h2)
=> {"Smith"=>"Jim", "Jones"=>"Jane"}
>> p h1["Smith"]
"John"
=> "John"

hash 变型

选择与拒绝元素

select:

>> h = Hash[1,2,3,4,5,6]
=> {1=>2, 3=>4, 5=>6}
>> h.select {|k,v| k > 1}
=> {3=>4, 5=>6}

reject:

>> h.reject {|k,v| k > 1}
=> {1=>2}

keep_if:

>> h
=> {1=>2, 3=>4, 5=>6}
>> h.keep_if {|k,v| k > 1}
=> {3=>4, 5=>6}
>> h
=> {3=>4, 5=>6}

delete_if:

>> h
=> {3=>4, 5=>6}
>> h.delete_if {|k,v| k > 3}
=> {3=>4}
>> h
=> {3=>4}

颠倒

invert:

>> h = { 1 => "one", 2 => "two" }
=> {1=>"one", 2=>"two"}
>> h.invert
=> {"one"=>1, "two"=>2}

注意 key 必须得是唯一的。

>> h = { 1 => "one", 2 => "more than 1", 3 => "more than 1" }
=> {1=>"one", 2=>"more than 1", 3=>"more than 1"}
>> h.invert
=> {"one"=>1, "more than 1"=>3}

清空

clear:

>> {1 => "one", 2 => "two"}.clear
=> {}

替换

>> { 1 => "one", 2 => "two" }.replace({ 10 => "ten", 20 => "twenty"})
=> {10 => "ten", 20 => "twenty"}

查询 hash

  • h.has_key?(1)
  • h.include?(1)
  • h.key?(1)
  • h.member?(1)
  • h.has_value?("three")
  • h.value?("three")
  • h.empty?
  • h.size

hash 作为方法的参数

参数列表里的最后一个参数是个 hash,不用 {} 了。

add_to_city_datebase("New York City",
  state: "New York",
  population: 7000000,
  nickname: "Big Apple"
)

方法应该像这样定义:

def add_to_city_datebase(name, info)
  c = City.new
  c.name = name
  c.state = info[:state]
  c.population = info[:population]
  # ...

命名的参数

定义方法的时候这么干:

>> def m(a:, b:)
>>   p a,b
>> end
=> :m
>> m(a: 1, b: 2)
1
2
=> [1, 2]

默认参数:

>> def m(a: 1, b: 2)
>>   p a,b
>> end
=> :m
>> self.m
1
2
=> [1, 2]
>> m(a:10)
10
2
=> [10, 2]

你猜猜 :

>> def m(a: 1, b: 2, **c)
>> p a,b,c
>> end
=> :m
>> m(x: 1, y: 2)
1
2
{:x=>1, :y=>2}
=> [1, 2, {:x=>1, :y=>2}]
>> def m(x, y, *z, a: 1, b:, **c, &block)
>>   p x,y,z,a,b,c
>> end
=> :m
>> m(1,2,3,4,5,b:10,p:20,q:30)
1
2
[3, 4, 5]
1
10
{:p=>20, :q=>30}
=> [1, 2, [3, 4, 5], 1, 10, {:p=>20, :q=>30}]

Range

Range:范围

  • 包含(inclusion)—— 给的值在一个范围里吗?
  • 枚举(enumeration)—— 把范围可获取的单立项目的集合

创建一个 range

Range.new:

>> r = Range.new(1, 100)
=> 1..100

上面创建的是 inclusive range,再创建一个 exclusive range,用三个点:

>> r = 1...100
=> 1...100

Range-inclusion 逻辑

begin 与 end

>> r = 1..10
=> 1..10
>> r.begin
=> 1
>> r.end
=> 10

exclusive range?

>> r.exclude_end?
=> false

cover?

如果给方法的参数大于 range 的起点,小于终点,range 说它 cover 了这个对象。

>> r = "a".."z"
=> "a".."z"
>> r.cover?("a")
=> true
>> r.cover?("abc")
=> true
>> r.cover?("A")
=> false
"a" >= "a"
"a" <= "z"

"abc" >= "a"
"abc" <= "z"

"A" < "a"

不兼容的情况:

>> r.cover?([])
=> false

include?

include? 把 range 看成是一个值的集合。

>> r.include?("a")
=> true
>> r.include?("abc")
=> false
>> r = 1.0..2.0
=> 1.0..2.0
>> r.include?(1.5)
=> true

Set

使用 Set 先得:

require 'set'

set 是一个集合,它里面的项目都是唯一的。项目可以是任何东西,字符串,整数,数组,其它的 set 等。

创建 set

Set.new 构造器创建 set 。

>> require('set')
=> false
>> n = [1,2,3]
=> [1, 2, 3]
>> n_set = Set.new(n)
=> #<Set: {1, 2, 3}>

给构造器提供一个代码块:

>> names = ["David", "Yukihiro", "Amy"]
=> ["David", "Yukihiro", "Amy"]
>> name_set = Set.new(names) {|name| name.upcase}
=> #<Set: {"DAVID", "YUKIHIRO", "AMY"}>

处理 set 元素

>> n = Set.new([1,2])
=> #<Set: {1, 2}>
>> n << 3
=> #<Set: {1, 2, 3}>
>> n << 3
=> #<Set: {1, 2, 3}>
>> n
=> #<Set: {1, 2, 3}>

delete

>> n.delete(2)
=> #<Set: {1, 3}>

add

>> n.add?(2)
=> #<Set: {1, 3, 2}>
>> n
=> #<Set: {1, 3, 2}>
>> n.add?(2)
=> nil

intersection,union,difference

  • intersection,别名 &
  • union,别名 +  与 |
  • difference,别名 -

这些方法都会返回新的 set。

>> n = Set.new([1, 2, 3])
=> #<Set: {1, 2, 3}>
>> n1 = Set.new([3, 4, 5])
=> #<Set: {3, 4, 5}>
>> n - n1
=> #<Set: {1, 2}>
>> n + n1
=> #<Set: {1, 2, 3, 4, 5}>
>> n & n1
=> #<Set: {3}>
>> n | n1
=> #<Set: {1, 2, 3, 4, 5}>

^

>> n ^ n1
=> #<Set: {4, 5, 1, 2}>

merge

>> n
=> #<Set: {1, 2, 3}>
>> n.object_id
=> 70179526750720
>> n.merge([4])
=> #<Set: {1, 2, 3, 4}>
>> n.object_id
=> 70179526750720

跟 hash 合并:

>> n
=> #<Set: {1, 2, 3, 4}>
>> n.merge({"北京" => "BJ"})
=> #<Set: {1, 2, 3, 4, ["北京", "BJ"]}>

subset 与 superset

subset:子集,superset:超集

>> n = Set.new([1, 2, 3])
=> #<Set: {1, 2, 3}>
>> n1 = Set_new([1, 2])
>> n1.subset?(n)
=> true
>> n.superset?(n1)
=> true

proper_subset 与 proper_superset。proper 子集至少要比父级少一个项目,如果两个 set 一样,那它们是彼此的 subset,但不是 proper 子集。

16:02 ***

六个半小时,现在有点疲惫,理解力还好,但由于疲惫不再想继续了。休息一下:)

Ruby
微信好友

用微信扫描二维码,
加我好友。

微信公众号

用微信扫描二维码,
订阅宁皓网公众号。

240746680

用 QQ 扫描二维码,
加入宁皓网 QQ 群。

统计

15260
分钟
0
你学会了
0%
完成

社会化网络

关于

微信订阅号

扫描微信二维码关注宁皓网,每天进步一点