Como escrever uma instrução de switch Ruby (caso ... quando) com regex e referências anteriores?

86

Eu sei que posso escrever uma instrução de caso Ruby para verificar uma correspondência com expressões regulares. No entanto, gostaria de usar os dados de correspondência em minha instrução de retorno. Algo como este semipseudocódigo:

foo = "10/10/2011"

case foo
    when /^([0-9][0-9])/
        print "the month is #{match[1]}"
    else
        print "something else"
end

Como posso conseguir isso?

Obrigado!


Apenas uma observação: eu entendo que nunca usaria uma instrução switch para um caso simples como acima, mas esse é apenas um exemplo. Na realidade, o que estou tentando alcançar é a correspondência de muitas expressões regulares potenciais para uma data que pode ser escrita de várias maneiras, e então analisá-la com a classe Date de Ruby de acordo.

Yuval Karmi
fonte
1
Date.parse de Ruby entende muitos formatos de data. Tentaste?
raine
Embora não responda a esta pergunta, você pode querer dar uma olhada na joia crônica ...
DGM

Respostas:

153

As referências aos grupos de correspondência de regex mais recentes são sempre armazenadas em pseudo variáveis $1 para $9:

case foo
when /^([0-9][0-9])/
    print "the month is #{$1}"
else
    print "something else"
end

Você também pode usar a $LAST_MATCH_INFOpseudo variável para obter o MatchDataobjeto inteiro . Isso pode ser útil ao usar capturas nomeadas:

case foo
when /^(?<number>[0-9][0-9])/
    print "the month is #{$LAST_MATCH_INFO['number']}"
else
    print "something else"
end
Yossi
fonte
1
@Yossi: Você tem uma fonte para seu comentário sobre segurança de tópicos? Acabei de fazer um experimento no Ruby 1.8.7 que parece indicar que é seguro para threads! (Thread correspondendo a uma regex a cada segundo - verificando no irb se as correspondências locais estão sendo superadas)
Joel
5
As variáveis ​​-1 $ relacionadas às expressões regulares não são globais, embora tenham um cifrão na frente.
Andrew Grimm
@AndrewGrimm Obrigado por apontar isso. Eu não estava ciente disso. Vou ter que mudar muito do código antigo: - /
Yossi
Você também pode fazer $1, $2... $9ou Regexp.last_match(1)conforme recomendado por rubocop
Edgar Ortega
6

Aqui está uma abordagem alternativa que fornece o mesmo resultado, mas não usa um switch. Se você colocar suas expressões regulares em uma matriz, poderá fazer algo assim:

res = [ /pat1/, /pat2/, ... ]
m   = nil
res.find { |re| m = foo.match(re) }
# Do what you will with `m` now.

Declarar mfora do bloco permite que ele ainda esteja disponível depois de findterminar com o bloco e findparará assim que o bloco retornar um valor verdadeiro, de modo que você obtém o mesmo comportamento de atalho que um switch fornece. Isso dá a você tudo MatchDataque você precisa (talvez você queira usar grupos de captura nomeados em seus regexes) e separa bem seus regexes de sua lógica de pesquisa (que pode ou não resultar em um código mais claro), você pode até carregar seus regexes de um arquivo de configuração ou escolha qual conjunto deles você deseja em tempo de execução.

mu é muito curto
fonte
Eu também estava pensando em thread safety usando a caseabordagem. Talvez você queira usar a abordagem de mu em um cenário encadeado, em vez de uma variável global com a abordagem de caso (?)
Casper