Создать код схемы пирамиды

32

Схема пирамиды - это язык, разрабатываемый @ ConorO'Brien . В Pyramid Scheme код, который вы пишете, выглядит следующим образом:

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

Теперь этот код обладает двумя очевидными качествами: его сложно анализировать и писать сложно. Конор решил первый, но ваша вторая задача - решить эту проблему.


Приведенный выше код обрабатывается интерпретатором PyramidScheme во вложенный массив строк, например:

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

Ваша задача - написать программу или функцию, которая, используя вложенный массив строк, выводит или возвращает воссозданный код PyramidScheme. Вы можете предположить, что входной массив всегда будет действительным.

Пирамида - это равнобедренный треугольник. Сверху ^стороны наклонены по диагонали от /и \, а снизу -. Два нижних угла либо пусты, либо содержат начало других пирамид, которые являются аргументами. Середина заполнена именем пирамиды, игнорируя разрывы строк.

Вот как парсер преобразует код в пригодный для использования формат. Во-первых, он сканирует пирамиду верхнего уровня. Если он не принимает аргументов, он представляет его одной строкой и движется дальше. В противном случае он представляет собой массив ["name",[arg1,arg2]]или ["name",[arg1]]. Аргументами являются пирамиды в левом нижнем и правом нижнем углу пирамиды, которые могут представлять собой либо строки, либо несколько массивов, описанных выше. Вы можете заметить, что это несколько напоминает Lisp, и в этом случае вы, возможно, также заметили ужасный каламбур, которым является название языка. После того, как пирамида полностью представлена, парсер переходит к следующей.

Это , самый короткий код выигрывает!

Тестовые случаи: это не единственные действительные результаты, это пример правильных результатов.

[["+", ["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\
       ---

Обратите внимание, что во втором тестовом случае вторая и третья outпирамиды имеют ["chr", ["108"]]параметр a, который свернут в один стек пирамид, совместно используемый двумя верхними уровнями. Это допустимая оптимизация, которую может поддерживать ваш код, но она совершенно необязательна; Оценка не зависит от длины вашего вывода.

Для любопытных первый случай отображается 9126 3из-за неявной печати пирамид верхнего уровня, второй печатает Hello, а последний - синтаксическая ошибка, включенная только потому, что имеет аккуратную структуру.


Можно предположить , что ввод содержит только печатаемые ASCII, за исключением пространства, ^, /, \, и -. Входные данные всегда будут действительными и содержат как минимум одну пирамиду. Нет ограничений на размер массива или входных строк, однако вы можете написать свой код, как если бы целочисленный тип вашего языка по умолчанию был бесконечной точности, и что ваш компьютер имел произвольную память. Если вы берете входные данные в виде одной строки, вы можете использовать что-либо разумное (запятую, пробел и т. Д., Если оно находится в формате ascii для печати, а не "или []) для разделения массивов. Вам не нужно включать скобки, окружающие всю вещь, и вместо этого взять несколько массивов, разделенных вашим разделителем.

Ваш вывод не должен быть в гольфе, вы можете вставить дополнительное пространство или сделать ваши пирамиды больше, чем необходимо. Трехуровневые пирамиды должны быть на первой линии. Вывод должен быть строкой с символами новой строки или списком строк.

Тот , кто делает включать в себя версию своего кода , который оптимально Гольфы пирамиды может получить некоторую репутацию в виде upvotes / щедрот (но , вероятно , просто upvotes).

Павел
источник
8
Серпински любил бы этот язык.
mbomb007
4
Абсолютно не опубликовал этот вызов, потому что мне лень правильно форматировать треугольники ...
Павел
@KodosJohnson Ввод может быть собственным массивом.
Павел
как вы можете иметь функцию с более чем двумя аргументами?
Разрушаемый лимон
@DestructibleWatermelon Входные данные никогда не будут содержать массив, так что для него потребуется передать два аргумента пирамиде, поскольку это невозможно в схеме пирамиды.
Павел

Ответы:

26

Common Lisp - 2524 1890 байт

(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))

Спасибо @coredump за ряд трюков в гольф. Пример вывода из вопроса:

> (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\   /   \     
/     \  -----     
-------            

Вот оригинальная (в основном) негольфированная версия:

(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)))

Попробуйте онлайн!

Нил Линдквист
источник
Вы должны быть в состоянии удалить много байтов, удалив ненужные пробелы.
clismique
2
Добро пожаловать в PPCG и хороший первый ответ!
Kritixi Lithos
Некоторые советы по игре в гольф CL: в циклах «для» можно также написать «как»; вы можете удалить пробелы до и после скобок и двойных кавычек; Вы можете заменить NIL на (); Вы также можете использовать переменные читателя, иногда
coredump
... loop while (not x)это loop until x, (cdr (cdr x))это (cddr x), (setf a b c d)короче , чем (setf a b)следует (setf c d), и т.д. Но это уже хороший ответ
CoreDump
2
Общая награда в 350 репутации является существенной ... но этот ответ заслуживает этого. Ответ Common Lisp на вопрос о построении вопросов для диалекта Lisp ... Вау.
wizzwizz4