트랙
/
Factor
Factor
/
연습 문제
/
장부 망꾼
장부 망꾼

장부 망꾼

학습 연습 문제

소개

이미 기본은 알고 있어요. \d나 [A-Z] 같은 문자 클래스, {4}나 + 같은 수량자, 그리고 matches?, all-matching-subseqs, re-replace 같은 단어들요. 이번 연습 문제에서는 정규 표현식 가운데 구조와 문맥을 표현하는 부분을 다뤄요. 이들은 같은 regexp 어휘에 속하고, 같은 R/ / 리터럴로 작성해요.

앵커

앵커는 문자 대신 위치를 매칭해요.

^    ! the start of the string
$    ! the end of the string
\b   ! a word boundary

$는 문자열의 끝을 의미하므로, 달러 기호 자체를 나타낼 때는 \$라고 써요.

단어 경계(\b)는 단어 문자가 단어 문자가 아닌 것과 만나는 지점이에요. 단어 문자는 글자와 숫자, 밑줄이고, 그 밖의 모든 것(공백, 구두점, 문자열의 양 끝)은 경계로 쳐요. 그래서 \bcat\b는 cat이 단독으로 있을 때만 매칭해요.

USING: regexp ;

"cat nap" R/ \bcat\b/ re-contains? .   ! => t
"scatter" R/ \bcat\b/ re-contains? .   ! => f   (cat sits inside a word)

그룹화와 선택

소괄호 ( )는 패턴의 일부를 묶어서, 수량자나 |("이것 또는 저것")가 그룹 전체에 적용되도록 해요.

USING: regexp ;

"abab" R/ (ab)+/ matches? .                ! => t   (+ applies to "ab")
"dog"  R/ cat|dog/ matches? .              ! => t   (either word)
"$100.50" R/ \$\d+(\.\d{2})?/ matches? .   ! => t
"$100"    R/ \$\d+(\.\d{2})?/ matches? .   ! => t   (the group is optional)

마지막 패턴은 "달러 기호, 하나 이상의 숫자, 그다음 마침표와 두 자리 숫자로 이루어진 선택적 그룹"이라고 읽어요.

참고: Factor에서 그룹은 패턴을 구성할 뿐이에요. 일부 정규식 도구와 달리, 그룹이 매칭한 텍스트를 다시 꺼낼 수 없어요. 캡처 그룹 추출이 없고, \1 같은 역참조도 지원하지 않아요. 어떤 기준점 주변의 텍스트를 뽑아내려면, 대신 룩어라운드(아래)를 사용해요.

룩어라운드: 문맥으로 매칭하되 소비하지 않기

룩어라운드는 현재 위치 바로 앞이나 바로 뒤에 오는 것을 확인하지만, 그 문자들을 매칭의 일부로 만들지 않아요. "너비가 0"이기 때문에, 검사하는 내용이 결과에 포함되지 않아요.

(?=...)    ! lookahead:           ... must follow
(?!...)    ! negative lookahead:  ... must NOT follow
(?<=...)   ! lookbehind:          ... must come before
(?<!...)   ! negative lookbehind: ... must NOT come before

룩어라운드는 선택적이에요. 같은 문자라도 옆에 무엇이 있는지에 따라 매칭되기도 하고 안 되기도 해요. 평범한 \d+는 모든 숫자를 잡아요. 룩비하인드와 룩어헤드는 알맞은 이웃을 가진 것만 남겨요.

USING: regexp ;

"3 for $10 or 10 for $30" R/ \d+/ all-matching-subseqs .
! => { "3" "10" "10" "30" }   (every number)

"3 for $10 or 10 for $30" R/ (?<=\$)\d+/ all-matching-subseqs .
! => { "10" "30" }            (only the prices — the bare 3 and 10 drop)

"buy 2 at 15% off, 4 at 30% off" R/ \d+(?=%)/ all-matching-subseqs .
! => { "15" "30" }            (only the discounts — the bare 2 and 4 drop)

옵션

닫는 / 뒤에 문자를 붙이면 패턴 전체의 동작이 바뀌어요. i는 대소문자를 구분하지 않게 해요.

USING: regexp ;

"REFUND"  R/ refund/i matches? .       ! => t
"Refund"  R/ refund/i matches? .       ! => t

각 매칭 변환하기

all-matching-subseqs는 매칭된 텍스트를 그대로 줘요. map-matches는 한 걸음 더 나아가, 각 매칭에 대해 콰테이션을 실행하고 결과를 모아요. 콰테이션은 매칭의 start 인덱스, end 인덱스, 그리고 전체 문자열을 받아요. subseq가 이 세 가지를 매칭된 텍스트로 바꿔 주고, 그다음 변환하면 돼요.

map-matches ( string regexp quot: ( start end string -- obj ) -- seq )
USING: math regexp sequences ;

"a1 b22 c333" R/ \d+/ [ subseq length ] map-matches .
! => { 1 2 3 }     (the length of each run of digits)

부수 효과만을 위한 사촌 격인 each-match는 아무것도 모으지 않고 각 매칭에 대해 콰테이션을 실행해요.

지침

작은 회사의 장부를 관리하고 있는데, 매일 거래 내역이 정리되지 않은 텍스트 줄로 들어와요. 정규 표현식으로 금액이 올바른지 검증하고, 숫자를 표시하는 기호를 기준으로 값을 뽑아내고, 자세히 살펴봐야 할 줄에 표시를 달 거예요.

1. 금액 검증하기

올바른 형식의 금액은 $와 하나 이상의 숫자, 그리고 선택 사항인 소수 부분으로 이루어져요. 소수 부분은 점 뒤에 정확히 두 자리 숫자가 오는 형태예요. $100 또는 $100.50은 올바른 금액이지만, $100.5는 아니에요. valid-amount?를 정의해서, 전체 문자열이 올바른 형식의 금액일 때 t를 반환하게 해요.

"$100.50" valid-amount? .   ! => t
"$100"    valid-amount? .   ! => t
"$100.5"  valid-amount? .   ! => f

2. 달러 금액 뽑아내기

dollar-amounts를 정의해서, 줄에서 $ 바로 뒤에 오는 모든 숫자를 순서대로 반환하게 해요. 이때 $는 빼고 숫자만 반환해요.

"spent $100 and $25 today" dollar-amounts .
! => { "100" "25" }

3. 백분율 뽑아내기

percentages를 정의해서, 바로 뒤에 %가 오는 모든 숫자를 반환하게 해요.

"up 5% then down 12%" percentages .
! => { "5" "12" }

4. 줄에 표시하기

flagged?를 정의해서, 대소문자가 어떻게 섞여 있든 줄에 refund나 chargeback이 나오면 t를 반환하게 해요.

"Issued a REFUND today" flagged? .   ! => t
"a normal sale"         flagged? .   ! => f
GitHub에서 편집 링크가 새 창이나 탭에서 열려요
Factor Exercism

장부 망꾼 문제를 시작해 볼 준비가 됐나요?

Exercism에 가입하고 Factor 트랙을 개념 47개연습 문제 163개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.