오픈 소스 모험: 에피소드 81: Raku 정규식 API 탐색

4357 단어 regexrakuperl
이전 세 개의 에피소드에서 Ruby, Crystal 및 Python의 정규식 API를 살펴보았으므로 Raku에서 동일한 연습을 수행하여 이 작업을 마치겠습니다.

문제는 동일합니다. 여러 날짜 형식이 있고 일치하는 형식에서 정보를 추출하려고 합니다.

3개의 정규 표현식으로 하고 있지만 실제로는 수백 개가 있을 수 있습니다. 정규식 목록을 사용하여 순진하게 수행하려면 대량의 코드 복제와 정규식 엔진에 대한 많은 호출이 필요합니다. 이는 일반적으로 한 번 일치a|b|c|...하는 것보다 훨씬 느립니다.

문제




#!/usr/bin/env raku

use JSON::Fast;

for qw[2015-05-25 2016/06/26 27/07/2017] {
  say to-json(parse_date($_), :!pretty)
}


예상 출력은 다음과 같습니다.

[2015,5,25]
[2016,6,26]
[2017,7,27]


솔루션 1




sub parse_date($s) {
  if $s ~~ /(\d\d\d\d)\-(\d\d)\-(\d\d)/ {
    [+$0, +$1, +$2]
  } elsif $s ~~ /(\d\d\d\d)\/(\d\d)\/(\d\d)/ {
    [+$0, +$1, +$2]
  } elsif $s ~~ /(\d\d)\/(\d\d)\/(\d\d\d\d)/ {
    [+$2, +$1, +$0]
  }
}


첫 번째 방법은 가능한 모든 정규식을 순차적으로 나열하는 것입니다.

Raku에 익숙하지 않은 경우 알아두어야 할 몇 가지 사소한 사항이 있습니다.
  • 일치 그룹은 $0가 아닌 $1에서 번호가 매겨집니다.
  • 이러한 변수는 다른 언어에서처럼 String 유형이 아니라 Match
  • to-json 객체를 문자열이나 ot 숫자로 변환하지 않고는 Match 할 수 없으므로 [$0, $1, $2]를 반환하면 충돌이 발생합니다
  • .
  • + 숫자로 변환
  • 일치 연산자는 ~~가 아니라 =~입니다.
  • \d0에서 9까지가 아니라 유니코드 숫자입니다.

  • 또한 고려해야 할 더 큰 사항이 있습니다. 다른 언어에서는 / 또는 -와 같은 알 수 없는 구두점을 문자 그대로 정규식에 사용할 수 있습니다. 돌이켜보면 이것은 이전 버전과의 호환성을 깨뜨리지 않고 새로운 정규식 구문을 추가하는 것을 방지하기 때문에 실수였습니다. Raku 힘은 현재 사용되지 않는 모든 구두점을 벗어나므로 미래의 어느 시점에서 - 또는 /에 어떤 의미를 부여할 수 있습니다.

    어쨌든 다른 언어의 솔루션 1과 마찬가지로 코드 중복과 순차 일치로 인한 성능 저하라는 두 가지 문제가 있습니다.

    솔루션 2




    sub parse_date($_) {
      if /(\d\d\d\d)\-(\d\d)\-(\d\d)/ {
        [+$0, +$1, +$2]
      } elsif /(\d\d\d\d)\/(\d\d)\/(\d\d)/ {
        [+$0, +$1, +$2]
      } elsif /(\d\d)\/(\d\d)\/(\d\d\d\d)/ {
        [+$2, +$1, +$0]
      }
    }
    


    Perl에서와 마찬가지로 ~~ 를 사용할 필요가 없습니다. 부울 컨텍스트에서 정규식을 사용하면 자동으로 $_ 와 일치합니다.

    해결책 3




    sub parse_date($_) {
      if /(\d\d\d\d)\-(\d\d)\-(\d\d)/ or /(\d\d\d\d)\/(\d\d)\/(\d\d)/ {
        [+$0, +$1, +$2]
      } elsif /(\d\d)\/(\d\d)\/(\d\d\d\d)/ {
        [+$2, +$1, +$0]
      }
    }
    


    그룹이 동일한 순서이면 코드 중복을 줄일 수 있습니다.

    해결책 4




    sub parse_date($_) {
      if /(\d\d\d\d)\-(\d\d)\-(\d\d) | (\d\d\d\d)\/(\d\d)\/(\d\d)/ {
        [+$0, +$1, +$2]
      } elsif /(\d\d)\/(\d\d)\/(\d\d\d\d)/ {
        [+$2, +$1, +$0]
      }
    }
    


    무슨 일이야? Raku에서 $0 , $1 , $2 는 표현식에서 N번째 일치 그룹을 의미하는 것이 아니라 실제로 일치하는 N번째 일치 그룹을 의미합니다(적어도 | 대안의 경우 전체 스토리가 더 복잡합니다)!
    +($0 or $3 or $6) 와 같은 넌센스 없이 원하는 만큼 많은 표현을 할 수 있어 좋습니다.

    반면에 순서가 다른 경우 대안을 수행할 방법이 없음을 의미합니다.

    솔루션 5




    sub parse_date($_) {
      if /(\d\d\d\d)\-(\d\d)\-(\d\d) |
          (\d\d\d\d)\/(\d\d)\/(\d\d) |
          (\d\d)\/(\d\d)\/(\d\d\d\d)/ {
        [+$0, +$1, +$2]
      }
    }
    


    이것은 실제로 작동하지 않습니다. 블록은 일치하는 분기를 알 수 없으므로 그룹이 YMD 또는 DMY 순서인지 알 수 없습니다.

    해결책 6




    sub parse_date($_) {
      if /
        $<y>=(\d\d\d\d) \- $<m>=(\d\d) \- $<d>=(\d\d) |
        $<y>=(\d\d\d\d) \/ $<m>=(\d\d) \/ $<d>=(\d\d) |
        $<d>=(\d\d) \/ $<m>=(\d\d) \/ $<y>=(\d\d\d\d)
        / {
        [+$<y>, +$<m>, +$<d>]
      }
    }
    


    명명된 캡처로 이 문제를 해결할 수 있으며 훌륭하게 작동합니다.

    지금까지의 이야기



    구문과 API는 모두 기존의 정규 표현식과 매우 다르지만 결국 필요한 모든 것을 얻었습니다.

    All the code is on GitHub .

    다음에 온다



    정규식 API를 개선할 수 있는 방법에 대한 다른 게시물을 작성할 계획이었지만, 우리가 탐색한 API(Python의 실망스러울 정도로 제한적인 API 제외)에는 실제로 내가 말하고 싶었던 대부분의 기능을 포함하는 기능이 거의 없었습니다. 실제 세계에서는 아직 사용되지 않았습니다.

    그래서 다음 에피소드는 완전히 다른 것에 관한 것입니다.

    좋은 웹페이지 즐겨찾기