Ruby を初めて使用することを考えると、良いスタートを切ったと思います。バブルソートを使用する必要があるかどうかを尋ねました。単語の複数の出現をグループ化し、配列を調べてそれらをカウントすることを考えていると思います。それは機能しますが、より簡単で「Ruby に似た」アプローチが他にもいくつかあります。(つまり、言語の強力な機能を利用すると同時に、より自然になります。)
1 行で一意の単語を数えることに注目しましょう。それができれば、それを複数の行に簡単に一般化できるはずです。
最初の方法: ハッシュを使用する
最初のアプローチは、ハッシュを使用することです。 h = {}
新しい空のものを作成します。ハッシュのキーは単語になり、その値は各単語が行に存在する回数になります。たとえば、「cat」という単語が 9 回出現する場合、h["cat"] = 9
必要なものだけが になります。このハッシュを作成するw
には、行内の各単語が既にハッシュ化されているかどうかを確認します。ハッシュ内にある場合
h[w] != nil
そうであれば、単語数をインクリメントします。
h[w] = h[w] + 1
あるいは単に
h[w] += 1
ハッシュに含まれていない場合は、次のように単語をハッシュに追加します。
h[w] = 1
つまり、次のことができます。
if h[w]
h[w] += 1
else
h[w] = 1
end
ここif h[w]
は と同じであることに注意してくださいif h[w] != nil
。
実際には、これをさらに簡単にするトリックを使用できます。次のようにハッシュを作成すると:
h = Hash.new(0)
値なしで追加したキーには、デフォルト値のゼロが割り当てられます。そうすれば、単語がすでにハッシュに含まれているかどうかを確認する必要がなくなります。私たちは単に書く
h[w] += 1
w
がハッシュにない場合、h[w]
それを追加して に初期化し0
、次に+= 1
にインクリメントし1
ます。かっこいいね?
これをすべてまとめましょう。仮定する
line = "the quick brown fox jumped over the lazy brown fox"
String#split
メソッドを使用して、この文字列を配列に変換します。
arr = line.split # => ["the", "quick", "brown", "fox", "jumped", \
"over", "the", "lazy", "brown", "fox"]
それから
h = Hash.new(0)
arr.each {|w| h[w] += 1}
h # => {"the"=>2, "quick"=>1, "brown"=>2, "fox"=>2, "jumped"=>1, "over"=>1, "lazy"=>1}
終わったね!
2番目の方法: メソッドを使用Enumerable#group_by
する
配列、ハッシュ、またはその他のコレクションの要素をグループ化したいときはいつでも、group_by
メソッドが頭に浮かぶはずです。
迅速な茶色のキツネの配列に適用group_by
するために、グループ化基準を含むブロックを提供します。この場合、それは単に単語そのものです。これによりハッシュが生成されます。
g = arr.group_by {|e| e}
# => {"the"=>["the", "the"], "quick"=>["quick"], "brown"=>["brown", "brown"], \
# "fox"=>["fox", "fox"], "jumped"=>["jumped"], "over"=>["over"], "lazy"=>["lazy"]}
次に行うことは、ハッシュ値を単語の出現回数に変換することです (たとえば、convert ["the", "the"]
to 2
)。これを行うには、新しい空の hash を作成し、h
それにハッシュ ペアを追加します。
h = {}
g.each {|k,v| h[k] = v.size}
h # => {"the"=>2, "quick"=>1, "brown"=>2, "fox"=>2, "jumped"=>1, "over"=>1, "lazy"=>1
もう一つ
次のコード スニペットがあります。
if(p[i] != "the" and p[i] != "to" and p[i] != "union" and p[i] != "political")
print p[i] + " "
end
上記のハッシュを使用して、これを少しきれいにするいくつかの方法を次に示しますh
。
最初の道
skip_words = %w[the to union political] # => ["the", "to", "union", "political"]
h.each {|k,v| (print v + ' ') unless skip_words.include?(k)}
第二の道
h.each |k,v|
case k
when "the", "to", "union", "political"
next
else
puts "The word '#{k}' appears #{v} times."
end
end
あなたのコメントに対処するために編集します。これを試して:
p = "The quick brown fox jumped over the quick grey fox".split
freqs = Hash.new(0)
p.each {|w| freqs[w] += 1}
sorted_freqs = freqs.sort_by {|k,v| -v}
sorted_freqs.each {|word, freq| puts word+' '+freq.to_s}
=>
quick 2
fox 2
jumped 1
The 1
brown 1
over 1
the 1
grey 1
通常、ypu はハッシュをソートしません。むしろ、最初に配列に変換します。
sorted_freqs = freqs.to_a.sort_by {|x,y| v}.reverse
また
sorted_freqs = freqs.to_a.sort_by {|x,y| -v}
sorted_freqs
ハッシュではなく配列になりました。最後の行はそのままです。一般に、ハッシュの順序に依存しないことが最善です。実際、Ruby バージョン 1.9.2 より前では、ハッシュは順序付けされていませんでした。順序が重要な場合は、配列を使用するか、ハッシュを配列に変換してください。
そうは言っても、ハッシュ値で最小から最大に並べ替えるか、(私が行ったように) ハッシュ値の負の値で最大から最小に並べ替えることができます。Enumerable#reverse
またはがないことに注意してくださいHash#reverse
。別の方法として (Ruby で猫の皮を剥ぐには常に多くの方法があります)、次のように並べ替えてv
使用することもできEnumerable#reverse_each
ます。
sorted_freqs.reverse_each {|word, freq| puts word+' '+freq.to_s}
最後に、最後の 2 つのステートメントをチェーンすることにより、一時変数sorted_freqs
(メソッドがないために必要)を削除できます。Enumerable#sort_by!
freqs.sort_by {|k,v| -v}.each {|word, freq| puts word+' '+freq.to_s}