Retina , 108 102 94 87 82 64 63 byte
Terima kasih kepada Sp3000 untuk membuat saya mengejar pendekatan asli saya, yang membawa jumlah byte dari 108 ke 82.
Terima kasih banyak kepada Kobi yang menemukan solusi yang jauh lebih elegan, yang memungkinkan saya untuk menyimpan 19 byte lagi.
S_`(?<=^(?<-1>.)*(?:(?<=\G(.)*).)+)
.
$0
m+`^(?=( *)\S.*\n\1)
<space>
Dimana <space>
mewakili karakter spasi tunggal (yang kalau tidak akan dilucuti oleh SE). Untuk tujuan penghitungan, setiap baris masuk dalam file terpisah dan \n
harus diganti dengan karakter linefeed yang sebenarnya. Untuk kenyamanan, Anda dapat menjalankan kode seperti dari satu file dengan -s
bendera.
Cobalah online.
Penjelasan
Yah ... seperti biasa saya tidak bisa memberikan pengantar penuh untuk menyeimbangkan kelompok di sini. Untuk primer, lihat jawaban Stack Overflow saya .
S_`(?<=^(?<-1>.)*(?:(?<=\G(.)*).)+)
Tahap pertama adalah tahap S
plit, yang membagi input menjadi garis-garis yang semakin panjang. Yang _
menunjukkan bahwa potongan kosong harus dihilangkan dari pemisahan (yang hanya mempengaruhi akhir, karena akan ada kecocokan di posisi terakhir). Regex itu sendiri sepenuhnya terkandung dalam look-around sehingga tidak akan cocok dengan karakter apa pun, tetapi hanya posisi.
Bagian ini didasarkan pada solusi Kobi dengan golfitude tambahan yang saya temukan sendiri. Perhatikan bahwa lookbehinds dicocokkan dari kanan ke kiri di .NET, jadi penjelasan berikut sebaiknya dibaca dari bawah ke atas. Saya juga memasukkan yang lain \G
dalam penjelasan untuk kejelasan, meskipun itu tidak perlu untuk pola untuk bekerja.
(?<=
^ # 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.
Saya masih mengagumi karya Kobi di sini. Ini bahkan lebih elegan daripada regex pengujian utama. :)
Mari kita beralih ke tahap selanjutnya:
.
$0
Sederhana: masukkan spasi setelah setiap karakter non-linefeed.
m+`^(?=( *)\S.*\n\1)
<space>
Tahap terakhir ini membuat indentasi semua garis dengan benar untuk membentuk segitiga. Ini m
hanya mode multiline biasa untuk ^
mencocokkan awal baris. The +
memberitahu Retina mengulangi tahap ini sampai string berhenti berubah (yang, dalam hal ini berarti bahwa regex pertandingan tidak lagi).
^ # 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.
)
Jadi ini cocok dengan awal dari setiap baris yang tidak memiliki indentasi lebih besar dari yang berikutnya. Dalam posisi apa pun kita memasukkan spasi. Proses ini berakhir, setelah garis disusun dalam segitiga yang rapi, karena itulah tata letak minimal di mana setiap baris memiliki indentasi yang lebih besar daripada yang berikutnya.