Wygeneruj kod programu Pyramid Scheme

32

Pyramid Scheme to język rozwijany przez @ ConorO'Brien . W Pyramid Scheme kod, który piszesz, wygląda następująco:

      ^         ^
     / \       /3\
    /   \      ---
   /  +  \
  ^-------^
 /9\     /3\
/123\    ---
-----

Ten kod ma dwie oczywiste cechy: Trudno go analizować i pisać. Conor rozwiązał pierwszy, jednak Twoim zadaniem będzie rozwiązanie tego drugiego problemu.


Powyższy kod jest przetwarzany przez interpreter PyramidScheme w zagnieżdżoną tablicę ciągów:

[["+", ["9123", "3"]], "3"]

Twoim zadaniem jest napisanie programu lub funkcji, która poda zagnieżdżoną tablicę ciągów, wyprowadza lub zwraca odtworzony kod PyramidScheme. Możesz założyć, że tablica wejściowa zawsze będzie poprawna.

Piramida jest trójkątem równoramiennym. Wierzchołek jest ^, boki nachylają się ukośnie z /i \, a dolny jest -. Dwa dolne rogi są puste lub zawierają początek innych piramid, które są argumentami. Środek jest wypełniony nazwą piramidy, ignorując podział linii.

Oto jak parser konwertuje kod na użyteczny format. Najpierw skanuje piramidę najwyższego poziomu. Jeśli nie przyjmuje żadnych argumentów, reprezentuje go jednym ciągiem znaków i przechodzi dalej. W przeciwnym razie reprezentuje jako tablica ["name",[arg1,arg2]]lub ["name",[arg1]]. Argumentami są piramidy w lewym dolnym rogu i prawym dolnym rogu piramidy, które mogą być łańcuchami lub więcej tablic opisanych powyżej. Możesz zauważyć, że to trochę przypomina Lisp, w którym to przypadku mogłeś zauważyć okropną grę słów, jaką jest nazwa języka. Po pełnym przedstawieniu piramidy parser przechodzi do następnego.

To jest , wygrywa najkrótszy kod!

Przypadki testowe: nie są to jedyne prawidłowe dane wyjściowe, to przykład prawidłowych danych wyjściowych.

[["+", ["9123", "3"]], "3"]

      ^         ^
     / \       /3\
    /   \      ---
   /  +  \
  ^-------^
 /9\     /3\
/123\    ---
-----

[["out", [["chr", ["72"]], ["chr", ["101"]]]], ["out", [["chr", ["108"]]]], ["out", [["chr", ["108"]]]], ["out", [["chr", ["111"]]]]]

        ^      ^     ^     ^
       / \    / \   / \   / \
      /out\  /out\ /out\ /out\
     ^-----^ -----^----- -----^
    / \   / \    / \         / \
   /chr\ /chr\  /chr\       /chr\
  ^----- -----^ -----^     ^-----
 / \         / \    / \   / \
/72 \       /101\  /108\ /111\
-----       -----  ----- -----

[ ["+", [ ["asdfghjkl"], ["do", [ "1" ]] ]] ]

       ^
      / \
     / + \
    /     \
   ^-------^
  /a\     /d\
 /sdf\   /o  \
/ghjkl\ ^-----
-------/1\
       ---

Zauważ, że w drugim przypadku testowym outobie piramidy druga i trzecia mają ["chr", ["108"]]jako parametr parametr, który jest zwinięty w jeden stos piramidy wspólny dla dwóch najwyższych poziomów. Jest to poprawna optymalizacja, którą Twój kod może obsługiwać, ale jest ona całkowicie opcjonalna; ocena nie zależy od długości twoich wyników.

Dla ciekawskich pierwszy przypadek wyświetla się z 9126 3powodu niejawnego drukowania piramid najwyższego poziomu, drugi drukuje Hello, a ostatni to błąd składniowy, zawarty tylko dlatego, że ma schludną strukturę.


Można zakładać, że wejście do druku zawiera tylko ASCII, z wyjątkiem miejsc, ^, /, \, i -. Dane wejściowe zawsze będą prawidłowe i będą zawierać co najmniej jedną piramidę. Nie ma ograniczeń co do wielkości tablicy ani ciągów wejściowych, jednak możesz napisać kod tak, jakby domyślną liczbą całkowitą w twoim języku była nieskończona precyzja i że twój komputer ma dowolną pamięć. Biorąc dane wejściowe jako pojedynczy ciąg znaków, możesz użyć dowolnej rozsądnej wartości (przecinek, spacja itp., Pod warunkiem, że jest w postaci drukowanej ascii, a nie "lub []) do rozgraniczenia tablic. Nie musisz dołączać nawiasów otaczających całą rzecz, zamiast tego weź wiele tablic oddzielonych separatorem.

Twój wynik nie musi być golfa, możesz wstawić dodatkowe miejsce lub zwiększyć swoje piramidy, niż to konieczne. Piramidy najwyższego poziomu powinny znajdować się w pierwszej linii. Dane wyjściowe powinny być ciągiem z nowymi wierszami lub listą ciągów.

Każdy, kto ma zawierać wersję swojego kodu, który optymalnie golfs piramidy mogą otrzymać jakąś rep w postaci upvotes / dobrodziejstw (ale prawdopodobnie tylko upvotes).

Pavel
źródło
8
Sierpiński pokochałby ten język.
mbomb007
4
Zupełnie nie opublikowałem tego wyzwania, ponieważ jestem zbyt leniwy, aby poprawnie sformatować trójkąty ...
Pavel
@KodosJohnson Input może być rodzimą tablicą.
Pavel
jak możesz mieć funkcję z więcej niż dwoma argumentami?
Zniszczalna cytryna
@DestructibleWatermelon Dane wejściowe nigdy nie będą zawierać takiej tablicy, że wymagałyby przekazania dwóch argumentów do piramidy, ponieważ jest to niemożliwe w schemacie piramidy.
Pavel

Odpowiedzi:

26

Common Lisp - 2524 1890 bajtów

(defun f(i)(let((s(loop as r in i collect(g r)))(n())(output""))(loop until n do(setf n T)(loop as r in s do(if(cdr r)(progn(setf output(c output(e r))(cdr r)(cdr(cdr r)))(setf n()))(setf output(c output(b(car r))))))(setf output(c output(format()"~%"))))output))(defun g(r)(if(stringp r)(d(m(length r))r)(if(<(length r)2)(d(m(length(car r)))(car r))(if(=(length(e r))1)(let((h(g(car(e r))))(p(d(m(length(car r)))(car r))))(let((o(+ 1(position #\^(e h))))(parent_length(car p)))(if(<(-(car h)o)parent_length)(l(cons(+ o parent_length)())(loop as n in(butlast(cdr p))collect(c(b o)n))(cons(c(subseq(e h)0 o)(car(last p)))())(loop as n in(cdr(cdr h))collect(c n(b (- parent_length(-(car h)o))))))(let((i(-(- o 1)parent_length)))(l(cons(car h)())(loop as n in(butlast(cdr p))collect(c(b o)n(b i)))(cons(c(subseq(nth 1 h)0 o)(car(last p))(b i))())(cddr h))))))(let((l-h(g(car(e r))))(r-h(g(e(e r)))))(let((ll(position #\^(e l-h)))(rl(position #\^(e r-h))))(let((lr(-(car l-h)ll 1))(rr(-(car r-h)rl 1)))(let((p(d(max(m(length(car r)))(ceiling(+ lr rl)2))(car r))))(let((m-pad(if(>(car p)(+ lr rl))(-(car p)lr rl)0)))(l(cons(+ ll 1(car p)1 rr)())(loop as n in(butlast(cdr p))collect(c(b(+ 1 ll))n(b(+ 1 rr))))(cons(c(subseq(e l-h)0(+ 1 ll))(car(last p))(subseq(e r-h)rl))())(loop as y in(append(cddr l-h)(make-list(length l-h):initial-element(b(car l-h))))as z in(append(cdr(cdr r-h))(make-list(length r-h):initial-element(b(car r-h))))collect(c y(b m-pad)z))))))))))))(defun d(r n)(cons(+(* 2 r)1)(l(cons(c(b r)"^"(b r))())(loop as i from 1 to r collect(c(b(- r i))"/"(subseq(c n(b(expt i 2)))(expt(- i 1)2)(expt i 2))"\\"(b(- r i))))(cons(make-string(+ 1(* 2 r)):initial-element #\-)()))))(defun m(l)(+ 1(floor(sqrt l))))(defun b(n)(make-string n :initial-element #\space))(defun c(&rest a)(apply 'concatenate 'string a))(defun l(&rest a)(apply 'concatenate 'list a))(defun e(tree)(nth 1 tree))

Dzięki @coredump za wiele sztuczek golfowych. Przykładowy wynik pytania:

> (f '(("out" (("chr" ("72")) ("chr" ("101")))) ("out" (("chr" ("108")))) ("out" (("chr" ("108")))) ("out" (("chr" ("111"))))))
          ^               ^          ^          ^  
         /o\             /o\        /o\        /o\ 
        /ut \           /ut \      /ut \      /ut \
       /     \         ^-----     ^-----     ^-----
      /       \       /c\        /c\        /c\    
     ^---------^     /hr \      /hr \      /hr \   
    /c\       /c\   ^-----     ^-----     ^-----   
   /hr \     /hr \ /1\        /1\        /1\       
  ^-----    ^-----/08 \      /08 \      /11 \      
 /7\       /1\    -----      -----      -----      
/2  \     /01 \                                    
-----     -----                                    










> (f '( ("+" ( ("asdfghjkl") ("do" ( "1" )) )) ))
          ^        
         /+\       
        /   \      
       /     \     
      /       \    
     /         \   
    ^-----------^  
   /a\         /d\ 
  /sdf\       /o  \
 /ghjkl\     ^-----
/       \   /1\    
---------  /   \   
           -----   








> (f '(("+" ("9123" "3")) "3"))
       ^        ^  
      /+\      /3\ 
     /   \    /   \
    /     \   -----
   ^-------^       
  /9\     /3\      
 /123\   /   \     
/     \  -----     
-------            

Oto oryginalna, (głównie) nie golfowa wersja:

(defun f (input)
    (let ((trees (loop for tree in input collect (g tree)))
          (done nil)
          (output ""))
        (loop while (not done)
            do  (setf done T) 
                (loop for tree in trees
                    do  (if (cdr tree)
                            (progn
                                (setf output (conStr output (car (cdr tree))))
                                (setf (cdr tree) (cdr (cdr tree)))
                                (setf done nil))
                            (setf output (conStr output (blank (car tree))))))
                (setf output (conStr output  (format nil "~%"))))
        output))

;creates a single tree
;output is a list, first element is the length of each line, the rest are the lines of text
(defun g (tree)
    (if (stringp tree)
        ;strings should be drawn as just the pyramid for the name
        (draw-body (min-rows (length tree)) tree)

        (if (< (length tree) 2)
            ;lists with no arguments should be drawn as just the pyramid for the name
            (draw-body (min-rows (length (car tree))) (car tree))
            (if (= (length (car (cdr tree))) 1)
                ;single child
                (let ((child (g (car (car (cdr tree))))) (parent (draw-body (min-rows (length (car tree))) (car tree))))
                    (let ((parent_offset (+ 1 (position #\^ (first-line child)))) (parent_length (car parent)))
                        (if (< (- (car child) parent_offset) parent_length)
                            (let ((child-fill (- parent_length (- (car child) parent_offset))))
                                (concatenate 'list 
                                    (cons (+ parent_offset parent_length) nil)
                                    (loop for line in (butlast (cdr parent))
                                        collect (conStr (blank parent_offset) line))
                                    (cons (conStr (subseq (nth 1 child) 0 parent_offset) (car (last parent))) nil)
                                    (loop for line in (cdr (cdr child))
                                        collect (conStr line (blank child-fill)))))
                            (let ((parent-fill (- (- parent_offset 1) parent_length)))
                                (concatenate 'list 
                                    (cons (car child) nil)
                                    (loop for line in (butlast (cdr parent))
                                        collect (conStr (blank parent_offset) line (blank parent-fill)))
                                    (cons (conStr (subseq (nth 1 child) 0 parent_offset) (car (last parent)) (blank parent-fill)) nil)
                                    (cdr (cdr child)))))))
                ;two children
                (let ((l-child (g (car (car (cdr tree))))) (r-child (g (car (cdr (car (cdr tree)))))))
                    (let ((lc-l-width (position #\^ (first-line l-child))) (rc-l-width (position #\^ (first-line r-child))))
                        (let ((lc-r-width (- (car l-child) lc-l-width 1)) (rc-r-width (- (car r-child) rc-l-width 1)))
                            (let ((parent (draw-body (max (min-rows (length (car tree))) (ceiling (+ lc-r-width rc-l-width) 2)) (car tree))))
                                (let ((m-pad (if (> (car parent) (+ lc-r-width rc-l-width))
                                            (- (car parent) lc-r-width rc-l-width)
                                            0)))
                                    (concatenate 'list
                                        (cons (+ lc-l-width 1 (car parent) 1 rc-r-width) nil)
                                        (loop for line in (butlast (cdr parent))
                                            collect (conStr (blank (+ 1 lc-l-width)) line (blank (+ 1 rc-r-width))))
                                        (cons (conStr (subseq (first-line l-child) 0 (+ 1 lc-l-width)) (car (last parent)) (subseq (first-line r-child) rc-l-width)) nil)
                                        (loop for left in (append (cdr (cdr l-child)) (make-list (length l-child) :initial-element (blank (car l-child))))
                                            for right in (append (cdr (cdr r-child)) (make-list (length r-child) :initial-element (blank (car r-child))))
                                            collect (conStr left (blank m-pad) right))))))))))))


;create a single pyramid
; output is a list, first element is the length of each line, the rest are the lines of text
(defun draw-body (rows name)
    (print rows)
    (print name)
    (cons (+ (* 2 rows) 1)
        (concatenate 'list (cons (conStr (blank rows) "^" (blank rows)) nil)
            (loop for i from 1 to rows
                collect (conStr (blank (- rows i)) "/" (subseq (conStr name (blank (expt i 2))) (expt (- i 1) 2) (expt i 2)) "\\" (blank (- rows i))))
            (cons (make-string (+ 1 (* 2 rows)) :initial-element #\-) nil))))

(defun min-rows (l)
    (+ 1 (floor (sqrt l))))

(defun blank (n)
    (make-string n :initial-element #\space))

(defun conStr (&rest args)
    (apply 'concatenate 'string args))

(defun first-line (tree)
    (car (cdr tree)))

Wypróbuj online!

Neil Lindquist
źródło
Powinieneś być w stanie grać w golfa na wiele bajtów, usuwając niepotrzebne miejsca.
clismique
2
Witamy w PPCG i fajna pierwsza odpowiedź!
Kritixi Lithos
Kilka wskazówek dotyczących golfa CL: w pętlach „for” można również zapisać jako „;”; możesz usuwać spacje przed i po nawiasach i podwójnych cudzysłowach; możesz zastąpić NIL przez (); czasem można także użyć zmiennych czytnika
rdzeń
... loop while (not x)jest loop until x, (cdr (cdr x))jest (cddr x), (setf a b c d)jest krótszy niż (setf a b)po nim (setf c d)itd. Ale to już dobra odpowiedź
coredump
2
Całkowita nagroda za 350 reputacji jest znacząca ... ale ta odpowiedź na to zasługuje. Wspólna odpowiedź Lisp na pytanie dotyczące konstruowania pytań dla dialektu Lisp ... Wow.
wizzwizz4