Regex ile Sayı Ekleme


39

Yeni bir regex golf mücadelesi denemek istiyorum. Bu, regex ikameinden başka hiçbir şey olmadan önemsiz hesaplama işlemlerini çözmenizi ister. Bunu daha mümkün ve daha az bir angarya yapmak için, birbiri ardına birkaç ikame başvurusu yapmanıza izin verilecek.

Meydan okuma

Basit başlayalım: iki pozitif tamsayı içeren bir dize verildiğinde, a ile ayrılan ondalık sayılar ,, toplamlarını içeren bir dize, aynı zamanda ondalık sayı olarak da oluşturur. Yani, çok basit

47,987

dönüşmeli

1034

Cevabınız keyfi pozitif tamsayılar için çalışmalıdır.

Biçim

Her cevap bir değiştirme dizisi olmalı , her adım bir regex ve değiştirme dizgisinden ibarettir. İsteğe bağlı olarak, dizideki bu adımların her biri için, dize değişimini durdurana kadar ikame işlemini tekrarlamayı seçebilirsiniz. İşte bir örnek gönderme (yok hangi değil yukarıdaki sorunu çözmek):

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

Girdi verildiğinde 123,456, bu gönderim girişi şu şekilde işler: ilk yer değiştirme bir kez uygulanır ve elde edilen verim:

|123,|456

Şimdi ikinci ikame, dize değişmeyi bırakana kadar bir döngüde uygulanır:

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

Ve son olarak, üçüncü oyuncu değişikliği bir kez uygulanır:

111111

Döngüler için sonlandırma kriterinin, dizgenin değişip değişmediğini, regex'in bir eşleşme bulup bulmadığını unutmayın. (Yani, bir eşleşme bulursanız da sona erebilir, ancak değiştirme eşleşmeyle aynıdır.)

puanlama

Birincil puanınız, gönderiminizdeki ikame adımlarının sayısı olacaktır. Her tekrarlanan değiştirme 10 adım için geçerli olacaktır . Yani yukarıdaki örnek puan verecekti 1 + 10 + 1 = 12.

(Çok muhtemel olmayan) bir beraberlik durumunda, ikincil puan, tüm adımların boyutlarının toplamıdır. Her adım için regex ( sınırlayıcılar olmadan ), değiştiriciler ve ikame dizgisini ekleyin . Yukarıdaki örnek için bu olacaktır (6 + 1 + 3) + (3 + 0 + 2) + (2 + 1 + 0) = 18.

Çeşitli Kurallar

Herhangi bir regex aroması kullanabilirsiniz (belirtmeniz gerekir), ancak tüm adımların aynı aromayı kullanması gerekir. Ayrıca, gereken değil yedek geri aramaları veya Perl gibi, aroma ana bilgisayar dilinin özelliklerini kullanmak ePerl kodu değerlendirir değiştirici. Tüm manipülasyon sadece regex ikamesi yoluyla gerçekleşmelidir.

Lezzetinize ve değiştiricilerinize, her bir değişimin tüm oluşumların yerine mi yoksa yalnızca bir tanesinin mi değiştirildiğine bağlı olduğunu unutmayın. Örneğin, ECMAScript lezzetini seçerseniz, tek bir adım, değiştiriciyi kullanmadığınız sürece, varsayılan olarak yalnızca bir oluşumu gdeğiştirir. Öte yandan, .NET lezzetini kullanıyorsanız, her adım her zaman tüm oluşumların yerini alacaktır.

Tek ve genel değişim için farklı ikame yöntemlerine sahip olan diller için (örneğin Ruby'nin subvs. gsub), tek değişimin varsayılan olduğunu varsayalım ve global değişime bir gdeğiştirici gibi davranın .

Test yapmak

Seçtiğiniz lezzet .NET veya ECMAScript ise, gönderinizi test etmek için Retina'yı kullanabilirsiniz (Bana söyleniyor, Mono'da da çalışıyor). Diğer tatlar için muhtemelen ev sahibi dilinde değiştirmeleri uygulayan küçük bir program yazmanız gerekecektir. Bunu yaparsanız, lütfen bu test programını cevabınıza ekleyin.


Herhangi birisinin bu tür bir mücadeleye ne diyeceği konusunda iyi bir fikri varsa, yorum yapın! :) (Gelecekte bunlardan daha fazlasını yapıyorum diye düşünüyorum.)
Martin Ender


Retina'nın regex "flavor" geçerli bir başvuru var mı? : P (iki sayı eklemeyi başarabildiğim için kendimle oldukça gurur duyuyorum, golf
oynamayı bırakalım

@icrieverytim Retina'nın tadı sadece .NET tadıdır.
Martin Ender

Fakat Retina'da .NET'te bulunmayan özellikler var değil mi?
tamamen insan

Yanıtlar:


32

.NET lezzet, puan: 2

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

Henüz golf oynamaktan rahatsız olmadım ve xsadece boşlukları görmezden gelmek için.

Önce 9876543210her bir pozisyona yerleştirir, sonra orijinal karakterleri ve toplamın o anki basamağı olmayan karakterleri siler.

Büyük regex (boşluksuz ve yorumsuz 1346 bayt):

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

Bu beni Manufactoria'nın son seviyesini düşündürdü ... Ama bence artık "normal" olan .NET regex'in PH'daki herhangi bir sorunu çözebileceğini düşünüyorum. Ve bu sadece L'de bir algoritma.


4
Tüm dolu .NET dengeleme grupları.
Sp3000

İlk önce beş aşamalı sürecimin oldukça iyi olduğunu düşündüm. Sonra birisinin yarısı uzunlukta bir çözüm talep ettiğini gördüm. Sonra bu. Bu bir regex olarak sayılıyor mu?
John Dvorak

1
@JanDvorak Teorik "normal ifade" için, hayır. "Regex" için, evet, herkes buna regex diyor ve hemen hemen her regex lezzetinin buna benzer bir şeyi var. Microsoft hala resmi olarak " normal ifadeler " diyor.
jimmy23013 10:15

Vay, bu harika bir iş!
user230910

6

Puan: 24

Bence bu işe yarıyor ...

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

Bireysel düzenli ifadeleri golf oynamak için fazla zaman harcamamıştım. Yakında bir açıklama göndermeye çalışacağım, ama şimdi geç oluyor. Bu arada, işte her adım arasındaki sonuç:

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

Tam perl programı:

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

Bu benim kendi konsept kanıtım gibi gözüküyor. :) Yine de 7 döngüsel olmayan değişiklik yaptım, ancak onları düşük tutmak için özellikle denemedim.
Martin Ender

@ MartinBüttner haha ​​güzel! Son iki sübvansiyonumun da birleştiğinden eminim, ama bir gün yetecek kadar oldum ...
grc

Tüm öncü mekanlar kasıtlı mı?
Doktor

@Optimizer evet. Üzgünüm daha iyi bir karakter seçmeliydim.
grc

5

Herhangi bir regex tadı, 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

Birliği deneyelim. dBir basamaklı sipariş ayırıcısına hizmet eder x, değeri depolar. Öncelikle her bir rakamın sesini kaldırırız, sonra x10 çarpanlarını sola doğru sıkar, sonra tüm ayırıcıları düşürür, sonra çarpanları geri ekler, sonra her sırayı tekrar sayılara dönüştürür.


5

.NET Regex, 14

User23013'ün çözümü kadar iyi değil, ama eğlenceliydi. Değişimlerin hiçbirinde değiştirici yok.

.NET regex'in nedeni, bir kereliğine grup dengeleme yüzünden değil. Sadece .NET'i kullanan Retina ile test ettim ve ayrıca değişken uzunluktaki gözlerin çok yardımcı olduğunu da gördüm.

Değiştirme 1 (tekrarla = hayır)

regex:

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

Değiştirme

0$1$2

İki sayıyı değiştirin, aynı sayıda öncü sıfırlara sahip olmak için dolgu yapın.

Değiştirme 2 (tekrar = hayır)

regex:

(\d+)

Değiştirme:

 $1

Her numaradan önce bir boşluk ekleyin

Değiştirme 3 (tekrar = hayır)

$

Değiştirme:

&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

Bir taşıma biti ( &0) ile birlikte dev arama tablosunu ekleyin <c> <a> <b> <carry of a+b+c> <last digit of a+b+c>.

4. Değiştirme (tekrar = evet)

regex:

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

Değiştirme:

$4$10

Her numaranın son hanelerini almaya devam edin ve (toplam, taşıma) sayılarını bulun. Toplamı dizenin başlangıcına koyun ve taşıma işlemini değiştirin.

5. değiştirme (tekrar = hayır)

regex:

^0*| .*

Değiştirme:

<empty>

Temizlemek.

Örnek çalışma

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

(Adımlardan birkaçını birleştirerek 12 adım alabilirim, ancak oldukça dağınık ve yine de kazanamayacağımı düşünüyorum, bunun yerine daha şık bir sürüm tutacağımı düşünüyorum.)


4

Puan: 50 40 31 21

Bu mükemmel meydan okuma için teşekkürler. Bu çözüm çok zarif değil, ancak kısıtlamalar göz önüne alındığında, çıktıda genel olarak bir basamağı kullanmanın bir yolunu göremedim.

Bu çözüm, bazen eşleşmeyen ve bu olduğunda boş olmalarına dayanan yakalama gruplarına sahiptir. Bu normalde bir uyarı üretmesine rağmen Perl'de çalışır.

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)

Ara sonuçların açıklanması ve yazdırılmasıyla birlikte tam Perl kod örneği:

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;

Güncelleme: Döngüsel regexlerin ikisini bir araya getirerek 10'u kurtardım.

Güncelleme 2: Giriş basamağı dönüşümünü tek bir regex ile kırmayı başardım.

Güncelleme 3: Tek bir döngü regex'e düştüm.


İlginç bir çözüm. :) Diş telleri değiştirme teli içinde ne yapar? Mı ${1}farklı $1? Ayrıca, bağ olması durumunda bayt sayısını eklemek isteyebilirsiniz.
Martin Ender

@ MartinBüttner, parantez değişken değişken adını değişkende olabilecek diğer karakterlerden ayırır.

Ah, bu mantıklı. Teşekkürler.
Martin Ender

@ MartinBüttner, onu kullanmak için değiştirdim \1, bunun yerine, birkaç karakter kaydederek.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.