根据规范生成矩形


14

介绍

这项挑战的灵感来自我的2D模式匹配语言Grime。基本上,您将获得一个描述二维字符网格的“语法”,而您的工作是根据该语法生成​​一个网格。另外,从某种意义上说,网格应尽可能小。

输入值

您的输入是一个包含小写ASCII字符以及符号|和的字符串-。为简单起见,输入不包含重复的小写字符。字符串是一类矩形字符网格的规范,它使用堆栈从左到右进行解析,如下所示。

  • 给定一个小写字符c,将任何一个m×n字符的网格压入堆栈。cm, n ≥ 1
  • 给定一个管道|,弹出两个网格AB从堆栈中弹出(B位于顶部),然后将AB通过串联B而获得的网格推到的右侧A。这要求AB具有相等的高度。
  • 给定一个连字符-,弹出两个网格AB从堆栈中弹出(B位于顶部),然后将A/B通过串联B而获得的网格推到的底部A。这需要AB具有相等的宽度。

确保在解析过程中对进行某些选择m和选择n(每个字母可能有所不同),输入规范正确地描述了一些矩形,该矩形最后留在堆栈上。

输出量

您的输出是由输入指定的字符的矩形网格。在删除任何行或列将使其无效的意义上,网格必须最小。您可以返回以换行符分隔的字符串(带或不带尾随换行符),2D字符数组或字符串数​​组,以最方便的格式为准。

请注意,您不需要完全如上所述地处理输入;唯一重要的是您的输出正确。

考虑规格

par-s||e-

首先,我们选择推1×2的矩形p,和1×1的矩形ar(这样做的原因很清楚更高版本)。然后,我们弹出ar矩形,并推动它们的垂直串联

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

n和m来自哪里?
seequ

可以是静态的还是必须是某种形式的输入?
seequ

@Sieg n和不确定m地选择。可以保证存在适合它们的值,但是程序的工作是找到它们。
Zgarb 2015年

您实际上并没有定义它们是什么。
seequ

un|co|p-|yr|i|gh--t-ab|-|le-||-是不可能的。最后-一个的Arity为2,而堆栈上只有1个元素。
orlp

Answers:


6

K,123110字节

我对硬纸板解决方案使用了类似的方法。

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

使用科纳测试,但它也将在工作正常,如果你更换:的定义中,f$- K5改变“电导率”的语法。

关键是要认识到,进行垂直附加是两个矩阵的转置的水平附加的转置。(请参阅定义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之间不同的任何原语,因此该版本现在可以在无需修改的情况下正常运行。


恭喜您获得了赏金!
Zgarb

谢谢!这是一个有趣的问题,对K来说效果很好。看到J或APL中的尝试进行比较会很有趣。
副结核病

4

Prolog,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(parse)谓词,并将空堆栈作为第二个参数。

谓词p使用经过适当修改的堆栈递归调用自身(查找[H|T]解构和构造函数模式)。在输入列表为空的基本情况下调用时,将p打印堆栈的唯一元素。如果此时堆栈中的元素少于或多于一个,则意味着我们有一个空的输入字符串,一个错误的输入字符串或一个错误(带有空字符串,谓词失败(Prolog说No),但没有输出任何内容,可以,因为我们不应该为空字符串打印任何内容。

解决

堆栈包含对构造的矩形的描述,用表示S:W:H,其中S是矩形,矩形W的宽度和H高度的符号表示(请注意,它是一个名为functor A:B:(A,B)元组的语法糖:;它的编写比具有元组的编写要短(带有前缀表示法)。

具有AB子矩形规格,S可以是:

  • h(A,B) :A和B的水平组合
  • v(A,B) :A和B的垂直组合
  • f(C) :用C填充,其中C是字符代码

网格的宽度和高度是约束编程变量:在垂直(水平)连接过程中,被操纵矩形的宽度(高度)是统一的,而得到的高度(水平宽度)被约束为每个子网格的高度(分别为宽度)。

在过程结束时,标记步骤使用最小的可能值在遵守约束的同时实例化变量(这是尝试解决方案的顺序的属性)。

我可能会使用与其他答案相同的推理来获得较短的答案,没有限制,但是现在为时已晚。

还要注意,因为变量的默认域设置为1..100,所以网格的可能大小存在限制。如果需要,可以更改上限。这对性能的影响在于,确定某个特定解决方案不接受任何解决方案可能会花费大量时间。我说“ 可以 ”,是因为约束条件可能会极大地修剪指数搜索。如果您发现难以拒绝或难以接受的输入字符串,请分享。

列印

因为有一种打印部件是有趣的光线投射在结构算法:我遍历导致网格的每个细胞,从点(1,1)到一点(W,H),并调用w谓词打印格的内容在主树,在此位置(当然,在处理每行之后会打印换行符)。

在中w,位置是相对于当前网格的(根网格定义了绝对坐标)。

h(A,B)在point处打印结构时(X,Y),我无条件地在两个分支中打印:

  • 在网格A(X,Y)
  • B点的网格中(H,Y),其中HX减去宽度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.就是这样。在这种情况下,矫kill过正,因为语法是如此简单,并且存在捷径。
edc65

@ edc65是的,这太过分了。但是由于它是代码高尔夫,所以我尝试最小化大小,并且操纵数组会太冗长。
coredump

3

Python 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要打印的字符的叶子或节点NN可以是a)并排连接(t==2),b)上下连接(t==1)或c)单字母正方形(t==0)。所有节点都有一个width和height字段以及一个左右子节点。解析后,p通过递归调整子节点的大小(宽度x高度)并将其联接,来打印剩余的根节点。

编辑:输出为行列表,而不是漂亮的打印


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这是我第一次,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。该方法与carpica_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)返回的水平级联ab,一个较短的通过被加长E如果必要的话。
  • s 是堆栈。
  • for循环中,我们遍历输入字符串,并替换s为新值:如果|(大于z),则弹出两个值,然后推入它们H(如果-小于(a)),则弹出两个值,即转置,输入到H,再次转置并推入结果,否则推入带有字母的1x1数组。
  • 最后,我们打印第一个元素 s
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.