2

一度に 1 行ずつファイルを読み取り、配列をハッシュ クラスに割り当てる小さなスクリプトを作成するために、ハッシュのオブジェクトを作成しています。ハッシュをサブクラス化するかどうかに応じて、非常に異なる結果が得られます。さらに、スーパーを使用すると、理解できないことが変わります。

私の主な問題は、ハッシュ (< ハッシュ) をサブクラス化しなくても完全に機能することですが、ハッシュのメソッドが得られないことです (キーを反復処理してそこから何かを取得するなど)。ハッシュされた配列の最後の要素だけが保存されているようです....だから、サブクラスのメソッドを取得する方法についての洞察. 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"
}

ここでハッシュをサブクラス化して何が起こっているのかを誰かが理解し、いくつかのポインタを持っているなら、それは素晴らしいことです。

明示的な < ハッシュなしで実行:

./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
4

2 に答える 2

4

コードを使用して問題を再現できません:

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 メソッドが呼び出すため) とゲッター/セッターを再実装する必要があります。代わりに1つのメソッドを変更するだけでよい場合、努力する価値はありません.

于 2012-10-28T13:47:57.883 に答える
2

Andrew Marshallの答えはすでに正しいですが、以下の代替案を 試すこともできます.

あなたのコードから、ハッシュのように振る舞うオブジェクトを作成したいと思うかもしれませんが、動作は少し異なります。したがって、最初のコードは次のようになります。

class Dictionary < Hash

ここでは、ディクショナリ内のキーに新しい値を割り当てる方法が異なります。上記の例から、割り当ては前の値を新しい値に置き換えませんが、キーがまだ存在しない場合は、新しい値を新しい値で初期化された前の配列または新しい配列にプッシュします。

ここでは、<<演算子を Array の push メソッドの省略形として使用しています。また、スーパーが行うことであるため、メソッドは値を返します(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 クラスにモンキーパッチを適用する必要はありません。

  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"] }

しかし、確かに、クラス定義はこの1行で置き換えることができます

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

OK、おしゃべりしてすみません。お役に立てば幸いです。

于 2012-10-28T13:49:12.843 に答える