考虑到您是 Ruby 新手,我会说您有一个良好的开端。您问是否应该使用冒泡排序。我猜你正在考虑对一个单词的多次出现进行分组,然后通过数组来计算它们。这会奏效,但还有其他一些更简单且更“类似于 Ruby”的方法。(我的意思是他们利用了语言的强大功能,同时更自然。)
让我们专注于计算一行中的唯一词。一旦你能做到这一点,你应该能够轻松地将其推广到多行。
第一种方法:使用哈希
第一种方法是使用散列。 h = {}
创建一个新的空的。哈希的键是单词,它的值是每个单词在行中出现的次数。例如,如果“猫”这个词出现 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}
我们完成了!
方法二:使用Enumerable#group_by
方法
每当您想对数组、哈希或其他集合的元素进行分组时,group_by
都应该想到该方法。
为了应用于group_by
快速的 brown fox 数组,我们提供了一个包含分组标准的块,在这种情况下,分组标准就是单词本身。这会产生一个哈希:
g = arr.group_by {|e| e}
# => {"the"=>["the", "the"], "quick"=>["quick"], "brown"=>["brown", "brown"], \
# "fox"=>["fox", "fox"], "jumped"=>["jumped"], "over"=>["over"], "lazy"=>["lazy"]}
接下来要做的是将哈希值转换为单词的出现次数(例如,转换["the", "the"]
为 2
)。为此,我们可以创建一个新的空哈希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}
最后,您可以通过链接最后两个语句来消除临时变量sorted_freqs
(因为没有Enumerable#sort_by!
方法而需要):
freqs.sort_by {|k,v| -v}.each {|word, freq| puts word+' '+freq.to_s}