Retina , 108 102 94 87 82 64 63 byte
Grazie a Sp3000 per avermi fatto perseguire il mio approccio originale, che ha portato il conteggio dei byte da 108 a 82.
Grazie di cuore a Kobi che ha trovato una soluzione molto più elegante, che mi ha permesso di salvare altri 19 byte.
S_`(?<=^(?<-1>.)*(?:(?<=\G(.)*).)+)
.
$0
m+`^(?=( *)\S.*\n\1)
<space>
Dove <space>
rappresenta un singolo personaggio spaziale (che altrimenti verrebbe rimosso da SE). Ai fini del conteggio, ogni riga va in un file separato e \n
deve essere sostituita con un carattere di avanzamento riga effettivo. Per comodità, è possibile eseguire il codice come da un singolo file con-s
flag.
Provalo online.
Spiegazione
Bene ... come al solito non posso dare una completa introduzione ai gruppi di bilanciamento qui. Per un primer vedi la mia risposta Stack Overflow .
S_`(?<=^(?<-1>.)*(?:(?<=\G(.)*).)+)
Il primo stadio è un S
plit stage, che divide l'input in linee di lunghezza crescente. Il_
indica che blocchi vuoti devono essere omessi dalla suddivisione (che interessa solo la fine, perché ci sarà una corrispondenza in ultima posizione). La regex stessa è interamente contenuta in uno sguardo in modo da non corrispondere a nessun personaggio, ma solo a posizioni.
Questa parte si basa sulla soluzione di Kobi con un po 'di golfitude aggiuntivo che mi sono trovato. Si noti che i lookbehind sono abbinati da destra a sinistra in .NET, quindi la seguente spiegazione dovrebbe essere letta da cima a fondo. Ne ho anche inserito un altro \G
nella spiegazione per chiarezza, sebbene ciò non sia necessario per il funzionamento del modello.
(?<=
^ # 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.
Sto ancora ammirando il lavoro di Kobi qui. Questo è ancora più elegante del regex di test prime. :)
Passiamo alla fase successiva:
.
$0
Semplice: inserire uno spazio dopo ogni carattere senza avanzamento riga.
m+`^(?=( *)\S.*\n\1)
<space>
Quest'ultima fase fa rientrare correttamente tutte le linee per formare il triangolo. Il m
è solo la modalità multi solito fare ^
l'inizio di una riga. Questo +
dice a Retina di ripetere questo passaggio fino a quando la stringa non smette di cambiare (che, in questo caso significa che la regex non corrisponde più).
^ # 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.
)
Quindi questo corrisponde all'inizio di qualsiasi riga che non ha un rientro più grande di quello successivo. In tale posizione inseriamo uno spazio. Questo processo termina, una volta che le linee sono disposte in un triangolo pulito, perché quello è il layout minimo in cui ogni linea ha un rientro più grande di quello successivo.