Створіть прямокутник із специфікації


14

Вступ

Цей виклик натхненний Grime , моєю 2D мовою відповідності шаблонів. В основному, вам дається "граматика", яка описує двовимірні сітки символів, і ваше завдання полягає в створенні сітки відповідно до граматики. Крім того, сітка повинна бути якомога меншою у певному слабкому сенсі.

Вхідні дані

Ваш вхід - це рядок, що містить малі символи ASCII та символи |та -. Для простоти введення не містить повторених малих символів. Рядок - це специфікація для класу прямокутних сіток символів, і вона розбирається зліва направо, використовуючи стек наступним чином.

  • Надавши рядкові символи c, натисніть на стек m×nсітку символу cдля будь-якогоm, n ≥ 1 .
  • Давши трубу |, висуньте дві сітки Aі Bзі стека ( Bбув зверху), і висуньте сітку, ABотриману шляхом об'єднання Bправоруч від A. Це вимагає , щоб Aі Bмають однакову висоту.
  • Давши дефіс -, висуньте дві сітки Aта Bзі стека ( Bбув зверху) та натисніть на сітку, A/Bотриману шляхом конкатенації, Bна дно A. Це вимагає , щоб Aі Bмати однакову ширину.

Гарантується, що для деяких варіантів mі nзроблених під час процесу розбору (які можуть бути різними для кожної літери), специфікація вводу правильно описує якийсь прямокутник, який залишається в стеці в кінці.

Вихідні дані

Ваш вихід - це прямокутна сітка символів, вказана вводом. Сітка повинна бути мінімальною, оскільки вилучення будь-якого рядка чи стовпця зробить її недійсною. Ви можете повернути розділений рядок з новим рядком (з чи без нового рядка), двовимірний масив символів або масив рядків, залежно від того, який формат є найбільш зручним.

Зауважте, що вам не потрібно обробляти вхід точно так, як описано вище; єдине важливе - це ваш результат правильний.

Приклад

Розглянемо специфікацію

par-s||e-

По-перше, ми вирішимо висунути 1×2прямокутник pі, 1×1прямокутники aі r(причина цього буде зрозуміла пізніше). Потім ми вставляємо aі rпрямокутники, і висуваємо їх вертикальне з'єднання

a
r

Далі ми проштовхуємо 1×2прямокутник s, вставляємо його та вищевказаний прямокутник і просуваємо їх горизонтальне з'єднання

as
rs

Потім ми вставляємо цей прямокутник і pпрямокутник і підштовхуємо їх конкатенацію

pas
prs

Нарешті, ми висуваємо 3×1прямокутник e, вставляємо його та вищевказаний прямокутник і натискаємо на вертикальне з'єднання

pas
prs
eee

Це вихід програми або хоча б одна з можливостей. Зауважте це, хоча

ppas
ppas
pprs
eeee

також генерується специфікацією, це не є дійсним висновком, оскільки багато рядків і стовпців можна видалити.

В якості більш тонкого прикладу розглянемо

co|m|p|il|e|r|-

Ця специфікація генерує прямокутник

comp
iler

що є дійсним результатом. Однак це також породжує

commp
iiler

що також дійсно, оскільки жоден рядок чи стовпець не можна видалити, не скасовуючи його.

Правила

Ви можете надати повну програму або функцію. Виграє найменший байт, а стандартні лазівки заборонені.

Додаткові випробувальні випадки

Ви можете використовувати їх для тестування програми.

Input:
a
Output:
a

Input:
co|mp|l|-ex|i|f|-y|
Example output:
cccoy
mplly
exify

Input:
ja-r|g-o|ni-|ze|d-|
Example output:
jronze
arondd
ggoidd

Input:
un|co|p-yr|i|gh-t-ab|-|le-||-
Example output:
unnnnnnn
coyriggl
ppyrihhe
ppyritte
ppyriabe

Звідки родом н і м?
seequ

Може бути статичним або має бути якась форма введення?
seequ

@Sieg nі mобираються недетерміновано. Гарантується, що відповідні значення для них існують, але завдання їх програми - знайти їх.
Згарб

Ви насправді не визначаєте, що вони є.
seequ

un|co|p-|yr|i|gh--t-ab|-|le-||-неможливо бути дійсним. Останній -має атрибут 2, тоді як на стеку є лише 1 елемент.
orlp

Відповіді:


6

К, 123 110 байт

Я використовував подібний підхід до рішення cardboard_box.

r:{y,x#,*|y};h:{t:(#x)|#y;r[t-#x;x],'r[t-#y]y};a:{(,x .|2#y),2_ y};*(){(a[{+h[+x;+y]}]x;a[h]x;(,,y),x)"-|"?y}/

Ця програма являє собою низку допоміжних визначень з наступною негласною функцією, яка приймає рядок як правильний аргумент. Переформатування для читабельності та призначення кінцевої функції як f:

r: {y,x#,*|y};                           / repeat last row x times
h: {t:(#x)|#y;r[t-#x;x],'r[t-#y;y]};     / append horizontal
v: {+h[+x;+y]};                          / append vertical
a: {(,x[y@1;y@0]),2_ y};                 / pop two values, concat

f: *(){:[y="-";a[v;x]; y="|";a[h;x]; (,,y),x]}/;

Приклад використання:

  f "ja-r|g-o|ni-|ze|d-|"
("jronze"
 "aroidd"
 "ggoidd")

Тестовано за допомогою Kona, але воно також буде працювати в oK, якщо ви заміните :у визначенні на fa$ - k5 змінили синтаксис "cond".

Ключовим моментом є визнання того, що виконання вертикального додавання - це перенесення горизонтального додавання транспозиції обох матриць. (Див. Визначення v.) Я думаю, що є ще місце, щоб вичавити кілька символів тут і там. Якщо когось цікавить більш детальне пояснення, я можу надати його.

редагувати:

Оновлено програму вгорі цього запису. Негольована версія:

r: {y,x#,*|y};                           / repeat row x times
h: {t:(#x)|#y;r[t-#x;x],'r[t-#y;y]};     / append horizontal
v: {+h[+x;+y]};                          / append vertical
a: {(,x .|2#y),2_ y};                    / apply a concat
f: *(){(a[v]x;a[h]x;(,,y),x)"-|"?y}/;

Помітні оптимізації довжини включають в себе використання «точка застосувати» в a, замінивши «Cond» зі списком індексації f(менш ефективним, але коротше) і заміщають члени виду a[b;c]в a[b]cяких допускається угруповання. Оскільки я більше не використовую "cond" або будь-які примітиви, які відрізняються між k3 та k5, ця версія тепер працює в oK без змін.


Вітаємо з виграшем баунті!
Zgarb

Спасибі! Це була цікава проблема, яка досить добре поступилася К. Було б цікаво побачити спроби в J або APL для порівняння.
JohnE

4

Пролог, 539 байт

:-lib(ic).
:-lib(util).
b(A,B,C):-between(A,B,C).
g(S):-string_list(S,L),p(L,[]).
w(h(L,R):_:_,I,J):-w(L,I,J);L=_:W:_,X is I-W,w(R,X,J).
w(v(U,D):_:_,I,J):-w(U,I,J);U=_:_:H,Y is J-H,w(D,I,Y).
w(f(C):W:H,I,J):-b(1,W,I),b(1,H,J),char_code(S,C),put_char(S).
p([],[S]):-term_variables(S,V),S=_:X:Y,labeling(V),!,b(1,Y,J),(b(1,X,I),w(S,I,J);nl),fail.
p([124|T],[Q,Z|R]):-!,Q=_:WA:H,Z=_:WB:H,W #= WA+WB,p(T,[h(Z,Q):W:H|R]).
p([45|T],[Q,Z|R]):-!,Q=_:W:HA,Z=_:W:HB,H #= HA+HB,p(T,[v(Z,Q):W:H|R]).
p([C|T],R):-!,[H,W] #:: 1..100,p(T,[f(C):W:H|R]).

Пояснення

Ми починаємо з предиката g, який бере рядок, перетворюємо його у список символів і називаємоp предикат (розбір) з порожнім стеком як другий аргумент.

Предикат викликує pсебе рекурсивно з відповідно модифікованим стеком (шукайте [H|T]схеми деструктуризації та конструктора). При виклику в базовому випадку, де список вводу порожній, pдрукується унікальний елемент стека. Якщо в стеці є менше або більше одного елемента в цей момент, це означає, що у нас є порожній рядок введення, неправильна вхідна рядок або помилка (при рядку emtpy предикат виходить з ладу (Prolog кажеNo ), але нічого не друкується, що нормально, оскільки ми не повинні нічого друкувати для порожніх рядків).

Розв’язування

Стек містить опис побудованих прямокутників, позначених S:W:H, де Sсимволічне зображення прямокутника, Wйого ширини та Hвисоти (примітка, A:Bсинтаксичний цукор для :(A,B)кортежу з функтором, названим: ; писати його просто коротше, ніж мати кортеж з позначенням префікса).

З специфікаціями Aта Bпідпрямокутниками Sможуть бути:

  • h(A,B) : горизонтальний конмат А і В
  • v(A,B) : вертикальний конмат A і B
  • f(C) : заповнити C, де C - код символу

Ширина та висота сіток є змінними програмування обмежень: під час вертикального (відповідно горизонтального) конкатенації ширина (відповідно висота) маніпульованих прямокутників уніфікована, тоді як отримана висота (відповідно ширина) обмежується сумою висота кожної підрешітки (відповідно ширина).

Крок маркування в кінці процесу створює змінні, дотримуючись обмежень, використовуючи мінімально можливі значення (це властивість порядку, за допомогою якого випробовуються рішення).

Я міг би отримати більш коротку відповідь, використовуючи ті ж міркування, що і в інших відповідях, без обмежень, але це вже пізно.

Зауважте також, що оскільки для домену за замовчуванням для змінних встановлено значення 1..100, існує обмеження щодо можливих розмірів сіток. Верхню межу можна змінити за потреби. Наслідки ефективності цього полягають у тому, що це може зайняти багато часу, щоб визначити, що певне рішення не приймає рішення. Я сказав " міг ", оскільки обмеження, ймовірно, кардинально обрізають експоненціальний пошук. Якщо ви знайдете рядок введення, який важко / дорого відхилити, будь ласка, поділіться ними.

Друк

Друкована частина цікава тим, що існує якийсь алгоритм випромінювання променів над структурою: я повторюю кожну комірку отриманої сітки, від точки (1,1)до точки (W,H)та закликаю wпредикат для друку вмісту сітки в головному дереві, на це місце (звичайно, новий рядок друкується після обробки кожного рядка).

В w, позиції відносно поточної сітки (коренева сітка визначає абсолютні координати).

Друкуючи h(A,B)структуру в точці (X,Y), я беззастережно друкую в обох галузях:

  • в сітці Aв точці (X,Y), і
  • в сітці Bв точці (H,Y), де Hзнаходиться Xмінус ширина A.

Листові гілки дерева сітки f(C), нарешті, або друкують символ C, якщо відносне розташування знаходиться всередині сітки, або нічого не роблять. Ось так я можу надрукувати вміст сітки у вихідний потік, зверху вниз, зліва направо. Фактичні масиви не створюються.

Тести

t("ja-r|g-o|ni-|ze|d-|").
t("un|co|p-yr|i|gh-t-ab|-|le-||-").
t("co|mp|l|-ex|i|f|-y|").
t("a").

tt :- t(X),nl,g(X).
tt.

Запуск тесту:

[eclipse] tt.

jronze
aronze
ggoidd

uuuuuuun
coyriggl
coyrihhl
coyrittl
ppyriabe

cccoy
mmply
exify

a

Yes (0.00s cpu)

+1 No actual arrays are produced.так треба робити. У цьому випадку надмірність, оскільки граматика така проста і є ярлики.
edc65

@ edc65 Так, це надмірно. Але оскільки це кодогольф, я намагався мінімізувати розмір, і маніпулювання масивами було б занадто багатослівним.
coredump

3

Пітон 2.7, 259

z=zip
def p(a,b):
 s,l=sorted([a,b],key=len)
 s+=([s[-1]]*(len(l)-len(s)))
 return z(*(z(*a)+z(*b)))
g=lambda s,t=[]:s and(s[0]=='-'and g(s[1:],t[:-2]+[z(*p(z(*t[-2]),z(*t[-1])))])or(s[0]=='|'and g(s[1:],t[:-2]+[p(t[-2],t[-1])])or g(s[1:],t+[[s[0]]])))or t[0]

gце функція, яка приймає специфікацію та надає 2D масив символів. Якщо ви хочете більш зручну для користувача версію, додайте цей рядок, щоб змусити його взяти специфікацію зі stdin та роздрукувати сітку:

print'\n'.join(''.join(s)for s in g(raw_input()))

Випробування

Input:
a
Output:
a
==========
Input:
co|mp|l|-ex|i|f|-y|
Output:
coooy
mplly
exify
==========
Input:
ja-r|g-o|ni-|ze|d-|
Output:
jronze
aroidd
ggoidd
==========
Input:
un|co|p-yr|i|gh-t-ab|-|le-||-
Output:
unnnnnnn
coyriggl
ppyrihhe
ppyritte
ppyriabe

Пояснення

Стратегія проста: якщо сітка Gсправедлива для специфікації S, то повторення крайнього правого стовпця Gтакож дає дійсну специфікацію для S, і те саме стосується повторення нижнього рядка (доказ цього - структурною індукцією S). Тому, коли ми хочемо об'єднати два прямокутники, ми можемо просто додати останній стовпчик / рядок меншого, поки вони не збігаються за розміром (це те, що робить функція p).


3

Haskell, 388 367 352 байт

data C=L{c::Char}|N{t::Int,w::Int,h::Int,l::C,r::C}
q=replicate
[]#[x]=x
(c:i)#(b:a:s)|c=='-'=i#(N 1(max(w a)$w b)(h a+h b)a b:s)|c=='|'=i#(N 2(w a+w b)(max(h a)$h b)a b:s)
(c:i)#s=i#(N 0 1 1(L c)(L c):s)
p i|t i==0=q(h i)$q(w i)$c$l i|t i==2=zipWith(++)(p(l i){h=h i})$p(r i){h=h i,w=w i-w(l i)}|1<2=p(l i){w=w i}++p(r i){w=w i,h=h i-h(l i)}
f=p.(#[])

Використання: f "par-s||e-"->["pas","prs","eee"]

Тестовий пробіг із симпатичним друком:

> putStr $ unlines $ f "par-s||e-"
pas
prs
eee

> putStr $ unlines $ f "co|m|p|il|e|r|-"
comp
iler

> putStr $ unlines $ f "a"
a

> putStr $ unlines $ f "co|mp|l|-ex|i|f|-y|"
coooy
mplly
exify

> putStr $ unlines $ f "ja-r|g-o|ni-|ze|d-|"
jronze
aroidd
ggoidd

> putStr $ unlines $ f "un|co|p-yr|i|gh-t-ab|-|le-||-"
unnnnnnn
coyriggl
ppyrihhe
ppyritte
ppyriabe

Як це працює: функція #аналізує вхідний рядок у структурі дерева, Cякий є або листом, що Lмістить символ для друку, або вузлом N. Nможе бути а) з'єднання бічного боку ( t==2), б) з'єднання вгорі-внизу ( t==1) або в) квадрат одного квадрата ( t==0). Усі вузли мають поле ширини та висоти та ліворуч та праворуч. Після розбору pдрукується решта кореневого вузла шляхом рекурсивного регулювання розміру (ширини х висоти) його дочірніх вузлів та приєднання до них.

Редагувати: виводити як список рядків замість симпатичного друку


1

JavaScript (ES6), 283 295

Редагувати зараз це (сильно гольф) рішення JS принаймні коротше, ніж референтне (цілком гольф) рішення Python.

Подібно до cardboard_box, просто повторюючи крайній лівий стовпець або верхній рядок.

F=w=>(
s=[t=1,l='length'],
[for(c of w)(
  b=s[t],a=s[--t],
  c>'z'?
    s[t]=(G=(a,b,m=b[l]-a[l])=>m?m>0?G([a[0],...a],b):G(a,[b[0],...b]):a.map((r,i)=>r+b[i]))(a,b)
  :c<'a'?
    s[t]=a.map(r=>r[m=b[0][l]-r[l],0].repeat(m>0&&m)+r).concat(b.map(r=>r[0].repeat(m<0&&-m)+r))
  :s[t+=2]=[c]
)],
s[t].join('\n'))

Ungolfed Це моє перше рішення, що не має волі.

F=sp=>{
  s=[]
  for(c of sp){
    a=s.pop(b=s.pop())
    if (c > 'z')
    {
      l = a.length
      m = b.length
      for(; l-m ;)
        l < m ? l = a.unshift(a[0]) : m = b.unshift(b[0])
      s.push(a.map((r,i) => r + b[i]))
    }
    else if (c < 'a')
    {
      l = a[0].length
      m = b[0].length
      s.push(
        a.map(r => r[0].repeat(l < m ? m-l : 0) + r)
        .concat(b.map( r => r[0].repeat( l > m ? l-m : 0) + r))
      )
    }
    else 
    {
      s.push(a,b,[c])
    }
  }
  return s.pop().join('\n')
}

Тест у консолі Firefox / FireBug

;['par-s||e-','co|m|p|il|e|r|-','co|mp|l|-ex|i|f|-y|',
 'ja-r|g-o|ni-|ze|d-|','un|co|p-yr|i|gh-t-ab|-|le-||-']
.forEach(w=>console.log(F(w)))

Вихідні дані

pas
prs
eee

comp
iler

cccoy
mmply
exify

jronze
aronze
ggoidd

uuuuuuun
coyriggl
coyrihhl
coyrittl
ppyriabe

0

Python 3, 251 байт

Ось опорна відповідь, яку я пообіцяв, гольф трохи далі.

T=lambda m:list(map(list,zip(*m)))
E=lambda a,b:a[:1]*(len(b)-len(a))+a
H=lambda a,b:[d+c for c,d in zip(E(a,b),E(b,a))]
s=[]
for k in input():s=k>"z"and[H(*s[:2])]+s[2:]or k<"a"and[T(H(*map(T,s[:2])))]+s[2:]or[[[k]]]+s
for r in s[0]:print("".join(r))

Це повна програма, яка бере рядок зі STDIN і друкує до STDOUT. Підхід такий самий, як у cardboard_box: натисніть на масив 1x1 для символу та скопіюйте рядки, якщо це потрібно для конкатенації.

$ echo "par-s||e-" | python3 gr.py
pas
prs
eee

Детальне пояснення

  • Tпереносить заданий список списків. Більшість робіт виконується за допомогою zip(*m)підкачки рядків на стовпці, решта - це просто перетворення результату в список списків, оскільки zipповертає генератор кортежів.
  • E(a,b) повертає a з його першим елементом, повтореним достатньо разів, щоб відповідати довжині b. Зауважте, що множення списку на від'ємне число дає порожній список, тож якщо bвоно коротше a, воно повертається a.
  • H(a,b)повертає горизонтальне конкатенацію aі b, якщо коротше, подовжується, Eякщо це необхідно.
  • s є стек.
  • У forциклі ми повторюємо вхідний рядок і замінюємо sновим значенням: якщо воно |(більше, ніж z), ми виводимо два значення і натискаємо їх H, якщо воно -(нижче ніжa ), ми додаємо два значення, переносимо, подаємо на H, перенесіть ще раз і натисніть результат, інакше натисніть на масив 1x1 з буквою.
  • Нарешті, ми друкуємо перший елемент s.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.