정규식으로 숫자 추가하기


39

새로운 유형의 정규식 골프 챌린지를 시도하고 싶습니다. 정규식 대체 작업으로 사소한 계산 작업을 해결하도록 요청합니다. 이 작업을 더 쉽고 덜 수월하게하기 위해 여러 번의 대체 작업을 차례대로 적용 할 수 있습니다.

도전

우리는 간단하게 시작할 것입니다 : 두 개의 양의 정수를 포함하는 문자열이 10으로 구분 된 10 진수로 주어진 경우 ,그 합을 포함하는 문자열을 10 진수로 생성합니다. 아주 간단하게

47,987

로 바뀌어야한다

1034

당신의 대답은 임의의 양의 정수에 효과적입니다.

체재

모든 대답은 일련 의 대체 단계 여야하며 각 단계는 정규식과 대체 문자열로 구성됩니다. 선택적으로 시퀀스의 각 단계에 대해 문자열 변경이 중지 될 때까지 대체를 반복하도록 선택할 수 있습니다. 다음은 제출 예제입니다 ( 위의 문제를 해결 하지 못함 ).

Regex    Modifiers   Replacement   Repeat?
\b(\d)   g           |$1           No
|\d      <none>      1|            Yes
\D       g           <empty>       No

input 123,456이 주어지면 이 제출은 다음과 같이 입력을 처리합니다.

|123,|456

이제 두 번째 대체는 문자열 변경이 중지 될 때까지 루프에 적용됩니다.

1|23,|456
11|3,|456
111|,|456
111|,1|56
111|,11|6
111|,111|

마지막으로 세 번째 대체는 한 번 적용됩니다.

111111

루프의 종료 기준은 정규식이 일치하는지 여부가 아니라 문자열이 변경되는지 여부입니다. 즉, 일치하는 항목을 찾았지만 대체 항목은 일치하는 경우 종료 될 수도 있습니다.

채점

기본 점수는 제출시 대체 단계 수입니다. 반복되는 모든 교체는 10 단계로 계산됩니다 . 따라서 위의 예는 점수를 매 깁니다 1 + 10 + 1 = 12.

동점 인 경우 (2 차 점수는 아님) 보조 점수는 모든 단계의 크기의 합입니다. 각 단계 (정규식 부가 없이 분리), 수식 및 치환 문자열. 위의 예에서 이것은입니다 (6 + 1 + 3) + (3 + 0 + 2) + (2 + 1 + 0) = 18.

기타 규칙

모든 정규식 풍미를 사용할 수 있지만 (표시해야 함) 모든 단계는 동일한 풍미를 사용해야합니다. 또한 교체 콜백 또는 Perl 코드를 평가하는 Perl의 수정 자 와 같은 풍미의 호스트 언어 기능을 사용 해서는 안됩니다e . 모든 조작은 정규식 대체를 통해 독점적으로 발생해야합니다.

각 단일 교체가 모든 발생을 대체하는지 또는 단일 교체 만 대체하는지에 따라 사용자의 선호도와 수정 자에 따라 달라집니다. 예를 들어 ECMAScript 풍미를 선택하면 g수정자를 사용하지 않는 한 기본적으로 한 단계 만 한 번만 발생합니다 . 반면에 .NET 맛을 사용하는 경우 각 단계는 항상 모든 발생을 대체합니다.

단일 및 전역 대체에 대한 대체 방법이 다른 언어 (예 : Ruby 's subvs. gsub)의 경우 단일 대체가 기본값이며 전역 대체를 g수정 자 처럼 취급 합니다.

테스팅

선택한 맛이 .NET 또는 ECMAScript 인 경우 Retina 를 사용하여 제출물을 테스트 할 수 있습니다 (알고 있습니다 . 모노에서도 작동합니다). 다른 맛의 경우 대체를 순서대로 적용하는 호스트 언어로 작은 프로그램을 작성해야 할 것입니다. 그렇다면이 테스트 프로그램을 답에 포함 시키십시오.


누구든지 이런 유형의 도전 과제를 부르는 것이 좋다면 의견을 남기십시오! :) (나중에 더 많은 일을하고있는 경우를 대비하여)
Martin Ender

사람이 같은 사람들도 즐길 수있는 추가없이 추가번호없이 곱하기
토비 Speight

Retina의 정규 표현식 "맛"은 유효한 제출물입니까? : P (골프는 물론 두 개의 숫자를 더할 수
있다는 사실에 자부심을 느낍니다

@icrieverytim Retina의 풍미는 .NET 풍미입니다.
Martin Ender

그러나 Retina에는 .NET에없는 기능이 있습니다.
완전히 인간적인

답변:


32

.NET 맛, 점수 : 2

Regex        Modifiers  Replacement  Repeat?
<empty>      <none>     9876543210   No
<see below>  x          <empty>      No

나는 아직 골프를 귀찮게하지 x않고 공백을 무시하기위한 것입니다.

먼저 9876543210각 위치에 삽입 한 다음 원래 문자와 합계의 현재 숫자가 아닌 문자를 삭제하십시오.

큰 정규식 (공백과 주석이없는 1346 바이트) :

# If the length of the left number <= right number, delete every digit on the left.
.(?=.*,(?<=^(?<len>.)*,)(?<-len>.)*(?(len)(?!)))|

# Do the opposite if it is not the case.
.(?<=(?(len)(?!))(?<-len>.)*(?=(?<len>.)*$),.*)|

# Remove leading zeros.
(?<=(^|,).{9})0|

# Delete everything that is not the current digit of the sum.
.(?!
    # For digits in the left part:
    (?<cur>.){0,9}               # cur = the matched digit
    (?=(.{11})*,)                # and find the position before the next digit.
    (?<first>)                   # first = true
    (                            # Loop on the less significant digits:
        (?<cur>){10}             # cur += 10
        (?<=                     # cur -= the current digit in this number.
            (
                0|^|
                1(?<-cur>)|
                2(?<-cur>){2}|
                3(?<-cur>){3}|
                4(?<-cur>){4}|
                5(?<-cur>){5}|
                6(?<-cur>){6}|
                7(?<-cur>){7}|
                8(?<-cur>){8}|
                9(?<-cur>){9}
            )
            .{10}
        )
        (?=
            (?<pos>.{11})*,      # pos = which digit it is.
            .*$(?<=              # cur -= the current digit in the other number.
                (
                    0|,|
                    1(?<-cur>)|
                    2(?<-cur>){2}|
                    3(?<-cur>){3}|
                    4(?<-cur>){4}|
                    5(?<-cur>){5}|
                    6(?<-cur>){6}|
                    7(?<-cur>){7}|
                    8(?<-cur>){8}|
                    9(?<-cur>){9}
                )
                .{10}
                (?(pos)(?!))     # Assert pos = 0.
                                 # Skip pos input digits from the end.
                                 # But stop and set pos = 0 if the comma is encountered.
                ((?<-pos>\d{11})|(?<=(?>(?<-pos>.)*),.{10}))*
            )
        )
        (?(first)                # If first:
            (?>((?<-cur>){10})?) #  cur -= 10 in case there is no carry.
                                 #  Assert cur = 0 or 1, and if cur = 1, set cur = 10 as carry.
            (?(cur)(?<-cur>)(?(cur)(?!))(?<cur>){10})
            (?<-first>)          #  first = false
        |                        # Else:
                                 #  cur is 10 or 20 at the beginning of an iteration.
                                 #  It must be 1 to 11 to make the equation satisfiable.
            (?<-cur>)            #  cur -= 1
            (?(cur)              #  If cur > 0:
                                 #   cur -= max(cur, 9)
                (?(cur)(?<-cur>)){9}
                (?(cur)          #   If cur > 0:
                                 #    Assert cur = 1 (was 11) and set cur = 10.
                    (?<-cur>)(?(cur)(?!))(?<cur>){10}
                |                #   Else:
                    .*(?=,)      #    cur was 2 to 10, break from the loop.
                )
            )                    #  Else cur is 0 (was 1) and do nothing.
        )
        (.{11}|,)                # Jump to the next digit.
    )*(?<=,)(?(cur)(?!))         # End the loop if it is the last digit, and assert cur = 0.
|
    # Do the same to the right part. So the sum will be calculated two times.
    # Both are truncated to the original length of the number on that side + 1.
    # Only the sum on the longer side will be preserved in the result.
    (?<cur>\d){0,9}
    (?=(\d{11})*$)
    (?<first>)
    (
        (?<cur>){10}
        (?<=
            (
                0|,|
                1(?<-cur>)|
                2(?<-cur>){2}|
                3(?<-cur>){3}|
                4(?<-cur>){4}|
                5(?<-cur>){5}|
                6(?<-cur>){6}|
                7(?<-cur>){7}|
                8(?<-cur>){8}|
                9(?<-cur>){9}
            )
            .{10}
        )
        (?=
            (?<pos>.{11})*$
            (?<=
                (
                    0|^|
                    1(?<-cur>)|
                    2(?<-cur>){2}|
                    3(?<-cur>){3}|
                    4(?<-cur>){4}|
                    5(?<-cur>){5}|
                    6(?<-cur>){6}|
                    7(?<-cur>){7}|
                    8(?<-cur>){8}|
                    9(?<-cur>){9}
                )
                .{10}
                (?(pos)(?!))
                ((?<-pos>\d{11})|(?<=^.{10})(?=(?>(?<-pos>.)*)))*
                ,.*
            )
        )
        (?(first)
            (?>((?<-cur>){10})?)
            (?(cur)(?<-cur>)(?(cur)(?!))(?<cur>){10})
            (?<-first>)
        |
            (?<-cur>)
            (?(cur)
                (?(cur)(?<-cur>)){9}
                (?(cur)
                    (?<-cur>)(?(cur)(?!))(?<cur>){10}
                |
                    .*$(?<end>)
                )
            )
        )
        (.{11}|$(?<end>))
    )*(?<-end>)(?(cur)(?!))
)

이것은 내가 Manufactoria의 최종 수준을 생각하게 만들었습니다. 그러나 더 이상 "정규"가 아닌 .NET 정규식은 PH의 문제를 해결할 수 있다고 생각합니다. 그리고 이것은 L의 알고리즘 일뿐입니다.


4
모든 hail .NET 밸런싱 그룹.
Sp3000

먼저 5 단계 프로세스가 꽤 좋다고 생각했습니다. 그런 다음 누군가가 절반 길이의 솔루션을 요구하는 것을 보았습니다. 그럼 ... 이거 이것도 정규식으로 간주됩니까?
John Dvorak

1
@JanDvorak 이론적 인 "정규 표현"의 경우, no. "정규식"의 경우, 모든 사람들이 이것을 정규식이라고 부르며 거의 모든 정규식 풍미에는 이와 같은 것이 있습니다. Microsoft는 여전히 공식적으로 " 정규 표현식 " 이라고 부릅니다 .
jimmy23013

와우, 이것은 놀라운 일입니다!
user230910

6

점수 : 24

나는 이것이 효과가 있다고 생각한다 ...

Regex                                                                                                                       Modifiers   Replacement             Repeat?
(?|(\d*)(\d)(,\d*)(\d)|(^,?\d*)(\d)|, |^,)                                                                                  <none>      $1$3 $2$4               Yes
$                                                                                                                           <none>      ;111111111234567890     No
0|(?|(;.*)|9(?=.*(1{9}))|8(?=.*(1{8}))|7(?=.*(1{7}))|6(?=.*(1{6}))|5(?=.*(1{5}))|4(?=.*(1{4}))|3(?=.*(111))|2(?=.*(11)))    g           $1                      No
 1{10}                                                                                                                      <none>      1                       Yes
 (?|1{9}(?=.*(9))|1{8}(?=.*(8))|1{7}(?=.*(7))|1{6}(?=.*(6))|1{5}(?=.*(5))|1{4}(?=.*(4))|1{3}(?=.*(3))|1{2}(?=.*(2))|)       g            $1                     No
 (?!\d)(?=.*(0))| |;.*                                                                                                      g           $1                      No

나는 개별 정규식을 아직 골프를 타는 데 많은 시간을 소비하지 않았습니다. 곧 설명을 게시하려고하지만 지금 늦어지고 있습니다. 그 동안 각 단계 사이의 결과는 다음과 같습니다.

'47,987'
' 9 48 77'
' 9 48 77;111111111234567890'
' 111111111 111111111111 11111111111111;111111111234567890'
'1  111 1111;111111111234567890'
'1  3 4;111111111234567890'
'1034'

풀 펄 프로그램 :

$_ = <>;
chomp;

do {
    $old = $_;
    s/(?|(\d*)(\d)(,\d*)(\d)|(^,?\d*)(\d)|, |^,)/$1$3 $2$4/;
} while ($old ne $_);

s/$/;111111111234567890/;

s/0|(?|(;.*)|9(?=.*(1{9}))|8(?=.*(1{8}))|7(?=.*(1{7}))|6(?=.*(1{6}))|5(?=.*(1{5}))|4(?=.*(1{4}))|3(?=.*(111))|2(?=.*(11)))/$1/g;

do {
    $old = $_;
    s/ 1{10}/1 /;
} while ($old ne $_);

s/ (?|1{9}(?=.*(9))|1{8}(?=.*(8))|1{7}(?=.*(7))|1{6}(?=.*(6))|1{5}(?=.*(5))|1{4}(?=.*(4))|1{3}(?=.*(3))|1{2}(?=.*(2))|)/ $1/g;

s/ (?!\d)(?=.*(0))| |;.*/$1/g;

print "$_\n";

이것은 내 자신의 개념 증명과 매우 비슷합니다. :) 그래도 7 개의 비 루프 대체가 있었지만,이를 유지하기 위해 특별히 노력하지 않았습니다.
Martin Ender

트윗 담아 가기 나는 마지막 두 서브도 합병 될 수 있다고 확신하지만, 하루 동안 충분했습니다 ...
grc

모든 주요 공간은 의도적입니까?
Optimizer

@Optimizer 예. 더 나은 캐릭터를 선택해서 죄송합니다.
grc

5

모든 정규식 맛, 41

    s/0/d/g
    ...
    s/9/dxxxxxxxxx/g
rep s/xd/dxxxxxxxxxxx/g
    s/[d,]//g
rep s/(^|d)xxxxxxxxxx/xd/g
    s/(^|d)xxxxxxxxx/9/g
    ...
    s/(^|d)x/1/g
    s/d/0/g

단항 해보자. d숫자 순서 구분 기호를 제공 x하고 값을 저장합니다. 먼저 각 자릿수를 해제 한 다음 x10 승수를 왼쪽으로 누른 다음 모든 구분 기호를 뺀 다음 승수를 다시 삽입 한 다음 각 순서를 숫자로 다시 변환합니다.


5

.NET 정규식, 14

user23013의 솔루션만큼 좋지는 않지만 재미있었습니다. 대체품에는 수정자가 없습니다.

.NET 정규식의 이유는 한 번 그룹을 균형 조정하기 때문이 아닙니다. .NET을 사용하는 Retina로 테스트 한 결과 가변 길이 lookbehinds가 많은 도움이되었다는 것을 알았습니다.

교체 1 (반복 = 아니오)

정규식 :

\d(?=\d+$)|\d(?=\d+,)|\d(?=,(\d+)$)|(?<=(\d+),\d*)\d$

바꿔 놓음

0$1$2

패딩이 같은 수의 선행 0을 갖도록 두 숫자를 교환하십시오.

교체 2 (반복 = 아니오)

정규식 :

(\d+)

바꿔 놓음:

 $1

각 숫자 앞에 공백을 추가하십시오

교체 3 (반복 = 아니오)

$

바꿔 놓음:

&0 ~00000 ~00101 ~00202 ~00303 ~00404 ~00505 ~00606 ~00707 ~00808 ~00909 ~01001 ~01102 ~01203 ~01304 ~01405 ~01506 ~01607 ~01708 ~01809 ~01910 ~02002 ~02103 ~02204 ~02305 ~02406 ~02507 ~02608 ~02709 ~02810 ~02911 ~03003 ~03104 ~03205 ~03306 ~03407 ~03508 ~03609 ~03710 ~03811 ~03912 ~04004 ~04105 ~04206 ~04307 ~04408 ~04509 ~04610 ~04711 ~04812 ~04913 ~05005 ~05106 ~05207 ~05308 ~05409 ~05510 ~05611 ~05712 ~05813 ~05914 ~06006 ~06107 ~06208 ~06309 ~06410 ~06511 ~06612 ~06713 ~06814 ~06915 ~07007 ~07108 ~07209 ~07310 ~07411 ~07512 ~07613 ~07714 ~07815 ~07916 ~08008 ~08109 ~08210 ~08311 ~08412 ~08513 ~08614 ~08715 ~08816 ~08917 ~09009 ~09110 ~09211 ~09312 ~09413 ~09514 ~09615 ~09716 ~09817 ~09918 ~10001 ~10102 ~10203 ~10304 ~10405 ~10506 ~10607 ~10708 ~10809 ~10910 ~11002 ~11103 ~11204 ~11305 ~11406 ~11507 ~11608 ~11709 ~11810 ~11911 ~12003 ~12104 ~12205 ~12306 ~12407 ~12508 ~12609 ~12710 ~12811 ~12912 ~13004 ~13105 ~13206 ~13307 ~13408 ~13509 ~13610 ~13711 ~13812 ~13913 ~14005 ~14106 ~14207 ~14308 ~14409 ~14510 ~14611 ~14712 ~14813 ~14914 ~15006 ~15107 ~15208 ~15309 ~15410 ~15511 ~15612 ~15713 ~15814 ~15915 ~16007 ~16108 ~16209 ~16310 ~16411 ~16512 ~16613 ~16714 ~16815 ~16916 ~17008 ~17109 ~17210 ~17311 ~17412 ~17513 ~17614 ~17715 ~17816 ~17917 ~18009 ~18110 ~18211 ~18312 ~18413 ~18514 ~18615 ~18716 ~18817 ~18918 ~19010 ~19111 ~19212 ~19313 ~19414 ~19515 ~19616 ~19717 ~19818 ~19919

캐리 비트 ( &0)와의 거대한 조회 테이블을 추가 <c> <a> <b> <carry of a+b+c> <last digit of a+b+c>합니다.

교체 4 (반복 = 예)

정규식 :

(?<=(\d),.*(\d)&)(\d)(?=.*~\3\1\2(.))|(\d)(?=,.*\d&)|(?<=\d,.*)(\d)(?=&)|^(?=.* .*(\d),.*(\d)&(\d).*~\9\7\8.(.))

바꿔 놓음:

$4$10

각 숫자의 마지막 숫자를 계속 사용하고 (sum, carry)를 찾으십시오. 문자열의 시작 부분에 합계를 넣고 캐리를 교체하십시오.

교체 5 (반복 = 아니오)

정규식 :

^0*| .*

바꿔 놓음:

<empty>

청소하십시오.

예제 실행

Repl no.        String
(input)         1428,57
1               000057,001428
2                000057, 001428
3                000057, 001428&0 <lookup table>
4               5 00005, 00142&1 <lookup table>
4               85 0000, 0014&0 <lookup table>
4               485 000, 001&0 <lookup table>
4               1485 00, 00&0 <lookup table>
4               01485 0, 0&0 <lookup table>
4               001485 , &0 <lookup table>
5               1485

(12 단계를 얻을 수있는 몇 가지 단계를 결합하면 꽤 지저분하고 어쨌든 이기지 않기 때문에이 우아한 버전을 대신 유지할 것이라고 생각합니다.)


4

점수 : 50 40 31 21

이 훌륭한 도전에 감사드립니다. 이 솔루션은 매우 우아하지는 않지만 제한 사항을 감안할 때 일반적으로 출력에서 ​​숫자를 처리하는 방법을 볼 수 없었습니다.

이 솔루션은 때때로 일치하지 않는 캡처 그룹을 제공하며 이러한 그룹이 발생하면 비어 있습니다. 이것은 일반적으로 경고를 생성하지만 Perl에서 작동합니다.

Regex 1:     (((((((((9)|8)|7)|6)|5)|4)|3)|2)|1)|0                                            
Modifiers:   g
Replacement: <$1$2$3$4$5$6$7$8$9>             
Repeat:      no

Regex 2:     (.*)<(\d*)>(,.*)<(\d*)>|(.*)<(\d*)>(.*)|(?:(^[^<]*)b(\d*)c)?(b)\d{9}(\d)(\d*)(c)
Modifiers:   none 
Replacement: \8\1\5\3b$9$11\2\6\4c\7$10$12$13 
Repeat:      yes

Regexes 3-12: ,?baaaaaaaaac
Modifiers:    g
Replacement:  9 etc. (one for each digit)

중간 결과의 설명 및 인쇄와 함께 전체 Perl 코드 샘플 :

no warnings;
use 5.16.0;

$_ = '47,987';

#Convert numbers to beans
s/(((((((((9)|8)|7)|6)|5)|4)|3)|2)|1)|0/<$1$2$3$4$5$6$7$8$9>/g;

say;
my $last;

#Combine pairs of digits, starting with least significant.
do {
    $last=$_;
    s/(.*)<(\d*)>(,.*)<(\d*)>|(.*)<(\d*)>(.*)|(?:(^[^<]*)b(\d*)c)?(b)\d{9}(\d)(\d*)(c)/\8\1\5\3b$9$11\2\6\4c\7$10$12$13/;
    say;
}
while ($last ne $_);

#Convert beans back to numbers.
s/,?b\d{9}c/9/g;
s/,?b\d{8}c/8/g;
s/,?b\d{7}c/7/g;
s/,?b\d{6}c/6/g;
s/,?b\d{5}c/5/g;
s/,?b\d{4}c/4/g;
s/,?b\d{3}c/3/g;
s/,?b\d{2}c/2/g;
s/,?b\d{1}c/1/g;
s/,?bc/0/g;

say;

업데이트 : 두 개의 반복 정규 표현식을 결합하여 10을 절약 할 수있었습니다.

업데이트 2 : 단일 정규식으로 입력 숫자 변환을 해독했습니다.

업데이트 3 : 단일 루핑 정규식으로 축소되었습니다.


재미있는 해결책. :) 대체 문자열에서 중괄호는 무엇을합니까? ${1}와 다른 가요 $1? 또한 관계가있는 경우 바이트 수를 포함시킬 수 있습니다.
Martin Ender

@ MartinBüttner에서 중괄호는 변수 이름을 변수에있을 수있는 다른 문자와 간단히 구분합니다.

아, 말이 되네요. 감사.
Martin Ender

@ MartinBüttner, \1대신 몇 가지 문자를 저장하는 등 을 사용하도록 변경했습니다 .
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.