子类化Ruby哈希,对象没有哈希方法?
我正在创建一个哈希对象,以便编写一个一次读入一行文件的小脚本,并将数组分配到我的哈希类中。 我得到了截然不同的结果,具体取决于我是否使用哈希子类,以及使用超级更改我不明白的东西。
我的主要问题是,如果没有使用哈希(<Hash)子类化,它可以很好地工作,但是我没有获得哈希方法(比如迭代关键字并将其从中取出)。子类化哈希使我可以执行这些操作,但它似乎只有哈希阵列的最后一个元素才会被存储......所以你需要了解如何获得子类的方法Dictionary类是我在这个网站上找到的一个很好的例子,所以我想了解如何正确使用它。
filename = 'inputfile.txt.'
# ??? class Dictionary < Hash
class Dictionary
def initialize()
@data = Hash.new { |hash, key| hash[key] = [] }
end
def [](key)
@data[key]
end
def []=(key,words)
@data[key] += [words].flatten
@data[key]
# super(key,words)
end
end
listData = Dictionary.new
File.open(filename, 'r').each_line do |line|
line = line.strip.split(/[^[:alpha:]|@|.]/)
puts "LIST-> #{line[0]} SUB-> #{line[1]} "
listData[line[0]] = ("#{line[1]}")
end
puts '====================================='
puts listData.inspect
puts '====================================='
print listData.reduce('') {|s, (k, v)|
s << "The key is #{k} and the value is #{v}.n"
}
如果任何人都明白这里发生了什么子类化哈希,并有一些指针,那将是非常好的。
无需显式运行<Hash:
./list.rb:34:in `<main>': undefined method `reduce' for #<Dictionary:0x007fcf0a8879e0> (NoMethodError)
这是我看到的典型错误,当我尝试以任何方式在我的哈希上进行迭代时。
这是一个示例输入文件:
listA billg@microsoft.com
listA ed@apple.com
listA frank@lotus.com
listB evanwhite@go.com
listB joespink@go.com
listB fredgrey@stop.com
我无法使用您的代码重现您的问题:
d = Dictionary.new #=> #<Dictionary:0x007f903a1adef8 @data={}>
d[4] << 5 #=> [5]
d[5] << 6 #=> [6]
d #=> #<Dictionary:0x007f903a1adef8 @data={4=>[5], 5=>[6]}>
d.instance_variable_get(:@data) #=> {4=>[5], 5=>[6]}
但是,如果你不子类化或包含一个定义它的类/模块,或者自己定义它,那么当然你不会得到reduce
!
你实施Dictionary
方式肯定会有问题。 你应该叫super
而不是在可能的情况下重新实现。 例如,简单的这个工作:
class Dictionary < Hash
def initialize
super { |hash, key| hash[key] = [] }
end
end
d = Dictionary.new #=> {}
d['answer'] << 42 #=> [42]
d['pi'] << 3.14 #=> [3.14
d #=> {"answer"=>[42], "pi"=>[3.14]}
如果你想重新实现内部哈希如何以及在哪里存储(即使用@data
),你至少必须重新实现each
(因为几乎所有Enumerable方法都调用它)和getter / setter。 如果您只需更改一种方法,则不值得付出努力。
虽然安德鲁马歇尔的回答已经正确,但您也可以尝试下面的替代方案。
从你的代码开始,我们可以假设你想创建一个像哈希一样行为的对象,但是有一些不同的行为。 因此我们的第一个代码就是这样。
class Dictionary < Hash
为字典中的某个键分配一个新值将在这里以不同的方式完成。 在上面的例子中,赋值不会用新值替换先前的值,而是将新值推向前一个或新值,如果该键尚不存在,则使用新值进行初始化。
这里我使用<<
操作符作为Array的推式方法的简写。 此外,该方法返回值,因为它是超级做的(参见if部分)
def []=(key, value)
if self[key]
self[key] << value
return value # here we mimic what super do
else
super(key, [value])
end
end
使用我们自己的类的好处是我们可以为类添加新的方法,并且它可以被所有的实例访问。 因此,我们不需要对认为是危险的Hash类进行monkeatch。
def size_of(key)
return self[key].size if self[key]
return 0 # the case for non existing key
end
现在,如果我们综合以上所有内容,我们将获得这些代码
class Dictionary < Hash
def []=(key, value)
if self[key]
self[key] << value
return value
else
super(key, [value])
end
end
def size_of(key)
return self[key].size if self[key]
return 0 # the case for non existing key
end
end
player_emails = Dictionary.new
player_emails["SAO"] = "kirito@sao.com" # note no << operator needed here
player_emails["ALO"] = "lyfa@alo.com"
player_emails["SAO"] = "lizbeth@sao.com"
player_emails["SAO"] = "asuna@sao.com"
player_emails.size_of("SAO") #=> 3
player_emails.size_of("ALO") #=> 1
player_emails.size_of("GGO") #=> 0
p listData
#=> {"SAO" => ["kirito@sao.com", "lizbeth@sao.com", "asuna@sao.com"],
#=> "ALO" => ["lyfa@alo.com"] }
但是,当然,班级定义可以用这一行代替
player_emails = Hash.new { [] }
# note that we wont use
#
# player_emails[key] = value
#
# instead
#
# player_emails[key] << value
#
# Oh, if you consider the comment,
# it will no longer considered a single line
答案完成后,我想评论一下你的示例代码:
filename = 'inputfile.txt.'
# Maybe it's better to use ARGF instead,
# so you could supply the filename in the command line
# and, is the filename ended with a dot? O.o;
File.open(filename, 'r').each_line do |line|
# This line open the file anonimously,
# then access each line of the file.
# Please correct me, Is the file will properly closed? I doubt no.
# Saver version:
File.open(filename, 'r') do |file|
file.each_line do |line|
# ...
end
end # the file will closed when we reach here
# ARGF version:
ARGF.each_line do |line|
# ...
end
# Inside the each_line block
line = line.strip.split(/[^[:alpha:]|@|.]/)
# I don't know what do you mean by that line,
# but using that regex will result
#
# ["listA", "", "", "billg@microsoft.com"]
#
# Hence, your example will fail since
# line[0] == "listA" and line[1] == ""
# also note that your regex mean
#
# any character except:
# letters, '|', '@', '|', '.'
#
# If you want to split over one or more
# whitespace characters use s+ instead.
# Hence we could replace it with:
line = line.strip.split(/s+/)
puts "LIST-> #{line[0]} SUB-> #{line[1]} "
# OK, Is this supposed to debug the line?
# Tips: the simplest way to debug is:
#
# p line
#
# that's all,
listData[line[0]] = ("#{line[1]}")
# why? using (), then "", then #{}
# I suggest:
listData[line[0]] = line[1]
# But to make more simple, actually you could do this instead
key, value = line.strip.split(/s+/)
listData[key] = value
# Outside the block:
puts '====================================='
# OK, that's too loooooooooong...
puts '=' * 30
# or better assign it to a variable since you use it twice
a = '=' * 30
puts a
p listData # better way to debug
puts a
# next:
print listData.reduce('') { |s, (k, v)|
s << "The key is #{k} and the value is #{v}.n"
}
# why using reduce?
# for debugging you could use `p listData` instead.
# but since you are printing it, why not iterate for
# each element then print each of that.
listData.each do |k, v|
puts "The key is #{k} and the value is #{v}."
end
好吧,抱歉,这么多,希望它有帮助。
链接地址: http://www.djcxy.com/p/60955.html