带标点,并转换为小写
你可能想要做的第一件事就是从字符串拿着文件的内容删除所有标点,然后转换还剩下些什么小写,后者让你不用担心把'猫'和'猫'算作同一个词。这两个操作可以按任意顺序完成。
更改大写字母为小写很简单:
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