正規表現で番号を追加する


39

新しいタイプの正規表現ゴルフチャレンジを試してみたいと思います。これは、正規表現の置換だけで、自明ではない計算タスクを解決するように求めます。これを可能にし、面倒な作業を少なくするために、次々にいくつかの置換を適用できます。

チャレンジ

簡単に始めましょう。2つの正の整数を含む文字列を、a ,で区切られた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、このサブミッションは入力を次のように処理します。最初の置換が1回適用され、次のようになります。

|123,|456

これで、ストリングの変更が停止するまで、2番目の置換がループで適用されます。

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

最後に、3番目の置換が1回適用されます。

111111

ループの終了基準は、正規表現が一致を検出したかどうかではなく、文字列が変更されたかどうかであることに注意してください。(つまり、一致するものが見つかっても置換が一致する場合にも終了する可能性があります。)

得点

プライマリスコアは、提出の代替ステップの数になります。置換が繰り返されるたびに、10ステップがカウントされます。したがって、上記の例ではスコアが付けられ1 + 10 + 1 = 12ます。

タイの(あまりありそうにない)ケースでは、2次スコアはすべてのステップのサイズの合計です。各ステップで、正規表現(区切り文字なし)、修飾子、置換文字列を追加します。上記の例では、これはになります(6 + 1 + 3) + (3 + 0 + 2) + (2 + 1 + 0) = 18

その他の規則

任意の正規表現フレーバー(指定する必要があります)を使用できますが、すべてのステップで同じフレーバーを使用する必要があります。さらに、置換コールバックやPerlコードを評価するPerl の修飾子など、フレーバーのホスト言語の機能を使用しないでくださいe。すべての操作は、正規表現の置換によってのみ行われる必要があります。

各置換がすべての出現を置き換えるか、単一の出現のみを置き換えるかは、フレーバーと修飾子に依存することに注意してください。たとえば、ECMAScriptフレーバーを選択した場合、g修飾子を使用しない限り、デフォルトでは1つのステップで1つのオカレンスのみが置き換えられます。一方、.NETフレーバーを使用している場合、各ステップは常にすべての出現を置き換えます。

シングルとグローバル交換(例えばRubyのためのさまざまな代替方法を持っている言語についてはsub対をgsub)、単一の置換は、デフォルトなど御馳走グローバルの交換であることを前提としていg修飾子。

テスト中

選択したフレーバーが.NETまたはECMAScriptの場合、Retinaを使用して送信をテストできます(私は言われていますが、Monoでも動作します)。他のフレーバーの場合、おそらく、置換を順番に適用するホスト言語で小さなプログラムを作成する必要があります。その場合は、このテストプログラムを回答に含めてください。


誰かがこの種のチャレンジを何と呼ぶべきかについて良い考えを持っているなら、コメントを残してください!:)(将来これらをさらに行う場合に備えて)
マーティンエンダー

誰がこのようなものはまた、楽しむかもしれない添加することなく、追加番号なし乗算
トビースパイツ

Retinaの正規表現「フレーバー」は有効な提出ですか?:P(ゴルフは
もちろんの

@icrieverytim Retinaのフレーバーは、.NETのフレーバーです。
マーティンエンダー

しかし、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
すべての.NETバランシンググループを称えます。
Sp3000

最初に、私は自分の5つのステップからなるプロセスはかなり良いと思いました。それから私は誰かが半分の長さで解決策を主張するのを見ました。その後...これ。これは正規表現としてもカウントされますか?
ジョン・ドヴォルザーク

1
@JanDvorak理論的な「正規表現」については、いいえ。「正規表現」については、はい、誰もがこれを正規表現と呼び、ほとんどすべての正規表現のフレーバーにはこのようなものがあります。マイクロソフトはまだ正式に「正規表現」と呼んでいます。
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'

完全なperlプログラム:

$_ = <>;
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つの非ループ置換がありましたが、それらを抑えるために特に一生懸命努力しませんでした。
マーティンエンダー

@yokohamaいいね!最後の2つの潜水艦もマージできると確信していますが、1日で十分でした
...-grc

すべての先行スペースは意図的ですか?
オプティマイザー

@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 Regex、14

user23013のソリューションほどではありませんが、楽しかったです。置換には修飾子はありません。

.NET正規表現の理由は、一度だけグループのバランスを取るためではありません。.NETを使用するRetinaでテストしたところ、可変長のルックビハインドが非常に役立つこともわかりました。

交換1(繰り返し=いいえ)

正規表現:

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

置換

0$1$2

2つの数値を交換し、先行ゼロの数が同じになるようにパディングします。

交換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

各番号の最後の桁を取り続け、それらの(合計、キャリー)を見つけます。文字列の先頭に合計を入れて、キャリーを置き換えます。

交換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;

更新:2つのループ正規表現を組み合わせて、10を節約できました。

更新2:単一の正規表現で入力数字の変換をクラックすることができました。

更新3:単一のループ正規表現に減らしました。


興味深いソリューション。:)置換文字列で中括弧は何をしますか?とは${1}違う$1?また、同点の場合にバイトカウントを含めることもできます。
マーティンエンダー

@MartinBüttner、中括弧は、変数名を変数に含まれる可能性のある他の文字から単に分離します。

ああ、それは理にかなっています。ありがとう。
マーティンエンダー

@MartinBüttner、\1代わりにを使用するように変更し、代わりに、いくつかの文字を保存しました。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.