valores de retorno implícitas em Ruby
-
21-08-2019 - |
Pergunta
Eu sou um pouco novo para Ruby e embora eu encontrá-lo para ser uma linguagem muito intuitivo eu estou tendo alguma dificuldade compreender como implícita valores de retorno comportar.
Eu estou trabalhando em um pequeno programa para grep registros do Tomcat e gerar arquivos CSV delimitado por tubos a partir dos dados pertinentes. Aqui está um exemplo simplificado que eu estou usando para gerar as linhas de uma entrada de registo.
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*?)\]/)
Quando eu executar esse código eu voltar o seguinte, que é o que se espera quando retornar explicitamente o valor da produção.
00:00:13,207|06/18|0951714636005|20
No entanto, se eu mudar LineMatcher ao seguinte e não retornar explicitamente saída:
class LineMatcher
class << self
def match(line, regex)
output = ""
line.scan(regex).each do |matched|
output << matched.join("|") << "\n"
end
end
end
end
Então eu recebo o seguinte resultado:
00:00:13,207
06/18
0951714636005
20
Obviamente, este não é o resultado desejado. Parece que eu deveria ser capaz de se livrar da variável de saída, mas não está claro onde o valor de retorno está vindo. Além disso, quaisquer outras sugestões / melhorias para facilitar a leitura são bem-vindos.
Solução
Qualquer declaração em Ruby retorna o valor da última expressão avaliada. Você precisa saber a implementação e o comportamento do método mais utilizado, a fim de saber exatamente como seu programa irá funcionar.
#each
retorna a coleção que você iterated diante. Dito isto, o seguinte código irá retornar o valor de line.scan (regexp).
line.scan(regex).each do |matched|
output << matched.join("|") << "\n"
end
Se você quiser retornar o resultado da execução, você pode usar map
, que funciona como each
mas retorna a coleção modificado.
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
Existem vários métodos úteis que você pode usar dependendo do seu caso muito específico. Neste você pode querer usar inject
a menos que o número de resultados retornados pelo scan
é elevada (trabalhando em matrizes, em seguida, fundi-las é mais eficiente do que trabalhar em uma única string).
class LineMatcher
class << self
def match(line, regex)
line.scan(regex).inject("") do |output, matched|
output << matched.join("|") << "\n"
end
end
end
end
Outras dicas
Em rubi o valor de retorno de um método é o valor devolvido pela última declaração. Você pode optar por ter um retorno explícita também.
No seu exemplo, o primeiro trecho retorna o output
string. O segundo trecho no entanto retorna o valor retornado pelo método each
(que é agora o último stmt), que acaba por ser uma série de fósforos.
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"]
Update: No entanto, que ainda não explica sua saída em uma única linha. Eu acho que é um erro de formatação, deve imprimir cada uma das partidas em uma linha diferente porque é assim que puts
imprime uma matriz. Um código de pouco pode explicar isso melhor do que eu ..
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
Pessoalmente acho o seu método com o retorno explícita mais legível (neste caso)