帶標點,並轉換爲小寫
你可能想要做的第一件事就是從字符串拿着文件的內容刪除所有標點,然後轉換還剩下些什麼小寫,後者讓你不用擔心把'貓'和'貓'算作同一個詞。這兩個操作可以按任意順序完成。
更改大寫字母爲小寫很簡單:
text = whole_file.downcase
要刪除標點它可能是更容易決定如何保持,而不是什麼丟棄。如果我們只希望保持小寫字母,你可以這樣做:
text = whole_file.downcase.gsub(/[^a-z]/, '')
也就是說,替代比(^
)小寫字母以外的所有字符的空字符串。
確定的個別字
頻率如果你想算的次數text
數包含單詞'candy'
,則可以使用方法String#scan的字符串text
,然後確定的大小返回的數組:
text.scan(/\bcandy\b/).size
scan
返回與該字符串'candy'
的每次出現的陣列; .size
返回該數組的大小。這裏\b
確保'candy gram'
在每一端都有一個單詞「邊界」,它可以是空格或行或文件的開始或結束。這是爲了防止「candycane」被計算在內。
的第二種方法是將字符串text
轉換爲詞的數組,你幹得:
myArray = text.split
如果你不介意的話,我想稱之爲:
words = text.split
因爲我覺得更有表現力。
最直接的方法來確定的時間'candy'
顯示的數字是使用方法Enumberable#count,像這樣:
words.count('candy')
您也可以使用數組差分法,Array#-,正如你指出:
words.size - (words - ['candy']).size
如果你想知道的時間是「糖果」或「克」的號碼出現,當然你可以做如上的,總結的兩項罪名。其他一些方法是:
words.size - (myArray - ['candy', 'gram']).size
words.count { |word| word == 'candy' || word = 'gram' }
words.count { |word| ['candy', 'gram'].include?(word) }
確定出現在文本中的所有單詞的頻率
你的哈希的使用爲零的默認值是一個不錯的選擇:
def frequency_of_all_words(words)
frequency = Hash.new(0)
words.each { |word| frequency[word] +=1 }
frequency
end
我寫這個作爲強調words.each...
不返回frequency
的方法。你經常會看到這樣寫的更加簡潔使用方法Enumerable#each_with_object,它返回的哈希(「對象」):
def frequency_of_all_words(words)
words.each_with_object(Hash.new(0)) { |word, h| h[word] +=1 }
end
一旦你的哈希像你一樣frequency
你可以對它進行排序:
frequency.sort_by {|word, freq| freq }
或
frequency.sort_by(&:last)
,你可以寫:
frequency.sort_by {|_, freq| freq }
因爲您沒有使用第一個塊變量。如果您想首先最頻繁的一句話:
frequency.sort_by(&:last).reverse
或
frequency.sort_by {|_, freq| -freq }
所有這些都會給你一個數組。如果你想將它轉換回散列(首先說明最大值):
Hash[frequency.sort_by(&:last).reverse]
或者在Ruby 2中。0+,
frequency.sort_by(&:last).reverse.to_h
計數次數的子串出現
現在讓我們來算的字符串'candy gram'
出現的次數。你可能會認爲,我們可以在串保持整個文件使用String#scan
,正如我們前面做:
text.scan(/\bcandy gram\b/).size
的第一個問題是,這不會趕上「糖果\ NGRAM」;即,當單詞由換行符分隔時。我們可以通過將正則表達式更改爲/\bcandy\sgram\b/
來解決此問題。第二個問題是「糖果克」可能是「糖果」。克'在文件中,在這種情況下,你可能不想數它。
更好的方法是在陣列words
上使用方法Enumerable#each_cons。向你展示它是如何工作的最簡單的方法是通過例如:
words = %w{ check for candy gram here candy gram again }
#=> ["check", "for", "candy", "gram", "here", "candy", "gram", "again"]
enum = words.each_cons(2)
#=> #<Enumerator: ["check", "for", "candy", "gram", "here", "candy",
# "gram", "again"]:each_cons(2)>
enum.to_a
#=> [["check", "for"], ["for", "candy"], ["candy", "gram"],
# ["gram", "here"], ["here", "candy"], ["candy", "gram"],
# ["gram", "again"]]
each_cons(2)
返回一個枚舉;我已將其轉換爲數組以顯示其內容。
因此,我們可以寫
words.each_cons(2).map { |word_pair| word_pair.join(' ') }
#=> ["check for", "for candy", "candy gram", "gram here",
# "here candy", "candy gram", "gram again"]
;最後:
words.each_cons(2).map { |word_pair|
word_pair.join(' ') }.count { |s| s == 'candy gram' }
#=> 2
1如果你也想保持破折號,爲複姓的話,改變正則表達式來/[^-a-z]/
或/[^a-z-]/
。從String#split
2注意.split
相同既.split(' ')
和.split(/\s+/)
)。
3此外,Ruby的命名約定是對變量和方法(如my_array
)使用小寫字母和下劃線(「蛇狀」)。
我不明白,「...顯示文字,計數次數」糖果「和」克「出現」。你的意思是你想要計算每個「糖果」和「克」字樣出現的次數,並顯示結果?我意識到這個問題還有第二部分。 – 2014-12-05 05:56:09
嗨卡里。是的,我希望我的輸出結果除了顯示我的文本中「糖果克」這個詞的組合頻率外,還顯示「糖果」和「克」這兩個詞的頻率。我會澄清這個問題,謝謝你幫我清楚了。 – maria 2014-12-05 06:05:19