Bagaimana cara menulis pernyataan switch Ruby (case… when) dengan regex dan backreferences?

89

Saya tahu bahwa saya dapat menulis pernyataan kasus Ruby untuk memeriksa kecocokan dengan ekspresi reguler. Namun, saya ingin menggunakan data pertandingan dalam pernyataan pengembalian saya. Sesuatu seperti semi-pseudocode ini:

foo = "10/10/2011"

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

Bagaimana saya bisa mencapai itu?

Terima kasih!


Sekadar catatan: Saya mengerti bahwa saya tidak akan pernah menggunakan pernyataan switch untuk kasus sederhana seperti di atas, tetapi itu hanya satu contoh. Pada kenyataannya, yang saya coba capai adalah mencocokkan banyak ekspresi reguler potensial untuk sebuah tanggal yang dapat ditulis dalam berbagai cara, dan kemudian menguraikannya dengan kelas Ruby Date yang sesuai.

Yuval Karmi
sumber
1
Ruby Date.parse memahami banyak format tanggal. Apakah kamu sudah mencobanya
raine
Meskipun tidak menjawab pertanyaan ini, Anda mungkin ingin melihat permata Kronis ...
DGM

Jawaban:

156

Referensi ke grup pencocokan ekspresi reguler selalu disimpan dalam variabel pseudo $1 untuk $9:

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

Anda juga dapat menggunakan $LAST_MATCH_INFOvariabel pseudo untuk melihat keseluruhan MatchDataobjek. Ini bisa berguna saat menggunakan tangkapan bernama:

case foo
when /^(?<number>[0-9][0-9])/
    print "the month is #{$LAST_MATCH_INFO['number']}"
else
    print "something else"
end
Yossi
sumber
1
@Yossi Apakah Anda memiliki sumber untuk komentar Anda tentang keamanan thread? Saya baru saja melakukan percobaan pada ruby ​​1.8.7 yang tampaknya menunjukkan bahwa ini aman untuk benang! (Utas mencocokkan regex setiap satu detik - memeriksa di irb jika pertandingan lokal semakin dipukul)
Joel
5
-1 $ variabel yang harus dilakukan dengan ekspresi reguler tidak global meskipun memiliki tanda dolar di depannya.
Andrew Grimm
@AndrewGrimm Terima kasih telah menunjukkan hal ini. Saya tidak menyadarinya. Saya harus mengubah banyak kode lama: - /
Yossi
Anda juga dapat melakukannya $1, $2... $9atau Regexp.last_match(1)seperti yang direkomendasikan oleh rubocop
Edgar Ortega
6

Berikut adalah pendekatan alternatif yang memberi Anda hasil yang sama tetapi tidak menggunakan sakelar. Jika Anda meletakkan ekspresi reguler Anda dalam sebuah array, Anda dapat melakukan sesuatu seperti ini:

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

Mendeklarasikan di mluar blok memungkinkannya untuk tetap tersedia setelah findselesai dengan blok dan findakan berhenti segera setelah blok mengembalikan nilai sebenarnya sehingga Anda mendapatkan perilaku pintasan yang sama dengan yang diberikan oleh sakelar. Ini memberi Anda sepenuhnya MatchDatajika Anda membutuhkannya (mungkin Anda ingin menggunakan grup penangkapan bernama di regex Anda) dan dengan baik memisahkan regex Anda dari logika pencarian Anda (yang mungkin atau mungkin tidak menghasilkan kode yang lebih jelas), Anda bahkan dapat memuat regex Anda dari config file atau pilih yang mana yang Anda inginkan pada waktu proses.

mu terlalu pendek
sumber
Saya juga berpikir tentang keamanan benang dengan menggunakan casependekatan ini. Mungkin Anda ingin menggunakan pendekatan mu dalam skenario berulir, daripada variabel global dengan pendekatan kasus (?)
Casper