Ecco alcuni codice che implementa il generale approccio, ricorsivo descritto altrove. La rappresentazione interna di un albero è una stringa (foglia) o una tupla (coppia) di sottonodi. La rappresentazione interna del "frammento" intermedio di un nodo è la tupla (above, below, lines)
, dove above
e below
sono il numero di linee sopra e sotto la radice e lines
è un iteratore su ciascuna linea parziale (senza spazi a sinistra).
#!/usr/local/bin/python3.3
from itertools import chain
from random import randint
def leaf(t):
return isinstance(t, str)
def random(n):
def extend(t):
if leaf(t):
return (t+'l', t+'r')
else:
l, r = t
if randint(0, 1): return (l, extend(r))
else: return (extend(l), r)
t = ''
for _ in range(n-1): t = extend(t)
return t
def format(t):
def pad(prefix, spaces, previous):
return prefix + (' ' * spaces) + previous
def merge(l, r):
l_above, l_below, l_lines = l
r_above, r_below, r_lines = r
gap = r_below + l_above
gap_above = l_above
gap_below = gap - gap_above
def lines():
for (i, line) in enumerate(chain(r_lines, l_lines)):
if i < r_above:
yield ' ' + line
elif i - r_above < gap_above:
dash = '_' if i - r_above == gap_above - 1 else ' '
if i < r_above + r_below:
yield pad(dash + '/', 2 * (i - r_above), line)
else:
yield pad(dash + '/', 2 * gap_below - 1, line)
elif i - r_above - gap_above < gap_below:
if i < r_above + r_below:
yield pad(' \\', 2 * gap_above - 1, line)
else:
spaces = 2 * (r_above + gap_above + gap_below - i - 1)
yield pad(' \\', spaces, line)
else:
yield ' ' + line
return (r_above + gap_above, gap_below + l_below, lines())
def descend(left, t):
if leaf(t):
if left:
return (1, 0, [t])
else:
return (0, 1, [t])
else:
l, r = t
return merge(descend(True, l), descend(False, r))
def flatten(t):
above, below, lines = t
for (i, line) in enumerate(lines):
if i < above: yield (' ' * (above - i - 1)) + line
else: yield (' ' * (i - above)) + line
return '\n'.join(flatten(descend(True, t)))
if __name__ == '__main__':
for n in range(1,20,3):
tree = random(n)
print(format(tree))
Ecco alcuni esempio di output:
_/rrrr
_/ \_/rrrlr
/\ \rrrll
_/ \_/rrlr
/\ \rrll
/ \ _/rlrr
/ \_/ \rlrl
_/ \_/rllr
\ \_/rlllr
\ \rllll
\ _/lrrr
\ _/ \lrrl
\ /\_/lrlr
\_/ \lrll
\ _/llrr
\_/ \llrl
\_/lllr
\_/llllr
\lllll
E un po 'un altro asimmetrica che mostra, forse, perché non riempie le linee con spazi a sinistra fino alla fine (via flatten
). Se la metà inferiore era stata imbottita a sinistra, parte del braccio avrebbe attraversato l'area imbottita.
_/rrrrr
_/ \rrrrl
_/ \rrrl
_/ \_/rrlr
/\ \rrll
/ \_/rlr
/ \rll
/ /lrrr
/ _/ _/lrrlrr
/ /\_/ \lrrlrl
/ / \lrrll
_/ _/ _/lrlrrr
\ /\ _/ \lrlrrl
\ / \_/ \lrlrl
\_/ \lrll
\ _/llrrr
\ _/ \llrrl
\_/ \llrl
\lll
È l'algoritmo ricorsivo "ovvio": il diavolo è nei dettagli. È stato più facile scrivere senza "_", il che rende la logica leggermente più complessa.
Forse l'unica "intuizione" è gap_above = l_above
- che sta dicendo che il "braccio" giusto ha la lunghezza del lato destro della sottostruttura di sinistra (sarà necessario leggerlo alcune volte). Rende le cose relativamente equilibrate. Vedi l'esempio asimmetrico sopra.
Un buon modo per comprendere le cose in modo più dettagliato è modificare la routine pad
per prendere un carattere anziché ' '
e assegnare un carattere diverso per ogni chiamata. Quindi puoi vedere esattamente quale logica ha generato quale spazio. Questo è quello che si ottiene utilizzando A. B, C e D per le chiamate al pad dall'alto verso il basso, al di sopra (ovviamente non c'è alcun carattere quando la quantità di spazio è zero):
_/rrrr
/\rrrl
_/B _/rrlrr
/\_/ \rrlrl
/AA \rrll
_/BBB _/rlrrr
/\DD _/ \rlrrl
/AA \_/ \_/rlrlr
/AAAA \C \rlrll
/AAAAAA \_/rllr
_/AAAAAAAA \rlll
\DDDDDDDD _/lrrrr
\DDDDDD _/ \lrrrl
\DDDD/\lrrl
\DD _/B _/lrlrr
\_/ \_/ \lrlrl
\C \lrll
\_/llr
\lll
C'è di più spiegazioni here (anche se l'albero è leggermente diverso).
Cosa hai provato? Posso immaginare che comporti il calcolo delle proprietà dell'albero (profondità, larghezza, eccetera), il calcolo del layout e la generazione dell'arte ASCII. –
@SimeonVisser ha aggiunto del codice non funzionante – Will
Guardando questo mi viene in mente che dovresti tenere traccia della profondità dell'albero. Ho un codice rudimentale basato sul codice spezzato, ma sembra terribile. Per ogni riga, cerco di capire quanto spazio aggiuntivo dovrebbe avere, ma la ricostruzione per quella riga attualmente conta solo per il ramo più basso – Michael