Givens
Нам дана строка, представляющая белок, состоящий из аминокислот:
sequence = "MGAAARTLRLALGLLLLATLLRPADACSCSPVHPQQAFCNADVVIR" +
"AKAVSEKEVDSGNDIYGNPIKRIQYEIKQIKMFKGPEKDIEFI"
и га sh, который содержит формулы амино кислоты:
formulas = {
"A"=>"C3H7NO2", "R"=>"C6H14N4O2", "N"=>"C4H8N2O3", "D"=>"C4H7NO4",
"C"=>"C3H7NO2S", "E"=>"C5H9NO4", "Q"=>"C5H10N2O3", "G"=>"C2H5NO2",
"H"=>"C6H9N3O2", "I"=>"C6H13NO2", "L"=>"C6H13NO2", "K"=>"C6H14N2O2",
"M"=>"C5H11NO2S", "F"=>"C9H11NO2", "P"=>"C5H9NO2", "S"=>"C3H7NO3",
"T"=>"C4H9NO3", "W"=>"C11H12N2O2", "Y"=>"C9H11NO3", "V"=>"C5H11NO2"
}
Получение количества атомов в каждой аминокислоте
В качестве первого шага мы можем вычислить числа каждого атома в каждой аминокислоте:
counts = formulas.transform_values do |s|
s.scan(/[CHNOS]\d*/).
each_with_object({}) do |s,h|
h[s[0]] = s.size == 1 ? 1 : s[1..-1].to_i
end
end
#=> {"A"=>{"C"=>3, "H"=>7, "N"=>1, "O"=>2},
# "R"=>{"C"=>6, "H"=>14, "N"=>4, "O"=>2},
# ...
# "M"=>{"C"=>5, "H"=>11, "N"=>1, "O"=>2, "S"=>1}
# ...
# "V"=>{"C"=>5, "H"=>11, "N"=>1, "O"=>2}}
Вычислить формулу для белка
Тогда это просто:
def protein_formula(sequence, counts)
sequence.each_char.
with_object("C"=>0, "H"=>0, "N"=>0, "O"=>0, "S"=>0) do |c,h|
counts[c].each { |aa,cnt| h[aa] += cnt }
end.each_with_object('') { |(aa,nbr),s| s << "#{aa}#{nbr}" }
end
protein_formula(sequence, counts)
#=> "C434H888N120O213S5"
Другой пример:
protein_formula("MCMPCFTTDHQMARKCDDCCGGKGRGKCYGPQCLCR", count)
#=> "C158H326N52O83S11"
Объяснение расчета counts
Этот расчет:
counts = formulas.transform_values do |s|
s.scan(/[CHNOS]\d*/).each_with_object({}) do |s,h|
h[s[0]] = s.size == 1 ? 1 : s[1..-1].to_i
end
end
использует метод Hash # transform_values . Он вернет ha sh, имеющий те же ключи, что и ha sh formulas
, со значениями этих ключей в formula
, измененными блоком transform_values
. Например, formulas["A"]
("C3H7NO2"
) «преобразуется» в га sh {"C"=>3, "H"=>7, "N"=>1, "O"=>2}
в га sh, которое возвращается, counts
.
transform_values
передает каждое значение formulas
для блока и устанавливает переменную блока, равную ему. Первое переданное значение - "C3H7NO2"
, поэтому оно устанавливает:
s = "C3H7NO2"
Мы можем написать вычисление блока более просто:
h = {}
s.scan(/[CHNOS]\d*/).each do |s|
h[s[0]] = s.size == 1 ? 1 : s[1..-1].to_i
end
h
(Как только вы поймете это вычисление, которое я объясняю ниже см. Enumerable # each_with_object , чтобы понять, почему я использовал этот метод в своем решении.)
После инициализации h
пустым ха sh выполняются следующие вычисления:
h = {}
a = s.scan(/[CHNOS]\d*/)
#=> ["C3", "H7", "N", "O2"]
a
вычисляется с использованием String # scan с регулярным выражением /[CHNOS]\d*/
. Это регулярное выражение, или regex , соответствует ровно одному символу в классе символов [CHNOS]
, за которым следует ноль более (*
) цифр (\d
). Поэтому он разделяет строку "C3H7NO2"
на подстроки, которые возвращаются в массиве, показанном при вычислении a
выше. Продолжая,
a.each do |s|
h[s[0]] = s.size == 1 ? 1 : s[1..-1].to_i
end
изменяет h
на следующее:
h #=> {"C"=>3, "H"=>7, "N"=>1, "O"=>2}
Переменная блока s
первоначально устанавливается равной первому элементу a
, который передается в Блок each
:
s = "C3"
затем мы вычисляем:
h[s[0]] = s.size == 1 ? 1 : s[1..-1].to_i
h["A"] = 2 == 1 ? 1 : "3".to_i
= false ? 1 : 3
3
Это повторяется для каждого элемента a
.
Восклицательный знак построения формулы для белка
Мы можем упростить следующий код 1 :
sequence.each_char.with_object("C"=>0, "H"=>0, "N"=>0, "O"=>0) do |c,h|
counts[c].each { |aa,cnt| h[aa] += cnt }
end.each_with_object('') { |(aa,nbr),s| s << "#{aa}#{nbr}" }
до более или менее следующего:
h = { "C"=>0, "H"=>0, "N"=>0, "O"=>0, "S"=>0 }
ch = sequence.chars
#=> ["M", "G", "A",..., "F", "I"]
ch.each do |c|
counts[c].each { |aa,cnt| h[aa] += cnt }
end
h #=> {"C"=>434, "H"=>888, "N"=>120, "O"=>213, "S"=>5}
Когда первое значение ch
("M"
) передается в блок each
(когда h = { "C"=>0, "H"=>0, "N"=>0, "O"=>0, "S"=>0 }
), выполняются следующие вычисления:
c = "M"
g = counts[c]
#=> {"C"=>10, "H"=>22, "N"=>2, "O"=>4, "S"=>1}
g.each { |aa,cnt| h[aa] += cnt }
h #=> {"C"=>10, "H"=>22, "N"=>2, "O"=>4, "S"=>1}
Наконец , (когда h #=> {"C"=>434, "H"=>888, "N"=>120, "O"=>213, "S"=>5}
)
s = ''
h.each { |aa,nbr| s << "#{aa}#{nbr}" }
s #=> "C434H888N120O213S5"
Когда aa = "C"
и nbr = 434
,
"#{aa}#{nbr}"
#=> "C434"
добавлены к строке s
.
1. (("C"=>0, "H"=>0, "N"=>0, "O"=>0)
сокращенно для ({"C"=>0, "H"=>0, "N"=>0, "O"=>0})
.