Ruby操纵数据结构(四)

 
8 、转换哈希表为数组

要转换整个哈希表为一个数组,使用to_a 方法。在结果数组中,键将被做为奇数元素( 从零开始) ,值将被做为数组的偶数元素:
h = { "a"=>1,"b"=>2}
h.to_a
# ["a",1,"b",2]
也可以只转换键或值到一个数组中:
h.keys
# ["a","b"]
h.values
# [1,2]

最后,你可以以键的列表为基础从数组中取出值,这要使用indices 方法。在哈希表内工作的很多方法在数组中都有同样的名称:
h = { 1=>"one",2=>"two",3=>"three",4=>"four","cinco"=>"five"}
h.indices(3,"cinco",4)
# ["three","five","four"]
h.indexes(1,3)
# ["one","three"]
译注:文档是这样描述的
hsh.to_a → array
hsh.keys => array
hsh.values => array
hsh.indexes(key, ...) => array
select 的别名。
hsh.indices(key, ...) => array


9 、按标准选择键/ 值对

Hash 类混插了Enumerable 模块,所以你可以使用detect(find),select(find_all),grep,min,mix reject 这些方法,就像在数组中使用的一样。
detect 方法( 别名是find) 查找单个键/ 值对。它接受一个块( 每次将键/ 值对传入) 并返回它计算结果为true 的第一个键/ 值对。这儿是个例子:
names = { "fred"=>"jones","jane"=>"tucker",

"joe"=>"tucker","mary"=>"SMITH"}
# Find a tucker
names.detect { |k,v| v=="tucker" }
# ["joe","tucker"]
# Find a capitalized surname
names.find { |k,v| v==v.upcase }
# ["mary", "SMITH"]
当然,哈希表内的对象多么复杂都可以块内被测试,但是不同类型的比较可能会引起问题。
select( 别名是find_all) 方法返回多个匹配,而不只是单个匹配:
names.select { |k,v| v=="tucker" }
# [["joe", "tucker"], ["jane", "tucker"]]
names.find_all { |k,v| k.count("r")>0}
# [["mary", "SMITH"], ["fred", "jones"]]


10 、排序哈希表

哈希表天生不会按它们键的值或键关联的值来排序。要在哈希表内完成排序,Ruby 将哈希表转换成数组,然后对数组进行排序。结果自然也是个数组:
names = { "Jack"=>"Ruby","Monty"=>" ython",

"Blaise"=>" ascal", "Minnie"=>" erl"}
list = names.sort
# list is now:
# [["Blaise","Pascal"], ["Jack","Ruby"],
#
["Minnie","Perl"], ["Monty","Python"]]
译注:文档是这样描述的
1.
hsh.sort => array
2.
hsh.sort {| a, b | block } => array


11 、合并哈希表

有时候合并哈希表很有用。Ruby update 方法将一个哈希表的所有条目并入到目标哈希表中,并重写任何重复的值:
dict = { "base"=>"foundation", "pedestal"=>"base"}
added = { "base"=>"non-acid", "salt"=>"NaCl"}
dict.update(added)
# { "base"=>"non-acid", "pedestal"=>"base", "salt"=>"NaCl"}
译注:文档是这样描述的
1.
hsh.merge!(other_hash) => hsh
2.
hsh.update(other_hash) => hsh
3.
hsh.merge!(other_hash){|key, oldval, newval| block} => hsh
4.
hsh.update(other_hash){|key, oldval, newval| block} => hsh
合并哈希表的内容。若出现相同索引时,将使用 other 中对应的元素值。ruby 1.7 特性: 若给出了块的话,每当遇到相同索引时都会对块进行计算,然后以块的计算值作为该索引的对应值。此时,索引、self[key] other[key] 将被作为参数传递给该块。
Hash#merge 等同于hash.dup. update. Hash#merge! 则是 Hash#update 的别名.
foo = {1 => 'a', 2 => 'b', 3 => 'c'}
bar = {1 => 'A', 2 => 'B', 3 => 'C'}
p foo.dup.update(bar)
# => {1=>"A", 2=>"B", 3=>"C"}
p foo.dup.update(bar) {|k,v| v}
# => {1=>"a", 2=>"b", 3=>"c"}


12 、用数组创建哈希表

从数组中创建哈希表最容易的方法是使用方括号创建哈希表。只要这个数组有偶数个元素就可以工作。这儿是个例子:
array = [2, 3, 4, 5, 6, 7]
hash = Hash[*array]
# hash is now: { 2=>3, 4=>5, 6=>7}


13 、查找哈希表键的差集或交集

因为哈希表的键可以被当做数组取出,而不同哈希表取出后的数组可以使用Array 类方法& - 来产生键的差别和交集。用each 方法生成的匹配值可以生成第三个哈希表,以表示两个哈希表合并后的结果( 确保可以找到所有键)
a = { "a"=>1,"b"=>2,"z"=>3}
b = { "x"=>99,"y"=>88,"z"=>77}
intersection = a.keys & b.keys
difference = a.keys - b.keys
c = a.dup.update(b)
inter = { }
intersection.each { |k| inter[k]=c[k] }
# inter is { "z"=>77}
diff={ }
difference.each { |k| diff[k]=c[k] }
# diff is { "a"=>1, "b"=>2}


14 、用哈希表做稀疏矩阵

通常我们想确保数组或矩阵不为空。我们可以以常规的方法存储它,但这通常会浪费内存。哈希表提供了更好的方式来只存储实际存在的。
这儿的例子假设不存在的值缺省值为零:
values = Hash.new(0)
values[1001] = 5
values[2010] = 7
values[9237] = 9
x = values[9237]

# 9
y = values[5005]
# 0
例子中很明显,数组被创建子9000 次以上并且未使用元素。这可能是不可接受的。
如果我们想实现两个或更多维数的稀疏矩阵?我们所有做的就是需要使用数组做哈希表的键,像这样:
cube = Hash.new(0)
cube[[2000,2000,2000]] = 2
z = cube[[36,24,36]]
# 0
In this case, we see that literally billions of array elements would need to be created if this three-dimensional array were to be complete.


15 、用重复键实现哈希表

理论上可能会说有重复键的哈希表,不是真正的哈希表。我们不想争辩。调用它你要做什么,当你需要一个数据结构,它提供了灵活方便的哈希表而且还允许重复的键值。这时它就有用了。
我们这儿只提供部分解决。这有两个原因。首先,我们不打算实现我们希望的全部功能,而只一个很好的子数据集。其次,Ruby 的内部工作是可成一个Hash 类的实例的哈希表字面值,甚至我们直接从Hash 类继承。字面值不允许包含重复的值。
但是只要离开哈希表字面值符号,这个问题就可解决。这儿我们实现了一个类,它有"store" (@store) ,它是个简单的哈希表;哈希表内的每个值都是个数组。我们以一种方式控制哈希表的访问,当我们找到我们自己添加的键已存在时,我们添加值给与这个键关联的已存在的数组。
返回的大小是多少?很明显,键/ 值对的真实数量包含重复值。同样,keys 方法返回的值同样潜在地包含着重复的值。迭代器的行为就像期望的那样;像个普通哈希表,被访问的每个对都没有可预见的次序。
除了有用的delete 操作外,我们还实现了delete_pair 方法。这个形式将删除与一个键关联的所有值;后者将只删除指定的键/ 值对。
我们没有实现完整的类;但是有些方法,如invert, 要求一些设计来决定它们的行。如果你感兴趣,你可以完成余下的部分。
Listing 3.9 Hash with Duplicate Keys
class HashDup

def initialize(*all)

raise IndexError if all.size % 2 != 0

@store = { }

if all[0]
# not nil

keyval = all.dup

while !keyval.empty?

key = keyval.shift

if @store.has_key?(key)

@store[key] += [keyval.shift]

else

@store[key] = [keyval.shift]

end

end

end

end

def store(k,v)

if @store.has_key?(k)

@store[k] += [v]

else

@store[k] = [v]

end

end

def [](key)

@store[key]

end

def []=(key,value)

self.store(key,value)

end

def to_s

@store.to_s

end

def to_a

@store.to_a

end

def inspect

@store.inspect

end

def keys

result=[]

@store.each do |k,v|

result += ([k]*v.size)

end

result

end

def values

@store.values.flatten

end

def each

@store.each { |k,v| v.each { |y| yield k,y} }

end

alias each_pair each

def each_key

self.keys.each { |k| yield k}

end

def each_value

self.values.each { |v| yield v}

end

def has_key? k

self.keys.include? k

end

def has_value? v

self.values.include? v

end

def length

self.values.size

end

alias size length

def delete k

val = @store[k]

@store.delete k

val

end

def delete k,v

@store[k] -= [v] if @store[k]

v

end

# Other methods omitted here...
end
# This won't work... dup key will ignore
# first occurrence.
h = { 1=>1, 2=>4, 3=>9, 4=>16, 2=>0}
# This will work...
h = HashDup.new(1,1, 2,4, 3,9, 4,16, 2,0)
k = h.keys
# [4, 1, 2, 2, 3]
v = h.values
# [16, 1, 4, 0, 9]
n = h.size
# 5
h.each { |k,v| puts "#{ k}
=> #{ v} "}
# Prints:
# 4 => 16
# 1 => 1
# 2 => 4
# 2 => 0
# 3 => 9
[url=http://my4java.itpub.net/post/9983/@MSITStore:C ocuments %20and%20SettingsAdministrator桌面the%20ruby%20way.chm::/comete/?x=1& mode=section&sortKey=rank&sortOrder=desc&view=book&xmlid=0-672-32083-5/30041534&g=&catid=&s=1&b=1&f=1&t=1&c=1&u=1&r=&o=1&n=1&d=1&p=1&a=0][/url]


[url=http://my4java.itpub.net/post/9983/@MSITStore:C ocuments %20and%20SettingsAdministrator桌面the%20ruby%20way.chm:: /comete/ch03lev1sec1.htm][/url][url=http://my4java.itpub.net/post/9983/@MSITStore:C ocuments%20and%20SettingsAdministrator桌面the%20ruby%20way.chm::/comete/ch03lev1sec3.htm][/url]



三、用堆栈和队列工作 堆栈和队列是我们讨论的但不是Ruby 内建的第一个实体。我们的意思是Ruby 不应该没有Stack Quere ,应像Array Hash 类一样。
从某种程度上讲,它们应该内建于Ruby 中。事实上,只要我们将数组当成堆栈或队列的话,Ruby Array 类实现了所有功能。你马上就可以看到这些。
堆栈是一个后进先出(LIFO) 数据结构。自助餐厅的盘子就是一个堆栈;盘子被添加到顶部然后被从顶部移走。
这个有限制的操作可以用堆栈来完成。这至少包括push pop( 添加与移除条目) ;通常有种用于测试空堆栈的方式,也有方法用于检查顶的东西是否被移走。堆栈的实现从不提供对它中间条目的检查。
你可以请求数组如何实现堆栈,数组元素是可以被随机地访问而堆栈元素却不可以。回答很简单:堆栈的抽象层次比数组高;你就将它视为一个堆栈。你若非法地访问一个元素,它会被堆栈中止。
当然,你可以轻易地定义一个Stack 类,以便于只可合法地访问元素。我们将说明如何这样做。
注意,大多数用于堆栈的算法使用了递归。想一下就会知道其中的原因。调用功能或方法可将数据放入到系统堆栈中,然后这个数据被弹出返回。因此,递归算法可以简单地处理这些,定义用于系统层的堆栈。哪个更好些?这依赖于你的值如何读取,效率,和其它条件。
队列是先进先出(FIFO) 数据结构。它与人们站在一排的队伍类似,例如,电影院。新来的站到队尾,等待的时间最长的会获取下一个服务。在程序中,它的使用通常比堆栈要少。
队列在实时环境中更有用,它将要处理的东西提交给系统。它们在生产者/ 消费者情况下也很有用( 特别地是多线程的处理) 。打印队列是个很好的例子;打印任务被添加到队尾,然后它们站队直到被移动到其它后面。
两个基本的队列操作通常被称为入队和出队。Array 类中的相应实例方法分别是shift unshift
注意,在实现的堆栈内,unshift 被用于shift 的服务,而不是队列,因为unshif 添加从shift 移除东西到它的尾部。这些方法的不同组合实现了堆栈和队列,但我们不关心这种变化。
本节结束时,我们讨论堆栈和队列。现在让我们看一些例子。


1 、实现精确的堆栈

前面我们曾说过要显示堆栈如何拒绝非法的访问。现在我们可以做这些( Listing3.10) 。这儿我们用了个简单的类,它有一个内部数组并管理对这个数组的访问。( 另一种做法是使用委派,但我们此处用的例子简单且也能很好地工作。
Listing 3.10 Stack
class Stack

def initialize

@store = [] # 用数组实现堆栈

end

def push(x)

@store.push x

end

def pop

@store.pop

end

def peek

@store.last

end

def empty?

@store.empty?

end
end
我们添加了多个数组中没有定义的操作;peek 只简单地检查堆栈的顶部并返回结果。

[ 本帖最后由 blackanger 于 2007-5-20 17:55 编辑 ]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值