8
、实现稀疏矩阵 有时候我们需要被定义元素很少的数组;它的其余元素可以末被定义( 或者通常为零) 。这称为稀疏矩阵, 历史上它是消耗内存的大户,以致让人们寻找一些间接的方法来实现它。 当然,大多数情况下,Ruby 数组足够有效了,因为典型的现代体系有大量的内存。未赋值元素有值为nil ,它只使用很少几个byte 的存储空间。 另一方面,在前一个数组元素的范围内分派数组元素,也可在两者之间创建nil 元素。例如,如果元素从0 到9 被定义,我们突然赋值给第1000 个元素,则我们会有效地获得元素从10 到999 ,而它们所带的是nil 值。如果这不可以接受,你也以考虑另一种选择。 然而,对于其它解决办法,我们不认为它会做的比数组更好。如果你真的需要稀疏矩阵,哈希表可以是最好的解决办法。 9 、用数组实现Set 数据集 大多数语言并不直接实现Set 数据集(Pascal 是个例外) 。但是,Ruby 数组有很多特征可以使它们被用做Set 数据集。我们在介绍它并添加一些自己的东西。( 译注:1.8 中有Set 。) 首先,数组可以持有重复条目。如果你想将数组视为Set 数据集,你可以移除所有重复的条目( 使用uniq 或uniq!) 。 Set 数据集的两个基本操作是并集和交集。它们分别由| 和& 操作符来完成。为了一致数据集不包含重复,任何重复将被移除。( 在大多数语言中,如果使用数组的并集和交集可能与你希望的不同。) 这儿是个例子: a = [1, 2, 3, 4, 5] b = [3, 4, 5, 6, 7] c = a | b # [1, 2, 3, 4, 5, 6, 7] d = a & b # [3, 4, 5] # Duplicates are removed... e = [1, 2, 2, 3, 4] f = [2, 2, 3, 4, 5] g = e & f # [2, 3, 4] 串联操作符+ 也可用于数据集Set 的并集,但是它不排除重复。- 方法是" 数据集区别" 操作符将产生包含第一个数据集的所有元素,而不含在第二个数据集中出现的元素。这儿是个例子: a = [1, 2, 3, 4, 5] b = [4, 5, 6, 7] c = a - b # [1, 2, 3] # 注意额外的条目6 和7 是不相关的。 要" 堆积" 数据集,你可以使用 |= 操作符,像你想一样, a |= b 只是简单地等于 a = a | b 。同样 &= 可以次第地减少("narrow down") 数据集的元素。 是无排它的- 或对数组定义的,但我们可以很容易地实现我们自己的。数据集术语中,没有排它性- 或用于数组的定义,但我们可以非常容易地做出自己的。在数据集条目中,这对应于两个数据集的并集元素而不是交集。这儿个例子: class Array def ^(other) (self | other) - (self & other) end end x = [1, 2, 3, 4, 5] y = [3, 4, 5, 6, 7] z = x ^ y # [1, 2, 6, 7] 要检查数据集中否有此元素,我们可使用方法include? 或member?( 本质上是Comparable 模块保的别名) ,像这样: x = [1, 2, 3] if x.include? 2 puts "yes" # Prints "yes" else puts "no" end 当然,字面值<< 来自于我们使用的数学,这个操作符类似于希腊第五个字母代表数据集的成员。感觉上它反向的是左面而不是右面;我们不要问" 这个元素是这个数据集的吗?" 而是关心" 这个数据集包含这个元素吗?" 许多人根本就不关心这个,如果你使用Pascal 或Python( 或者你是数学爱好者) ,你可能想使用不同的方式。我们这有两个选择: class Object def in(other) other.include? self end end x = [1, 2, 3] if 2.in x puts "yes" # Prints "yes" else puts "no" end 例子有些难看,但至少次序我们很熟悉。至于让它看起来“更像个操作符”,Ruby 的让人不可思义的灵活的解析器允许你写表达式2.in x 代替 2 .in x 或 2. in x ,你可能会想的更远。 前面这些都不是标准的,我们可能基于这样的目的重写操作符比如<= 。但是,像这样的事情做起来应更小心。 这儿谈到了用于Ruby 的像Python( 或Pascal) 这样的操作符。但是,现在不会谈得太多。 我们会告诉你如何做而不管一个数据集是子数据集还是什么的超集。这儿没有内建的方法,但我们可以做像Listing3.4 中的那样。 Listing 3.4 Subset and Superset class Array def subset?(other) #self 是other 的子集吗 self.each do |x| if !(other.include? x) # x 包含在other 内吗 return false # 只要有一个元素不在就返回false 。 end end true end def superset?(other) # 是超集吗 other.subset?(self) end end a = [1, 2, 3, 4] b = [2, 3] c = [2, 3, 4, 5] flag1 = c.subset? a # false flag2 = b.subset? a # true flag3 = c.superset? b # true 注意,我们选择了“自然”次序,也就是说, x.subset? y 意味着“x 是y 的子集吗”。反之亦然。 要查出null 数据集( 或空数据集) ,我们只简单地查看空数组。Empty? 方法会做这些。 数据集否定( 或补集) 的概念依赖于通用数据集的概念。因为实际过程中这将从一个应用或状态到另一个应用或状态,最好的方式是简单地定义一个通用数据集,然后再做交集,像下面这样: universe = [1, 2, 3, 4, 5, 6] a = [2, 3] b = universe - a # complement of a = [1, 4, 5, 6] 当然,如果你认为这很主要,你可定义一个一元操作符( 如- 或~) 来做这些。 你可能通过对数组的迭代而达到对数据集的迭代。唯一的不同是元素没有次序,那可能不是你想要的。随机的迭代可以看"Iterating over an Array 。" 最后,我们可能时想要计算一个数据集的平方数据集。这可简单地所有可能的子数据集(包括空集和原始集合本身)。那么熟悉离散数学的,特别是组合数学,将看到这些必须是2n的子集。依照Listing3.5我们可以生成powerset。 Listing 3.5 Powerset of a Set class Array def powerset num = 2**size ps = Array.new(num, []) self.each_index do |i| a = 2**i b = 2**(i+1) - 1 j = 0 while j < num-1 for j in j+a..j+b ps[j] += [self ] end j += 1 end end ps end end x = [1, 2, 3] y = x.powerset # y is now: # [[], [1], [2], [1,2], [3], [1,3], [2,3], [1,2,3]] 译注:文档中的定义 1. array.uniq → an_array 2. array.uniq! → array or nil 3. array & other_array 4. array | other_array → an_array 5. array + other_array → an_array 6. array - other_array → an_array 7. array.include?(obj) → true or false 8. enum.include?(obj) => true or false 9. enum.member?(obj) => true or false 10. array.empty? → true or false uniq会删除数组中的重复元素后生成新数组并返回它。剩下的元素会向前移动。uniq!具有破环性,若进行了删除则返回self,若没有删除则返回nil。 &,| 数据集Set的交集(并集)运算。将同属于两个数组的元素重组为一个新数组并返回该数组。重复元素将被清除。若other并非数组的话,将尝试隐式地调用to_ary方法进行变换。 + 将self和other的内容连起来后生成新数组并返回该数组。若other并非数组时则使用other.to_ary的返回值。若该返回值依然不是数组时,则引发TypeError异常。 - 集合的补集运算。从self中删除other的元素后生成一个新数组并返回该数组。重复元素将被清除。若other并非数组的话,将尝试隐式地调用to_ary方法进行变换。ruby 1.8 特性: 重复元素将被保留。 [ 1, 1, 2, 2, 3, 3, 4, 5 ] - [ 1, 2, 4 ] #=> [ 3, 3, 5 ] 译注:1.8中已提供了Set库,我们下面来看一下它有什么方法,Set类包含了Enumerable模块。 Set实现了无序的不包含重复值的集合。它混杂了数组的直观操作功能和哈希表的快速搜索。 有几个方法接受任何实现each的Enumerable对象以达到更大的灵活性: new, replace, merge, subtract, |, &, -, ^ 。 每对元素的等同性由Object#eql?和Object#hash决定,因为Set使用哈希表做为存储。 最后,如果你使用类Set,你也可以使用Enumerable#to_set带来的便利。 例子: require 'set' s1 = Set.new [1, 2] # -> #<Set: {1, 2}> s2 = [1, 2].to_set # -> #<Set: {1, 2}> s1 == s2 # -> true s1.add("foo") # -> #<Set: {1, 2, "foo"}> s1.merge([2, 6]) # -> #<Set: {6, 1, 2, "foo"}> s1.subset? s2 # -> false s2.subset? s1 # -> true 类方法: 1. [](*ary) 2. new(enum = nil) {|o| ...} 实例方法: 1. &(enum) intersection(enum) 交集 2. +(enum) |(enum) union(enum) 并集 3. -(enum) difference(enum) 差集 4. ==(set) 5. ^(enum) 排它操作,等于((set | enum) - (set & enum)). 6. add(o) add?(o) <<(o) 添加对象o到集中。 7. classify( {|o| ...} 8. clear() 9. collect!() {|o| ...} map!() 10. delete(o) delete?(o) 从集合中删除元素o。 11. delete_if() {|o| ...} 删除块计算后为true的每个元素。 12. divide(&func) 13. each() {|o| ...} 为集中每个元素调用块,传递元素做为参数。 14. empty?() 15. flatten() 16. flatten!() 17. include?(o) member?(o) 集中包含o对象吗? 18. initialize_copy(orig) 拷贝内部Hash。 19. inspect() 20. length() size() 21. merge(enum) 将enumerable中的元素并入集中。 [ 本帖最后由 blackanger 于 2007-5-20 17:54 编辑 ] |
Ruby操纵数据结构(二)
最新推荐文章于 2020-04-21 14:15:11 发布