裁剪ASCII艺术挑战赛


13

ASCII艺术很有趣。现代文本编辑器非常擅长处理文本。现代编程语言可以胜任吗?

ASCII艺术处理中的一项常见任务是将文本裁剪为两个字符之间的矩形。这是您在此挑战中必须执行的任务。

细节

您的程序将接受3个输入:

  • 第一个是块的“开始”字符-标记左上角
  • 第二个是块的“结束”字符-标记右下角
  • 第三种是某种形式的多行文本,可以是字符串,也可以是字符串列表,或者是文件名,或者其他任何形式

结果将是多行文本(同样以上述任何一种格式)被裁剪为给定输入之间的矩形。请注意,前两个输入可能不是唯一的。

边缘情况

盒子的体积必须始终至少为2。因此,这些:

()     (
       )

盒子,但这些:

)(     )      (
       (     )

不是(带有start = (和end = ))。

输入将仅包含一个框。因此,开始和结束字符只能出现一次,除非它们是相同的字符,在这种情况下,它们必须恰好出现两次。

此外,输入中的每一行必须至少与从行首到输入中框的右边缘的距离一样长。

您的程序不需要处理无效的输入。它们可能导致不确定的行为。

规则

适用典型的代码高尔夫球规则。最短的代码获胜。

例子

晴天: start: ( end: ) input:

This is some text
. (but this text
  is in a box  ).
So only it is important.

输出:

(but this text
is in a box  )

注意水平空间的剥离。ASCII艺术品是二维的。

下雨天: start: ( end: ) input:

This is some text (
But is that even  )
really a box?

输出:

(
)

相同的开始/结束: start: / end: / input:

Oh, I get how this could be useful
 /----------------------------\
 | All this text is in a box! |
 \----------------------------/

输出:

/----------------------------\
| All this text is in a box! |
\----------------------------/

输入无效: start: ( end: ) input:

Boxes are rectangular ( so this has
0 volume ) which is illegal.

无效的输入2: start: ( end: ) input:

(The lines must already be square 
so this line that is too short
relative to this end, is illegal)

一个有效的盒子,外面的线比盒子短吗?
seadoggie01

1
已澄清且无效的输入
-LambdaBeta

如果输入无效,结果如何?还是提到了他们,这样就不必照顾他们了?
Uriel

1
结果很像C中未定义的行为,不用担心,一切顺利。
LambdaBeta

这是一个讨厌的小挑战:干得好!
seadoggie01

Answers:


15

VIM,16,12字节/击键

#<C-v>Nj*yggVGp

在线尝试!在V解释器中

现代文本编辑器非常擅长处理文本。现代编程语言可以胜任吗?

我敢打赌,旧的文本编辑器会更好!:D

即使不一定需要,此答案也适用于两个给定的“无效”输入,输出

 rectangular (
) which is ill

(The lines must already be square
so this line that is too short
relative to this end, is illegal)

说明:

#               " Move backward to the previous occurrence of the word (or in this case, character) under the cursor
 <C-v>          " Start a visual block selection
      N         " Go to the next occurrence of the last searched term (guaranteed to be line 1)
       j        " Move down a line
        *       " Move forward to the next occurrence of the character under the cursor
         y      " Yank (copy) the whole visually selected block
          gg    " Go to line 1
            VG  " Select every line
              p " And paste what we last copied over it, deleting the whole buffer and replacing it with the block

1
顺便说一句,这正是我要提示我编写此挑战的用例。我的q宏之类的/\/<cr><c-v>nygv$o0dp东西太久了:)
LambdaBeta

2
是的,矩形是最病的
AdmBorkBork

6

果冻,13 个字节

=€SŒṪr/,þ/Zœị

双向链接在左边接受一个开始和结束字符列表,在右边接受一个行列表(作为字符列表),从而产生一个行列表(作为字符列表)。

在线尝试!(完整程序-如果输入是有效的Python,则将需要使用Python字符串引号。)

怎么样?

=€SŒṪr/,þ/Zœị - Link: [start, stop], lines
 €            - for each (of [start, stop]):
=             -   equals? (vectorises across the lines)
  S           - sum (vectorises)
   ŒṪ         - multi-dimensional truthy (i.e. non-zero) indices
      /       - reduce by:
     r        -   inclusive range (vectorises)
         /    - reduce by:
        þ     -    outer product with:
       ,      -       pair
          Z   - transpose
           œị - multi-dimensional index-into (the lines)

例如,使用left = ['a', 'b']和right(作为字符列表的列表-行):

--------
--a+++--
--++++--
--+++b--
--------

=€产生两个列表的列表(第一个执行'a'=,第二个'b'=):

00000000         00000000
00100000         00000000
00000000    ,    00000000
00000000         00000100
00000000         00000000

将其相加得出一个列表列表(逐元素累加):

00000000
00100000
00000000
00000100
00000000

ŒṪ然后给我们非零的(1索引)多维索引[[2,3],[4,6]]-即[[top,left],[bottom,right]]

r/然后执行[2,3]r[4,6]它,因为rvectorises,是像[2r4, 3r6],评价对[[2,3,4],[3,4,5,6]]-即[rows,columns]

,þ/然后执行[2,3,4],þ[3,4,5,6]where þ是外生成指令并,成对。这将按[row,column]列产生所有值,在这种情况下:

[[[2,3],[3,3],[4,3]],
 [[2,4],[3,4],[4,4]],
 [[2,5],[3,5],[4,5]],
 [[2,6],[3,6],[4,6]]]

我们希望按行排列它们,因此Z用于将其转置为:

[[[2,3],[2,4],[2,5],[2,6]],
 [[3,3],[3,4],[3,5],[3,6]],
 [[4,3],[4,4],[4,5],[4,6]]]

最后œị索引回输入行:

a+++
++++
+++b

值得注意的是,当两个包围字符都相同时=€,两次都可以识别但SŒṪ最终却做对了,因为这2是真实的,例如['a','a']

--------         00000000   00000000        00000000
--a+++--         00100000   00100000        00200000
--++++--  =€ ->  00000000 , 00000000  S ->  00000000  ŒṪ ->  [[2,3],[4,6]]
--+++a--         00000100   00000100        00000020
--------         00000000   00000000        00000000

...我阅读了说明,但仍然不明白。o_o能否添加一个可行的示例?
DLosc

激励措施:如果有充分的解释,我会接受您的回答。:)
LambdaBeta

1
@DLosc-完成,希望对您有所帮助。
乔纳森·艾伦,

@LambdaBeta-V答案更短。
乔纳森·艾伦,

...甚至是Vim的答案。
乔纳森·艾伦

5

APL(Dyalog)38 30字节

@EriktheOutgolfer节省了4个字节

(1-⍨w-⍨⊃⍸⎕=s)↑(w←∊⊃⌽⍸⎕=s)↑s←↑⎕

在线尝试!


太复杂。您可以接受矩阵而不是矢量的向量,使用来找到两个位置⍸matrix∊separators,并对其进行取/放
ngn

(⍸a=⎕)↓(1+⍸a=⎕)↑a←⎕⎕io←0
ngn

@ngn OP表示行之间的字符数不同,因此我假设输入应在处理前为矢量。即使这样,我也需要选择部分(首先旋转),以防定界符显示几次(请参阅第三个测试用例),但是我想drop确实切了几个字节,所以谢谢!进入PC后,我将进行更新
Uriel

糟糕...我忘了第三种情况,对不起。然后可能是:(⊃{⌽⊖⍵↓⍨⊃⍸⍺=⍵}/⎕⎕⎕原文如此,带有3个尾随的四边形)甚至更短。或者,... ⎕⎕(↑⎕)如果不允许使用预混合矩阵。
ngn

3

果冻,14字节

œẹⱮẎQr/Ṛṭþ/œị⁸

在线尝试!


我尝试在其他一些示例上运行您的代码,但仅崩溃。那是一个错误,还是我做错了什么?
Ilmari Karonen

@IlmariKaronen您没有正确引用第二个参数(在帖子中未提及);用单引号或双引号引起来。调用它的方式,第二个参数是一个空(Python)元组(()),而不是'()'。如果它是可解析的,则需要用引号引起来,但是我//不需要用引号(没有操作数的整数除法运算符?hm ...)。
Erik the Outgolfer

@IlmariKaronen我“认为” ()果冻只是将其解释为某种特殊字符。我尝试的大多数字符都是对的。我很想听听人们更熟悉Jelly的想法。编辑:忍者由埃里克(Outrik)
LambdaBeta


3

Python 2,130个字节

def f(s,e,t):
 a=[(i,l.find(c))for i,l in enumerate(t)for c in s+e if c in l];r,c,R,C=a[0]+a[-1]
 for l in t[r:R+1]:print l[c:C+1]

在线尝试!


2

画布,37 字节

{³⁴⁰;x≡‽┐
X⁸)J╵⁶;┤ω┤⁵X⁶⁸⁰K├;┐┤└∔┘┘∔;@

在这里尝试!

36个字节用于获取字符的坐标(并将其转换为x,y,w,h,因为那样就需要这样做)和1个字节用于获取小节。.必须有一个更好的方法


2

JavaScript(ES6),98个字节

将输入作为两个整数和一个字符串数组。返回字符串数组。

(x,y,a,X=Y=0)=>a.filter(s=>!Y&&(Y=-~s.indexOf(y,X?X-1:X=-~s.indexOf(x)),X)).map(s=>s.slice(X-1,Y))

在线尝试!

已评论

( x,                          // x = start character
  y,                          // y = end character
  a,                          // a[] = array of strings
  X =                         // X = position of x, plus 1
  Y = 0                       // Y = position of y, plus 1
) =>                          //
  a.filter(s =>               // for each string s in a[]:
    !Y &&                     //   reject this string if Y is non-zero
    (                         //   otherwise, use the 2nd condition:
      Y = -~s.indexOf(        //     update Y:
        y,                    //       by looking for y in s
        X ?                   //       if X is non-zero:
          X - 1               //         start the search at X - 1
        :                     //       else:
          X = -~s.indexOf(x)  //         update X and start the search at X
      ),                      //     end of Y update
      X                       //     keep this string if X is non-zero
    )                         //   end of 2nd condition
  )                           // end of filter()
  .map(s =>                   // for each remaining string s:
    s.slice(X - 1, Y)         //   remove left and right characters outside the box
  )                           // end of map()

filter map?!用reduce或使用递归解决方案构建新数组会不会更短?在我的手机上,在酒吧下,或者我自己去试一试。
粗野的

@Shaggy有可能确实是一个较短的方式,但我认为这种方法是注定要使用2次:第1次前的第二环不能启动一个终止与双方XY已知的肯定。
Arnauld

2

Java 10,204个字节

(s,e,a)->{int b=-1,i=0;for(;i<a.length;i++)a[i]=(b=b<0?a[i].indexOf(s):b)<0|a[i].length()<b?"":a[i].substring(b);for(b=-1;i-->0;)a[i]=(b=b<0?a[i].indexOf(e):b)<0|a[i].length()<b?"":a[i].substring(0,b+1);}

修改输入数组,而不是返回一个新的数组以节省字节。确实,这意味着删除的行变成了""。如果不允许这样做,我将对其进行更改。

在线尝试。

说明:

(s,e,a)->{                 // Method with 2 Strings & String-array parameters and no return
  int b=-1,                //  Boundaries-integer, starting at -1
  i=0;for(;i<a.length;i++) //  Loop `i` in the range [0, amountOfLines)
    a[i]=                  //   Change the `i`th line in the array to:
      (b=b<0?              //    If `b` is -1:
          a[i].indexOf(s)  //     Set `b` to the index of `s` in the current line
                           //     (which is still -1 if it's not found)
         :                 //    Else (starting index already found)
          b                //     Leave `b` unchanged
      )<0                  //    Then, if `b` is -1,
         |a[i].length()<b? //    or the current line-length is too short:
       ""                  //     Remove the current line
      :                    //    Else:
       a[i].substring(b);  //     Shorten the line by removing every character before `b`
  for(b=-1;                //  Reset `b` to -1
      i-->0;)              //  Loop `i` in the range (amountOfLines, 0]
    a[i]=                  //  Change the `i`th line in the array to:
       (b=b<0?a[i].indexOf(e):b)<0|a[i].length()<b?"":
                           //   Similar as above (with end `e` instead of start `s`),
         a[i].substring(0,b+1);}
                           //   except we remove every character after `b` this time

例如:

用输入start = "("end = ")"lines =

["This is some text",
 ". (but this text",
 "  is in a box  ).",
 "So only it is important."]

第一个循环将在顶部和左侧进行裁剪,将其更改为:

["",
 "(but this text",
 "is in a box  ).",
 " only it is important."]

第二个循环将在底部和右侧进行裁剪,将其更改为:

["",
 "(but this text",
 "is in a box  )",
 ""]

1

视网膜0.8.2,110字节

^((.)¶.)(.*¶)+(.*\2)
$1¶$4
^(.)(¶.¶\1)
$2
}s`(?<=^.¶.+)¶.
¶
s`^¶(.)¶(.*\1).*
$2
+m`^((.)+).¶((?<-2>.)+)$
$1¶$3

在线尝试!说明:

^((.)¶.)(.*¶)+(.*\2)
$1¶$4

删除框第一行之前的输入行。

^(.)(¶.¶\1)
$2

如果起始字符在输入的左列中,则将其删除。

}s`(?<=^.¶.+)¶.
¶

如果尚未删除起始字符,则将所有输入列左移一并从头开始重复。

s`^¶(.)¶(.*\1).*
$2

删除结束字符以及结束字符后输入中的所有内容。

+m`^((.)+).¶((?<-2>.)+)$
$1¶$3

将所有行截断为下一行的长度。这是通过对每一行中除一个字符之外的所有字符进行计数,然后尝试在下一行中匹配多达这么多个字符来实现的。如果成功,则第二行较短,因此删除最后一个字符,并重复循环。


0

C(gcc),237字节

f(c,r,o,p)char*p,*c;{char*_=strchr(p,r),*a,b;*_=0;a=strrchr(p,10);a=(a?a:p);*_=r;r=_-a;p=a;_=strrchr(p,o);*_=0;a=strrchr(p,10);a=(a?a:p);*_=o;o=_-a+1;_[1]=0;for(_=p;_;_=strchr(_+1,10)){b=_[o];_[o]=0;strcat(c,_+r);strcat(c,"\n");_[o]=b;}}

在线尝试!

我有99%的把握可以使用某种辅助函数来查找字符的水平索引和指针,从而缩短此过程,因为它将重复两次。las,我找不到足够短的方法来做,如果有时间,我可能稍后再试。

描述

f(c,r,o,p)char*p,*c;{
    char*_=strchr(p,r),*a,b;         // find opening char (and declare vars)
    *_=0;a=strrchr(p,10);            // find \n before it
    a=(a?a:p);                       // deal with single line inputs
    *_=r;r=_-a;                      // save left margin width in r
    p=a;                             // crop everything before opening line

    _=strchr(p,o);                   // find closing char
    *_=0;a=strrchr(p,10);            // find \n before it
    a=(a?a:p);                       // deal with single line inputs
    *_=o;o=_-a+1;                    // save width in o
    _[1]=0;                          // crop everything after closing char
    for(_=p;_;_=strchr(_+1,10)){       // for each line
        b=_[o];_[o]=0;
        strcat(c,_+r);
        strcat(c,"\n");
        _[o]=b;
    }
}

1
更妙的是:219个字节
扎卡里

0

Stax,15 个字节

╛↨½╝v∞░W╧)╗Ö≈☼k

运行并调试

它在输入的第一行上使用一组框定界符(1或2)。其余的行是输入正文。

拆开包装,松开包装并进行评论,看起来像这样。

            first line of input is the delimiter characters
dL          discard the first line of input and listify the rest into an array
{           begin block for iteration
  Mr        rotate matrix 90 degrees
  {         begin block for while loop
    ch      copy first row of block
    y|&C    if it insersects with the first line of input, break iteration
    D       drop the first line
  W         do-while loop until break
}4*         execute block 4 times
m           display result lines

运行这个

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.