alphabet = ["A","B","C","D","E","F","G","H","I","J",
"K","L","M","N","O","P","Q","R","S","T",
"U","V","W","X","Y","Z"," ",".",",",";",
"-","'"
]
file = File.read("vt_00.txt")
i = 0
while i < alphabet.count do
single_char = alphabet[i]
single_char_count = file.count(single_char)
print "#{alphabet[i]} = #{single_char_count} "
j = 0
while j < alphabet.count do
two_chars = alphabet[i] + alphabet[j]
two_chars_count = file.scan(two_chars).count
if two_chars_count > 10 && two_chars_count < 15
print "#{two_chars} = #{two_chars_count} "
end
k = 0
while k < alphabet.count do
three_chars = alphabet[i] + alphabet[j] + alphabet[k]
three_chars_count = file.scan(three_chars).count
if three_chars_count > 10 && three_chars_count < 15
print "#{three_chars} = #{three_chars_count} "
end
k += 1
end
j += 1
end
i += 1
end
我有像上层代码这样的代码。但是后来我通过each_cons找到了解决方案,你能解释一下它是如何工作的吗? 我不明白.inject...part。
count = string.each_cons(1).inject(Hash.new(0)) { |total, bigram| total[bigram] += 1; total }.sort_by { |_key, value| value }.reverse.to_h
更精细的编写方法是:
total = Hash.new(0)
string.each_cons(1).each{|bigram| total[bigram] += 1}
inject
允许注入一些起始值(Hash.new(0)
-->我们使用默认的 0,这样我们就可以安全地使用+=
运算符),并且块返回的任何内容都会在下一次迭代中注入。所以在这种情况下,我们必须显式返回哈希(total
)才能在下一步中操作它。
一个简单的例子是将数组的所有值相加:
[1,4,5,23,2,66,123].inject(0){|sum, value| sum += value}
我们从 0 开始,这是我们执行0 + 1
的第一次迭代,然后其结果将在下一次迭代中注入。
注意:在原始代码中,您可以更轻松地迭代数组,而不是使用while
循环和维护计数器,如下所示:
alphabet.each do |single_char|
single_char_count = file.count(single_char)
print "#{alphabet[i]} = #{single_char_count} "
alphabet.each do |second_char|
two_chars = single_char + second_char
# do something with two_chars
alphabet.each do |third_char|
three_chars = single_char + second-char + third_char
# do something with three_chars
end
end
end
我猜这取决于file
的大小,迭代所有each_cons
(1-2-3)还是使用file.scan
会更有效。
问题
您想知道以下内容是如何工作的:
g = Hash.new(0)
count = str.each_char.inject(g) do |h, s|
h[s] += 1
h
end.sort_by { |_key, value| value }.reverse.to_h
str.each_cons(1)
不起作用,因为类String
(str
是一个实例)没有实例方法each_cons
。有一个方法 Enumerable#each_cons,但类String
不include
该模块,因此字符串不响应该方法:
String.included_modules
#=> [Comparable, Kernel]
String#each_char 在这里确实有意义,因为它返回一个生成字符串每个字符的枚举器。因此,我认为each_char
的意思是写each_cons(1)
的地方。
我已将变量名称更改为更通用的名称,并已移动
g = Hash.new(0)
到单独的行。
一个例子
假设str
如下所示:
str = "The Cat and the Hat"
检查执行的步骤
让我们将计算分解为多个部分:
g = Hash.new(0)
#=> {}
h = str.each_char.inject(g) do |h,s|
h[s] += 1
h
end
#=> {"T"=>1, "h"=>2, "e"=>2, " "=>4, "C"=>1,
# "a"=>3, "t"=>3, "n"=>1, "d"=>1, "H"=>1}
a = h.sort_by { |_key, value| value }
#=> [["T", 1], ["C", 1], ["n", 1], ["d", 1], ["H", 1],
# ["h", 2], ["e", 2], ["a", 3], ["t", 3], [" ", 4]]
b = a.reverse
#=> [[" ", 4], ["t", 3], ["a", 3], ["e", 2], ["h", 2],
# ["H", 1], ["d", 1], ["n", 1], ["C", 1], ["T", 1]]
count = b.to_h
#=> {" "=>4, "t"=>3, "a"=>3, "e"=>2, "h"=>2,
# "H"=>1, "d"=>1, "n"=>1, "C"=>1, "T"=>1}
a
、b
和count
的计算很简单,所以让我们先考虑一下。
a
的计算
与所有Enumerable
方法一样,Enumerable#sort_by 要求其接收方响应方法each
。这里sort_by
的接收者是一个哈希,所以h
必须响应 Hash#each。事实上,sort_by
的第一个操作是通过向枚举器发送方法Hash#each
将h
转换为枚举器:
enum = h.each
#=> #<Enumerator: {"T"=>1, "h"=>2, "e"=>2, " "=>4, "C"=>1, "a"=>3,
# "t"=>3, "n"=>1, "d"=>1, "H"=>1}:each>
我们可以通过重复向枚举器#next方法发送方法来查看此枚举器生成的值:
enum.next #=> ["T", 1]
enum.next #=> ["h", 2]
enum.next #=> ["e", 2]
...
enum.next #=> ["H", 1]
enum.next #=> StopIteration (iteration reached an end)
可以看出,enum
生成了哈希的键值对序列。因此
h.sort_by { |_key, value| value }
相当于
[["T", 1], ["h", 2], ["e", 2],..., ["H", 1]].sort_by { |_key, value| value }
这就解释了为什么a
等于上面显示的数组。
b
的计算
这个计算再简单不过了。请注意,我们可以通过将b = h.sort_by { |_key, value| value }.reverse
替换为
b = h.sort_by { |_key, value| -value }
#=> [[" ", 4], ["a", 3], ["t", 3], ["h", 2], ["e", 2],
# ["T", 1], ["C", 1], ["n", 1], ["d", 1], ["H", 1]]
这将像以前一样按值的降序对h
的键值对进行排序,尽管连接的顺序略有不同。
count
的计算
这是方法 Array#to_h 的简单应用。
h
的计算
此计算的第一步是使用 Hash::new 方法创建一个默认值为零的空哈希:
h = Hash.new(0)
#=> {}
这只会导致h[k]
在没有键k
时返回默认值零h
。例如,由于h
现在没有键:
h['cat']
#=> 0
如果我们现在设置
h['cat'] = 3
然后
h['cat']
#=> 3
因为默认值不再适用。以这种方式创建的哈希h
通常称为计数哈希。Ruby 解析表达式h[s] += 1
的第一步是将其扩展为:
h[s] = h[s] + 1
如果h
没有键s
则表达式简化为
h[s] = 0 + 1
因为等号右侧的h[s]
(方法 Hash#[],与左侧的方法 Hash#[]= 相反)返回默认值零。如果字符串被"aaa"
,将进行以下计算:
h['a'] = h['a'] + 1 => 0 + 1 => 1
h['a'] = h['a'] + 1 => 1 + 1 => 2
h['a'] = h['a'] + 1 => 2 + 1 => 3
右侧的h['a']
在第一步中返回默认值零,但由于h
在第二步和第三步中具有键'a'
,因此在第一步之后返回h['a']
的当前值。
Enumerable#inject(又名reduce
)可以在这里使用,但h
的计算更常写如下:
h = str.each_char.each_with_object(Hash.new(0)) { |s,h| h[s] += 1 }
#=> {"T"=>1, "h"=>2, "e"=>2, " "=>4, "C"=>1,
# "a"=>3, "t"=>3, "n"=>1, "d"=>1, "H"=>1}
请参阅枚举#each_with_object。