数组(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六个半小时,现在有点疲惫,理解力还好,但由于疲惫不再想继续了。休息一下:)