Я предлагаю вам построить свой класс следующим образом.
class Letter
def initialize(text) # 1
@text = text
end
def display
h = calculate_frequencies # 2
('a'..'z').each { |ltr|
puts "%s%s" % [ltr, '*' * h.fetch(ltr, 0)] } # 3
end
private # 4
def calculate_frequencies # 5
@text.downcase.
each_char. # 6
with_object(Hash.new(0)) { |c, letters| # 7
letters[c] += 1 if c.match?(/\p{Lower}/) } # 8
end
end
str = "Now is the time for all Rubyists to come to the aid of their bowling team."
ltr = Letter.new(str)
ltr.display
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
Примечания
1
text
должен быть аргументом initialize
, чтобы методы можно было использовать для любой строки, а не только для одной жестко заданной строки. @letters
не следует инициализировать здесь, поскольку его нужно инициализировать в calculate_frequencies
каждый раз, когда вызывается этот метод (и там он не должен быть переменной экземпляра).
2
Для str
, calculate_frequencies
возвращает
ltr.send(:calculate_frequencies)
#=> {"n"=>2, "o"=>7, "w"=>2, "i"=>6, "s"=>3, "t"=>8, "h"=>3, "e"=>6,
# "m"=>3, "f"=>2, "r"=>3, "a"=>3, "l"=>3, "u"=>1, "b"=>2, "y"=>1,
# "c"=>1, "d"=>1, "g"=>1}
Object # send вызывает private
методы, а также методы public
или protected
.
3
См. Hash # fetch и String # *.
4
Все методы, определенные после вызова ключевого слова private
, являются закрытыми, до тех пор, пока не будет найдено ключевое слово public
или protected
. В качестве альтернативы можно определить один частный метод как private def calculate_frequencies; ... ; end
. Также открытый (или защищенный) метод m
может быть сделан приватным, выполнив private m
.
5
Одним из соглашений Ruby является использование snake-case для имен переменных и методов. Вы не должны следовать этому соглашению, но 99% + Rubyists делают.
6
String # each_char возвращает перечислитель, тогда как String # chars возвращает массив. Последний должен использоваться только тогда, когда нужен массив или он связан с методом Array
; в противном случае предпочтительным является each_char
, поскольку он не создает ненужный временный массив.
7
См. Перечислитель # with_object .
8
Вместо того, чтобы сопоставлять все, кроме пробелов, вы, вероятно, хотите сопоставлять только буквы. Обратите внимание, как я использовал if
здесь, чтобы избежать необходимости в двух утверждениях. См. String # match? . Вместо этого можно написать c =~ /\p{Lower}/
или c[/\p{Lower}/]
. \p{Lower}
(или [[:lower:]]
) соответствует любой строчной букве Unicode, которая обычно предпочтительнее /[a-z]/
. Даже в английском тексте можно встретить слова, имеющие буквы с диакритическими знаками, такие как née, Señor, excé и rosé. "é".match? /[a-z]/ #=> false
но "é".match? /\p{Lower}/ #=> true
. Найдите документ Regexp для \p{Lower}
и [[:lower:]]
.