Thêm số bằng Regex


39

Tôi muốn thử một loại thử thách golf regex mới, yêu cầu bạn giải quyết các nhiệm vụ tính toán không cần thiết mà không có gì ngoài sự thay thế regex. Để làm cho điều này trở nên khả thi hơn và ít việc vặt hơn, bạn sẽ được phép áp dụng một số thay thế, lần lượt từng cái một.

Các thách thức

Chúng ta sẽ bắt đầu đơn giản: đưa ra một chuỗi chứa hai số nguyên dương, dưới dạng các số thập phân cách nhau bởi a ,, tạo ra một chuỗi chứa tổng của chúng, cũng như một số thập phân. Vì vậy, rất đơn giản

47,987

nên biến thành

1034

Câu trả lời của bạn nên làm việc cho các số nguyên dương tùy ý.

Định dạng

Mỗi câu trả lời phải là một chuỗi các bước thay thế, mỗi bước bao gồm một biểu thức chính quy và chuỗi thay thế. Tùy chọn, đối với mỗi bước trong chuỗi, bạn có thể chọn lặp lại thay thế cho đến khi chuỗi ngừng thay đổi. Dưới đây là một ví dụ đệ trình ( không giải quyết được vấn đề trên):

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

Với đầu vào 123,456, đệ trình này sẽ xử lý đầu vào như sau: thay thế đầu tiên được áp dụng một lần và mang lại:

|123,|456

Bây giờ thay thế thứ hai được áp dụng trong một vòng lặp cho đến khi chuỗi ngừng thay đổi:

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

Và cuối cùng, sự thay thế thứ ba được áp dụng một lần:

111111

Lưu ý rằng tiêu chí chấm dứt cho các vòng lặp là liệu chuỗi có thay đổi hay không, liệu regex có tìm thấy kết quả khớp hay không. (Nghĩa là, nó cũng có thể chấm dứt nếu bạn tìm thấy một trận đấu nhưng sự thay thế giống hệt với trận đấu.)

Chấm điểm

Điểm chính của bạn sẽ là số bước thay thế trong bài nộp của bạn. Mỗi lần thay thế lặp đi lặp lại sẽ được tính trong 10 bước. Vì vậy, ví dụ trên sẽ ghi điểm 1 + 10 + 1 = 12.

Trong trường hợp (không quá khó xảy ra) của một chiếc cà vạt, điểm phụ là tổng của kích thước của tất cả các bước. Đối với mỗi bước thêm regex ( không có dấu phân cách), bộ sửa đổi và chuỗi thay thế. Đối với ví dụ trên đây sẽ là (6 + 1 + 3) + (3 + 0 + 2) + (2 + 1 + 0) = 18.

Quy tắc linh tinh

Bạn có thể sử dụng bất kỳ hương vị regex nào (mà bạn nên chỉ ra), nhưng tất cả các bước phải sử dụng cùng một hương vị. Hơn nữa, bạn không được sử dụng bất kỳ tính năng nào của ngôn ngữ máy chủ của flavour, như các cuộc gọi lại thay thế hoặc công cụ esửa đổi của Perl , để đánh giá mã Perl. Tất cả các thao tác phải xảy ra độc quyền thông qua thay thế regex.

Lưu ý rằng nó phụ thuộc vào hương vị và công cụ sửa đổi của bạn cho dù mỗi lần thay thế duy nhất thay thế tất cả các lần xuất hiện hay chỉ một lần duy nhất. Ví dụ: nếu bạn chọn hương vị ECMAScript, một bước duy nhất theo mặc định sẽ chỉ thay thế một lần xuất hiện, trừ khi bạn sử dụng công cụ gsửa đổi. Mặt khác, nếu bạn đang sử dụng hương vị .NET, mỗi bước sẽ luôn thay thế tất cả các lần xuất hiện.

Đối với các ngôn ngữ có các phương thức thay thế khác nhau để thay thế một lần và toàn cầu (ví dụ: Ruby subso với gsub), giả sử rằng thay thế duy nhất là mặc định và coi thay thế toàn cầu như một công cụ gsửa đổi.

Kiểm tra

Nếu hương vị bạn chọn là .NET hoặc ECMAScript, bạn có thể sử dụng Retina để kiểm tra trình của mình (Tôi được cho biết, nó cũng hoạt động trên Mono). Đối với các hương vị khác, có lẽ bạn sẽ phải viết một chương trình nhỏ bằng ngôn ngữ máy chủ áp dụng các thay thế theo thứ tự. Nếu bạn làm như vậy, xin vui lòng bao gồm chương trình kiểm tra này trong câu trả lời của bạn.


Nếu bất cứ ai có ý tưởng tốt để gọi loại thách thức này, hãy để lại nhận xét! :) (Chỉ trong trường hợp tôi sẽ làm nhiều hơn những thứ này trong tương lai.)
Martin Ender

Những người thích điều này cũng có thể thưởng thức Thêm mà không cần thêmNhân mà không có số
Toby Speight

Có phải regex "hương vị" của Retina là một đệ trình hợp lệ? : P (Tôi khá tự hào về bản thân vì đã quản lý để thêm hai số, chứ đừng nói đến việc đánh gôn.)
hoàn toàn là con người

@icrieverytim Hương vị của Retina chỉ là hương vị .NET.
Martin Ender

Nhưng Retina có các tính năng .NET không có, không?
hoàn toàn là

Câu trả lời:


32

Hương vị .NET, điểm: 2

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

Tôi không bận tâm đến việc chơi golf và xchỉ để bỏ qua các khoảng trắng.

Đầu tiên, nó chèn 9876543210vào từng vị trí, sau đó xóa các ký tự gốc và các ký tự không phải là chữ số hiện tại của tổng.

Regex lớn (1346 byte không có khoảng trắng và nhận xét):

# 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)(?!))
)

Điều này khiến tôi nghĩ đến cấp độ cuối cùng của Nhà sản xuất ... Nhưng tôi nghĩ .NET regex, rõ ràng không còn "thông thường", có thể giải quyết bất kỳ vấn đề nào trong PH. Và đây chỉ là một thuật toán trong L.


4
Tất cả các nhóm cân bằng mưa đá.
Sp3000

Đầu tiên tôi nghĩ rằng quá trình năm bước của tôi là khá tốt. Sau đó, tôi thấy ai đó yêu cầu một giải pháp với một nửa chiều dài. Thì đây. Điều này thậm chí được tính là một regex?
John Dvorak

1
@JanDvorak Đối với "biểu thức chính quy" lý thuyết, không. Đối với "regex", vâng, mọi người gọi đây là regex và hầu như mọi hương vị regex đều có thứ như thế này. Microsoft vẫn gọi chúng là " biểu thức chính quy ".
jimmy23013

Wow, đây là công việc tuyệt vời!
dùng230910

6

Điểm: 24

Tôi nghĩ rằng điều này hoạt động ...

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

Tôi chưa dành nhiều thời gian để chơi các biểu thức chính quy cá nhân. Tôi sẽ cố gắng đăng một lời giải thích sớm, nhưng bây giờ đã muộn. Trong khi đó, đây là kết quả giữa mỗi bước:

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

Chương trình perl đầy đủ:

$_ = <>;
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";

Điều này trông rất giống như bằng chứng của tôi về khái niệm. :) Tôi đã có 7 thay thế không vòng lặp, nhưng tôi đã không cố gắng hết sức để giữ chúng xuống.
Martin Ender

@ MartinBüttner haha ​​tốt đẹp! Tôi khá chắc chắn rằng hai tàu con cuối cùng của tôi cũng có thể được hợp nhất, nhưng tôi đã có đủ cho một ngày ...
grc

Tất cả các không gian hàng đầu có chủ ý?
Tối ưu hóa

@Optimizer có. Tôi nên chọn một nhân vật tốt hơn xin lỗi.
grc

5

Bất kỳ hương vị regex, 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

Hãy thử unary. dphục vụ cho một dấu tách thứ tự chữ số, xlưu trữ giá trị. Đầu tiên, chúng tôi unarise từng chữ số, sau đó chúng tôi ép các số nhân x10 sang trái, sau đó thả tất cả các dấu phân cách, sau đó chèn lại các số nhân, sau đó chuyển đổi từng thứ tự trở lại các chữ số.


5

.NET Regex, 14

Không tốt như giải pháp của user23013, nhưng nó rất vui. Không có sự thay thế nào có sửa đổi.

Lý do cho regex .NET không phải vì cân bằng các nhóm một lần - Tôi mới thử nghiệm với Retina , sử dụng .NET và tôi cũng thấy rằng các giao diện có chiều dài thay đổi đã giúp ích rất nhiều.

Thay thế 1 (lặp lại = không)

Chế độ:

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

Thay thế

0$1$2

Hoán đổi hai số, đệm để có cùng số lượng số 0 đứng đầu.

Thay thế 2 (lặp lại = không)

Chế độ:

(\d+)

Thay thế:

 $1

Thêm một khoảng trắng trước mỗi số

Thay thế 3 (lặp lại = không)

$

Thay thế:

&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

Thêm một bit carry (the &0) cũng như bảng tra cứu khổng lồ của <c> <a> <b> <carry of a+b+c> <last digit of a+b+c>.

Thay thế 4 (lặp lại = có)

Chế độ:

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

Thay thế:

$4$10

Tiếp tục lấy các chữ số cuối của mỗi số và tìm (tổng, mang) của chúng. Đặt tổng ở đầu chuỗi và thay thế carry.

Thay thế 5 (lặp lại = không)

Chế độ:

^0*| .*

Thay thế:

<empty>

Dọn dẹp.

Chạy ví dụ

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

(Bằng cách kết hợp một vài bước tôi có thể nhận được 12, nhưng vì nó khá lộn xộn và dù sao tôi cũng sẽ không thắng. Tôi nghĩ rằng tôi sẽ giữ phiên bản thanh lịch hơn này thay thế.)


4

Điểm: 50 40 31 21

Cảm ơn vì thử thách tuyệt vời này. Giải pháp này không phải là rất thanh lịch, nhưng, với những hạn chế, tôi không thể thấy bất kỳ cách nào để xử lý một chữ số chung trong đầu ra.

Giải pháp này có các nhóm chụp đôi khi không khớp và dựa vào chúng trống khi điều đó xảy ra. Điều này hoạt động trong Perl, mặc dù nó thường tạo ra một cảnh báo.

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)

Mẫu mã Perl đầy đủ, với giải thích và in kết quả trung gian:

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;

Cập nhật: Tôi đã có thể kết hợp hai trong số các biểu thức lặp lại với nhau, tiết kiệm 10.

Cập nhật 2: Tôi đã quản lý để bẻ khóa chuyển đổi chữ số đầu vào bằng một biểu thức chính.

Cập nhật 3: Tôi giảm xuống một regex lặp đơn.


Giải pháp thú vị. :) Niềng răng làm gì trong chuỗi thay thế? Có ${1}khác $1gì không? Ngoài ra, bạn có thể muốn bao gồm số byte trong trường hợp quan hệ.
Martin Ender

@ MartinBüttner, niềng răng chỉ cần tách tên biến khỏi các ký tự khác có thể có trong một biến.

Ah, điều đó có ý nghĩa. Cảm ơn.
Martin Ender

@ MartinBüttner, tôi đã thay đổi nó để sử dụng \1, v.v., thay vào đó, lưu một vài ký tự.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.