Retina , 108 102 94 87 82 64 63 Bytes
Vielen Dank an Sp3000, der mich dazu gebracht hat, meinen ursprünglichen Ansatz zu verfolgen, bei dem die Byteanzahl von 108 auf 82 gesenkt wurde.
Ein großes Dankeschön an Kobi, der eine viel elegantere Lösung gefunden hat, mit der ich zusätzlich noch 19 Bytes einsparen konnte.
S_`(?<=^(?<-1>.)*(?:(?<=\G(.)*).)+)
.
$0
m+`^(?=( *)\S.*\n\1)
<space>
Wobei <space>
ein einzelnes Leerzeichen darstellt (das sonst von SE entfernt würde). Zu Zählzwecken wird jede Zeile in eine separate Datei geschrieben und \n
sollte durch ein tatsächliches Zeilenvorschubzeichen ersetzt werden. Der Einfachheit halber können Sie den Code so ausführen, wie er aus einer einzelnen Datei mit dem -s
Flag stammt.
Probieren Sie es online aus.
Erläuterung
Naja ... wie immer kann ich hier keine vollständige Einführung in die Bilanzkreise geben. Für einen Primer siehe meine Stack Overflow Antwort .
S_`(?<=^(?<-1>.)*(?:(?<=\G(.)*).)+)
Die erste Stufe ist eine S
Plitstufe, die die Eingabe in Zeilen mit zunehmender Länge aufteilt. Das _
bedeutet, dass leere Chunks von der Aufteilung ausgeschlossen werden sollten (dies wirkt sich nur auf das Ende aus, da an der letzten Position eine Übereinstimmung vorliegt). Die Regex selbst ist vollständig in einem Look-Around enthalten, sodass keine Zeichen, sondern nur Positionen gefunden werden.
Dieser Teil basiert auf der Lösung von Kobi mit einer zusätzlichen Golfstärke, die ich selbst gefunden habe. Beachten Sie, dass Lookbehinds in .NET von rechts nach links übereinstimmen. Daher sollte die folgende Erklärung am besten von unten nach oben gelesen werden. Ich habe \G
der Klarheit halber auch eine andere in die Erklärung eingefügt , obwohl dies nicht erforderlich ist, damit das Muster funktioniert.
(?<=
^ # And we ensure that we can reach the beginning of the stack by doing so.
# The first time this is possible will be exactly when tri(m-1) == tri(n-1),
# i.e. when m == n. Exactly what we want!
(?<-1>.)* # Now we keep matching individual characters while popping from group <1>.
\G # We've now matched m characters, while pushing i-1 captures for each i
# between 1 and m, inclusive. That is, group <1> contains tri(m-1) captures.
(?:
(?<=
\G # The \G anchor matches at the position of the last match.
(.)* # ...push one capture onto group <1> for each character between here
# here and the last match.
) # Then we use a lookahead to...
. # In each iteration we match a single character.
)+ # This group matches all the characters up to the last match (or the beginning
# of the string). Call that number m.
) # If the previous match was at position tri(n-1) then we want this match
# to happen exactly n characters later.
Ich bewundere immer noch Kobis Arbeit hier. Dies ist noch eleganter als der Prime Testing Regex. :)
Fahren wir mit der nächsten Stufe fort:
.
$0
Ganz einfach: Fügen Sie nach jedem Zeichen ohne Zeilenvorschub ein Leerzeichen ein.
m+`^(?=( *)\S.*\n\1)
<space>
In dieser letzten Phase werden alle Linien korrekt eingerückt, um das Dreieck zu bilden. Dies m
ist nur der übliche mehrzeilige Modus, um eine ^
Übereinstimmung mit dem Zeilenanfang herzustellen . Das +
weist Retina an, diesen Schritt zu wiederholen, bis sich die Zeichenfolge nicht mehr ändert (was in diesem Fall bedeutet, dass die Regex nicht mehr übereinstimmt).
^ # Match the beginning of a line.
(?= # A lookahead which checks if the matched line needs another space.
( *) # Capture the indent on the current line.
\S # Match a non-space character to ensure we've got the entire indent.
.*\n # Match the remainder of the line, as well as the linefeed.
\1 # Check that the next line has at least the same indent as this one.
)
Dies entspricht also dem Anfang einer Zeile, die keinen größeren Einzug als die nächste hat. In einer solchen Position fügen wir ein Leerzeichen ein. Dieser Vorgang wird beendet, sobald die Zeilen in einem ordentlichen Dreieck angeordnet sind, da dies das minimale Layout ist, bei dem jede Zeile einen größeren Einzug als die nächste hat.