Неявные возвращаемые значения в Ruby
-
21-08-2019 - |
Вопрос
Я новичок в Ruby, и хотя я считаю, что это очень интуитивный язык, мне трудно понять, как ведут себя неявные возвращаемые значения.
Я работаю над небольшой программой для поиска журналов Tomcat и создания CSV-файлов с разделителями-каналами из соответствующих данных.Вот упрощенный пример, который я использую для создания строк из записи журнала.
class LineMatcher
class << self
def match(line, regex)
output = ""
line.scan(regex).each do |matched|
output << matched.join("|") << "\n"
end
return output
end
end
end
puts LineMatcher.match("00:00:13,207 06/18 INFO stateLogger - TerminationRequest[accountId=AccountId@66679198[accountNumber=0951714636005,srNumber=20]",
/^(\d{2}:\d{2}:\d{2},\d{3}).*?(\d{2}\/\d{2}).*?\[accountNumber=(\d*?),srNumber=(\d*?)\]/)
Когда я запускаю этот код, я получаю следующее, что и ожидается при явном возврате выходного значения.
00:00:13,207|06/18|0951714636005|20
Однако, если я изменю LineMatcher на следующее и не буду явно возвращать вывод:
class LineMatcher
class << self
def match(line, regex)
output = ""
line.scan(regex).each do |matched|
output << matched.join("|") << "\n"
end
end
end
end
Тогда я получаю следующий результат:
00:00:13,207
06/18
0951714636005
20
Очевидно, это не желаемый результат.Такое ощущение, что я должен избавиться от выходной переменной, но неясно, откуда берется возвращаемое значение.Кроме того, приветствуются любые другие предложения/улучшения для удобства чтения.
Решение
Любой оператор в Ruby возвращает значение последнего вычисленного выражения.Вам необходимо знать реализацию и поведение наиболее используемого метода, чтобы точно знать, как будет действовать ваша программа.
#each
возвращает коллекцию, которую вы повторяли.Тем не менее, следующий код вернет значение line.scan(regexp).
line.scan(regex).each do |matched|
output << matched.join("|") << "\n"
end
Если вы хотите вернуть результат выполнения, вы можете использовать map
, который работает как each
но возвращает измененную коллекцию.
class LineMatcher
class << self
def match(line, regex)
line.scan(regex).map do |matched|
matched.join("|")
end.join("\n") # remember the final join
end
end
end
Есть несколько полезных методов, которые вы можете использовать в зависимости от вашего конкретного случая.В этом вы можете использовать inject
если количество результатов, возвращаемых scan
высокий (работа с массивами и их слияние более эффективны, чем работа с одной строкой).
class LineMatcher
class << self
def match(line, regex)
line.scan(regex).inject("") do |output, matched|
output << matched.join("|") << "\n"
end
end
end
end
Другие советы
В Ruby возвращаемое значение метода — это значение, возвращаемое последним оператором.Вы также можете выбрать явный возврат.
В вашем примере первый фрагмент возвращает строку output
.Однако второй фрагмент возвращает значение, возвращаемое функцией each
метод (который теперь является последним stmt), который оказывается массивом совпадений.
irb(main):014:0> "StackOverflow Meta".scan(/[aeiou]\w/).each do |match|
irb(main):015:1* s << match
irb(main):016:1> end
=> ["ac", "er", "ow", "et"]
Обновлять:Однако это все еще не объясняет ваш вывод в одной строке.Я думаю, что это ошибка форматирования, каждое совпадение должно печатать на отдельной строке, потому что именно так puts
печатает массив.Небольшой код может объяснить это лучше меня..
irb(main):003:0> one_to_three = (1..3).to_a
=> [1, 2, 3]
irb(main):004:0> puts one_to_three
1
2
3
=> nil
Лично я считаю ваш метод с явным возвратом более читабельным (в данном случае)