В предыдущем эпизоде мы рассмотрели Ruby Regular Expression API. Я хочу попробовать еще несколько языков, и наиболее очевидным для начала является Crystal.
Многие решения работают точно так же, как и в Ruby, но некоторые различия интересны.
Тестовый пример
В Crystal нет %W
, которая является одной из моих любимых функций Ruby, но в данном случае подойдет ее неинтерполирующий и гораздо менее потрясающий родственник %w
.
Вот тестовый пример:
%w[
2015-05-25
2016/06/26
27/07/2017
].each do |s|
p parse_date(s)
end
И ожидаемый результат:
[2015, 5, 25]
[2016, 6, 26]
[2017, 7, 27]
Решение 1
def parse_date(s)
case s
when %r[(dddd)-(dd)-(dd)]
[$1.to_i, $2.to_i, $3.to_i]
when %r[(dddd)/(dd)/(dd)]
[$1.to_i, $2.to_i, $3.to_i]
when %r[(dd)/(dd)/(dddd)]
[$3.to_i, $2.to_i, $1.to_i]
end
end
Самое простое решение работает точно так же, как и в Ruby, без каких-либо изменений.
Решение 2
#!/usr/bin/env crystal
def parse_date(s)
case s
when %r[(dddd)-(dd)-(dd)], %r[(dddd)/(dd)/(dd)]
[$1.to_i, $2.to_i, $3.to_i]
when %r[(dd)/(dd)/(dddd)]
[$3.to_i, $2.to_i, $1.to_i]
end
end
Группировка опций when
работает так же, как и в Ruby.
Решение 3
def parse_date(s)
case s
when %r[(dddd)([/-])(dd)2(dd)]
[$1.to_i, $3.to_i, $4.to_i]
when %r[(dd)/(dd)/(dddd)]
[$3.to_i, $2.to_i, $1.to_i]
end
end
Обратные ссылки также работают как в Ruby.
Решение 4
Теперь это не работает:
def parse_date(s)
case s
when %r[(dddd)-(dd)-(dd)|(dddd)/(dd)/(dd)]
[($1 || $4).to_i, ($2 || $5).to_i, ($3 || $6).to_i]
when %r[(dd)/(dd)/(dddd)]
[$3.to_i, $2.to_i, $1.to_i]
end
end
Причина в том, что в Ruby $1
может быть либо String
, либо nil
. В Crystal $1
является String
, поэтому если он не совпал, то обращение к нему будет ошибкой.
В Crystal также есть эквиваленты nil
$1?
, $2?
и т.д. Обратите внимание, что для того, чтобы все выражение не было nilable, мы не ставим ?
на последнем:
def parse_date(s)
case s
when %r[(dddd)-(dd)-(dd)|(dddd)/(dd)/(dd)]
[($1? || $4).to_i, ($2? || $5).to_i, ($3? || $6).to_i]
when %r[(dd)/(dd)/(dddd)]
[$3.to_i, $2.to_i, $1.to_i]
end
end
Решение 5
def parse_date(s)
case s
when %r[(dddd)-(dd)-(dd)|(dddd)/(dd)/(dd)|(dd)/(dd)/(dddd)]
[($1? || $4? || $9).to_i, ($2? || $5? || $8).to_i, ($3? || $6? || $7).to_i]
end
end
Зная то, что мы знаем, мы можем использовать тот же трюк, переписав ($1 || $4 || $9)
в ($1? || $4? || $9)
и так далее.
Решение 6
def parse_date(s)
case s
when
%r[(?<year>dddd)-(?<month>dd)-(?<day>dd)],
%r[(?<year>dddd)/(?<month>dd)/(?<day>dd)],
%r[(?<day>dd)/(?<month>dd)/(?<year>dddd)]
[$~["year"].to_i, $~["month"].to_i, $~["day"].to_i]
end
end
Использование именованных захватов работает так же, как и в версии для Ruby.
Решение 7
def parse_date(s)
case s
when %r[(?<year>dddd)-(?<month>dd)-(?<day>dd)|(?<year>dddd)/(?<month>dd)/(?<day>dd)|(?<day>dd)/(?<month>dd)/(?<year>dddd)]
[$~["year"].to_i, $~["month"].to_i, $~["day"].to_i]
end
end
Наличие групп захватов с одинаковыми именами работает так же, как и в Ruby, без изменений.
Решение 8
def parse_date(s)
case s
when %r[
(?<year>dddd)-(?<month>dd)-(?<day>dd) |
(?<year>dddd)/(?<month>dd)/(?<day>dd) |
(?<day>dd)/(?<month>dd)/(?<year>dddd)
]x
[$~["year"].to_i, $~["month"].to_i, $~["day"].to_i]
end
end
Так же как и флаг //x
— все просто работает.
Решение 9
def parse_date(s)
if %r[
(?<year>dddd)-(?<month>dd)-(?<day>dd) |
(?<year>dddd)/(?<month>dd)/(?<day>dd) |
(?<day>dd)/(?<month>dd)/(?<year>dddd)
]x =~ s
[year.to_i, month.to_i, day.to_i]
end
end
С другой стороны, это совершенно не поддерживается — единственным побочным эффектом совпадения регулярных выражений является переопределение переменной $~
($1
— это просто псевдоним для $[1]?
и т.д.). Соответствие регулярному выражению не может переопределять другие локальные переменные.
Мне не очень нравится эта особенность Ruby, поэтому неудивительно, что она не нашла своего применения здесь.
История на данный момент
Все работало без изменений или с минимальными изменениями. Это мой обычный опыт работы с Crystal. В большинстве случаев все просто работает.
Весь код находится на GitHub.
Следующий эпизод
В следующем эпизоде мы посмотрим, как другие языки справляются с этой проблемой.