解释///(发音为“斜杠”)


30

因为我们无法获得足够多的深奥语言高尔夫,我们可以吗?

/// —发音 斜杠)是一种有趣的小语言,它基于s///Perl成名的正则表达式替换功能。它仅包含两个特殊字符slash /和反斜杠\。您可以在esolangs Wiki上找到完整的文章,但我将在下面复制对语言的描述以及一些示例。

简而言之,它通过/pattern/repl/rest在程序中进行标识并进行尽可能多的替换来起作用。除特殊字符外,没有其他特殊字符/和,\::/划定程序中的模式和替换,同时\允许您在代码中插入文字/\字符。值得注意的是,这些不是正则表达式,只是纯字符串替换。

您面临的挑战是为///语言生成解释器,作为一个读取STDIN的程序或一个带有字符串参数的函数,并使用尽可能少的字符。

您可以使用///以外的任何语言。您不得使用任何解释///的库;但是,您可以使用正则表达式,正则表达式库或字符串匹配库。


执行

有四种状态,分别是printpatternreplacereplace。在每个国家中,除了替代

  • 如果程序为空,则暂停执行。
  • 否则,如果第一个字符为\,则对下一个字符(如果存在)进行操作,然后从程序中删除这两个字符。
  • 否则,如果第一个字符是 /,请将其删除,然后更改为下一个状态。
  • 否则,对第一个字符进行处理,然后将其从程序中删除。
  • 重复。

状态依次遍历printpatternreplace替换

  • 打印模式下,“执行某些操作”表示输出字符。
  • 模式模式下,“执行某些操作”意味着将字符添加到当前模式。
  • 替换模式下,“执行某些操作”意味着将字符添加到当前替换中。

替换模式下,您遵循一组不同的规则。重复用程序中的当前替换替换当前模式的第一个匹配项,直到不再有替换可用为止。此时,清除“模式和替换”并返回打印模式。

在程序中/foo/foobar/foo foo foo,发生以下情况:

/foo/foobar/foo foo foo
foo foo foo
foobar foo foo
foobarbar foo foo
foobarbarbar foo foo
...

这将永远循环,并且永远不会退出替换模式。同样,如果Pattern为空,则空字符串的第一个匹配项(在程序开头)总是匹配的,因此替换模式永远循环,永不停止。


例子

no

输出:no

/ world! world!/Hello,/ world! world! world!

输出:Hello, world!

/foo/Hello, world!//B\/\\R/foo/B/\R

输出:Hello, world!

a/ab/bbaa/abb

输出:a。程序不停止。

//

输出:无。

///

输出:无。程序不停止。

/\\/good/\/

输出: good

您也可以尝试在Wiki上找到一个quine 。


/-/World//--/Hello//--W/--, w/---!不去爱的种种?(尝试从末尾删除破折号)
参见

@Loovjo \ 字符转义其后的任何字符,包括/,以后可以正常使用。虽然看起来不多,但这使/// Turing-complete
algorithmhark

我认为这比esolangs Wiki文章对语言的解释更好。将在我///正在制作的IDE中使用此信息!
clabe45

Answers:


7

APL(133)

{T←''∘{(0=≢⍵)∨'/'=⊃⍵:(⊂⍺),⊂⍵⋄(⍺,N⌷⍵)∇⍵↓⍨N←1+'\'=⊃⍵}⋄⍞N←T⍵⋄p N←T 1↓N⋄r N←T 1↓N⋄''≡N:→⋄∇{⍵≡p:∇r⋄∨/Z←p⍷⍵:∇(r,⍵↓⍨N+≢p),⍨⍵↑⍨N←1-⍨Z⍳1⋄⍵}1↓N}

这是一个将///代码作为正确参数的函数。

取消高尔夫,并附有解释:

slashes←{
   ⍝ a function to split the input string into 'current' and 'next' parts,
   ⍝ and unescape the 'current' bit
   split←''∘{
       ⍝ if the string is empty, or '/' is reached,
       ⍝ return both strings (⍺=accumulator ⍵=unprocessed)
       (0=≢⍵)∨'/'=⊃⍵:(⊂⍺),⊂⍵
       ⍝ otherwise, add current character to accumulator,
       ⍝ skipping over '\'s. (so if '\/' is reached, it skips '\',
       ⍝ adds '/' and then processes the character *after* that.)
       idx←1+'\'=⊃⍵
       (⍺,idx⌷⍵)∇idx↓⍵
   }

   ⍞   next ← split ⍵      ⍝ output stage
   pat next ← split 1↓next ⍝ pattern stage, and eat the '/'
   rpl next ← split 1↓next ⍝ replacement stage, and eat the '/'

   ⍝ if there are no characters left, halt.
   ''≡next:⍬

   ⍝ otherwise, replace and continue.
   ∇{  ⍝ if the input string equals the pattern, return the replacement and loop
       ⍵≡pat:∇rpl

       ⍝ otherwise, find occurences, if there are, replace the first and loop
       ∨/occ←pat⍷⍵:∇(rpl, (idx+≢pat)↓⍵),⍨ (idx←(occ⍳1)-1)↑⍵

       ⍝ if no occurences, return string
       ⍵

   }1↓next
}

“如果没有字符了,那就停下来。” 这在///和上//foo/(即永远循环)是否正常工作?
algorithmhark

@algorithmshark:是的,在那种情况下/仍然会保留在那一点上。
marinus 2014年

11

的J - 181 190 170炭

这是一场噩梦。我从头开始重写了两次,因为它一直困扰着我。该函数采用单个字符串参数,输出到STDOUT。

(0&$`((2{.{:@>&.>)((j{.]),-i@=`p@.~:~/@[,]}.~#@p+j=.0{p I.@E.])i 5;@}.&,'/';"0;&.>)@.(2<#)@}.[4:1!:2~{:@>@p=.>@{.@[)@((0;(0,:~1 0,.2);'\';&<1 0)<;._1@;:'/'&,)i=. ::](^:_)

为了说明,我将其分解为子表达式。

i =. ::](^:_))
parse =: ((0;(0,:~1 0,.2);'\';&<1 0)<;._1@;:'/'&,)
print =: 4:1!:2~{:@>@p=.>@{.@[
eval  =: 0&$`((2{.{:@>&.>)sub 5;@}.&,'/';"0;&.>)@.(2<#)@}.
sub   =: ((j{.]),-i@=`p@.~:~/@[,]}.~#@p+j=.0{p I.@E.])i

interp =: (eval [ print) @ parse i
  • iiterate的缩写)是一个副词。它在左侧使用一个动词自变量,并返回一个动词(f)i,将其应用于自变量后会f重复应用于该自变量,直到发生以下两种情况之一:找到一个固定点(y = f y),或者抛出一个错误。定点行为是固有的^:_,并::]进行错误处理。

  • parse 将输入标记化为我所说的 半解析形式,然后将其未转义的'/'。它将转义的反斜杠绑定到其字符,但不会消除反斜杠,因此我们可以根据需要将其还原或完成。

    有趣的工作大部分发生在;:。这是一个顺序机器解释器原语,(0;(0,:~1 0,.2);'\';&<1 0)左边是对机器()的描述,右边是要解析的内容。这会进行标记化。我会注意到,这台特定的机器实际上将第一个字符视为非特殊字符,即使它是\并且应该绑定。我这样做的原因有几个:(1)状态表更简单,因此可以进一步打高尔夫球;(2)我们可以轻松地在前面添加一个虚拟字符来规避问题;(3)虚拟字符无需额外费用就可以进行一半解析,因此接下来我可以用它来设置剪切阶段。

    我们还可以<;._1在未转义/的字符上剪切标记化的结果(这是我选择作为第一个字符)。这很方便out/patt/repl/rest一步一步从所有输出,模式和替换中进行输出,但是不幸的是,这也削减了程序的其余部分,而我们需要这些/程序保持不变。我在期间将它们拼接起来eval,因为不理<;._1会它们最终会花费更多。

  • 叉子会(eval [ print)根据的副作用执行print结果parse,然后运行evalprint是一个简单的动词,它打开第一个框(我们确定可以输出的框),完成对它的解析,然后将其发送到STDOUT。但是,我们也借此机会定义了一个实用动词p

    p被定义为>@{.@[,因此它采用其左arg(如果仅给出一个arg,其作用类似于标识),采用其第一项(给出标量时的标识),然后将其取消装箱(如果已取消装箱,则标识)。这将非常方便sub

  • eval评估已处理程序的其余部分。如果我们没有完整的模式或完整的替换,eval请将其扔掉并仅返回一个空列表,该列表将通过在下一次迭代中产生;:(from parse)错误来终止评估。否则,eval完全解析模式并进行替换,纠正源的其余部分,然后将两者都传递给sub。爆炸:

                                                  @}.  NB. throw out printed part
                                           @.(2<#)     NB. if we have a pattern and repl:
          2{.                                          NB.  take the first two cuts:
                 &.>                                   NB.   in each cut:
             {:@>                                      NB.    drop escaping \ from chars
         (          )                                  NB.  (these are pattern and repl)
                                       &.>             NB.  in each cut:
                                      ;                NB.   revert to source form
                                '/';"0                 NB.  attach a / to each cut
                              &,                       NB.  linearize (/ before each cut)
                         5  }.                         NB.  drop '/pattern/repl/'
                          ;@                           NB.  splice together
        (            sub                  )            NB.  feed these into sub
       `                                               NB. else:
    0&$                                                NB.  truncate to an empty list
    
  • sub是发生一轮(可能是无限次)替换的地方。由于我们的设置方式eval,来源是右侧参数,而模式和替换在左侧捆绑在一起。由于参数是这样排序的,并且我们知道模式和替换在轮次替换中不会改变,因此我们可以使用的另一个功能(i事实上,它仅修改正确的参数,并始终向左传递)来委托J需要担心跟踪状态。

    但是,有两个麻烦点。首先是J动词最多可以有两个参数,因此我们没有一种简单的方法来访问捆绑在一起的任何东西,例如模式和替换。通过巧妙地使用p我们定义的实用程序,这并不是什么大问题。实际上,p由于其>@{.@[定义,我们只需使用即可访问一个字符中的模式:Left arg的First项的Unbox。获取替换项比较麻烦,但是最短的方法是p&|.,比手动取出要短2个字符。

    第二个问题是i在固定点上退出而不是永远循环,如果模式和替换相等,并且您进行了替换,则看起来像是J的固定点。如果我们检测到它们相等,那就是这-i@=`p@.~:~/部分,替换p&|.

                                        p    E.]    NB. string search, patt in src
                                          I.@       NB. indices of matches
                                      0{            NB. take the first (error if none)
                                   j=.              NB. assign to j for later use
                               #@p+                 NB. add length of pattern
                           ]}.~                     NB. drop that many chars from src
                       /@[                          NB. between patt and repl:
                      ~                             NB.  patt as right arg, repl as left
                  @.~:                              NB.  if equal:
            -i@=                                    NB.   loop forever
                `p                                  NB.  else: return repl
     (j{.])                                         NB. first j chars of src
           ,              ,                         NB. append all together
    (                                           )i  NB. iterate
    
  • 由于使用,该循环重复进行i,直到超出sub出现错误。据我所知,这只能在我们字符不足,抛出不完整的图案和替换集时发生。

关于此高尔夫的趣闻:

  • 一次,使用 ;:比手动遍历字符串短。
  • 0{ 应该有机会出错之前 sub进入无限循环,,因此,如果模式与替换匹配,但永远不会出现在源的其余部分中,则应该可以正常工作。但是,这可能是也可能不是未指定的行为,因为我在文档中都找不到引文。哎呀
  • 键盘中断在运行功能中被视为自发错误。但是,由于i,这些错误也会被捕获。根据您按下Ctrl + C的时间,您可能会:
    • 退出永久否定循环,退出错误 sub循环,尝试将数字连接到字符串,然后然后继续////解释错误,就好像您用无限次的自身替换了字符串一样。
    • 离开 sub一半,继续解释半字幕///表达式。
    • 脱离解释器,然后将未评估的程序//返回给REPL(尽管不是STDOUT)。

用法示例:

   f=:(0&$`((2{.{:@>&.>)((j{.]),-i@=`p@.~:~/@[,]}.~#@p+j=.0{p I.@E.])i 5;@}.&,'/';"0;&.>)@.(2<#)@}.[4:1!:2~{:@>@p=.>@{.@[)@((0;(0,:~1 0,.2);'\';&<1 0)<;._1@;:'/'&,)i=. ::](^:_)
   f 'no'
no
   f '/ world! world!/Hello,/ world! world! world!'
Hello, world!
   f '/foo/Hello, world!//B\/\\R/foo/B/\R'
Hello, world!
   f '//'  NB. empty string

   f '/\\/good/\/'
good

哇。我称其为受虐狂。+1
seequ 2014年

当我运行它时,我从每个测试用例中得到空字符串。我正在使用jqt64,您正在使用什么来运行它?
bcsb1001 2015年

@ bcsb1001我一直在直接使用(64位)jconsole二进制文件。现在检查jqt,除了/\\/good/\/测试用例外,我实际上得到了预期的结果;调试告诉我问题是我使用1!:2&4,因为jqt没有stdin / out。会调查。什么是你9!:12''9!:14''
algorithmhark

@algorithmshark我9!:12''是6 岁,9!:14''是j701 / 2011-01-10 / 11:25。
bcsb1001 2015年

4

Perl-190

$|=1;$/=undef;$_=<>;while($_){($d,$_)=/(.)(.*)/;eval(!$e&&({'/','$a++','\\','$e=1'}->{$d})||('print$d','$b.=$d','$c.=$d')[$a].';$e=0');if($a==3){while($b?s/\Q$b/$c/:s/^/$c/){}$a=0;$b=$c=''}}

///从stdin 读取程序,直到EOF。


沿着路线(m/^(.*?)(?<!\\)\/(.*?)(?<!\\)\/(.*?)(?<!\\)\/(.*)$/s即同时匹配输出,花样和替换)的方法是否会使高尔夫运动时间缩短?我自己都不认识Perl。
algorithmhark

我相信这会失败/a/\0/a
Asone Tuhid

3

PIP100个 102字节

我从来没有证明Pip是图灵完备的(尽管很明显是这样),我没有走通常的BF路线,而是觉得///很有趣。找到解决方案后,我想我会打高尔夫球并将其张贴在这里。

101个字节的代码,+ 1表示-r标志:

i:gJnf:{a:xW#i&'/NE YPOia.:yQ'\?POiya}W#iI'\Q YPOiOPOiEIyQ'/{p:VfY0s:VfIyQ'/WpNi&YviR:Xp{++y?ps}}E Oy

这是我的非公开版本,带有大量注释:

; Use the -r flag to read the /// program from stdin
; Stdin is read into g as a list of lines; join them on newline and assign to c for code
c : gJn

; Loop while c is nonempty
W #c {
 ; Pop the first character of c and yank into y
 Y POc
 ; If y equals "\"
 I yQ'\
  ; Pop c again and output
  O POc
 ; Else if y equals "/"
 EI yQ'/ {
  ; Build up pattern p from empty string
  p : ""
  ; Pop c, yank into y, loop while that is not equal to "/" and c is nonempty
  W #c & '/ NE Y POc {
   ; If y equals "\"
   I yQ'\
    ; Pop c again and add that character to p
    p .: POc
   ; Else, add y to p
   E p .: y
  }

  ; Yank 0 so we can reliably tell whether the /// construct was completed or not
  Y0
  ; Build up substitution s from empty string
  s : ""
  ; Pop c, yank into y, loop while that is not equal to "/" and c is nonempty
  W #c & '/ NE Y POc {
   ; If y equals "\"
   I yQ'\
    ; Pop c again and add that character to s
    s .: POc
   ; Else, add y to s
   E s .: y
  }

  ; If the last value yanked was "/", then we have a complete substitution
  ; If not, the code must have run out; skip this branch, and then the outer loop
  ; will terminate
  I yQ'/ {
   ; While pattern is found in code:
   W pNc {
    ; Set flag so only one replacement gets done
    i : 0
    ; Convert p to a regex; replace it using a callback function: if ++i is 1,
    ; replace with s; otherwise, leave unchanged
    c R: Xp {++i=1 ? s p}
   }
  }
 }
 ; Else, output y
 E Oy
}

在线尝试!(请注意,当程序未终止时,TIO不会提供任何输出,并且也有时间限制。对于较大的示例和无限循环,建议从命令行运行Pip。)


我想这应该是pip + -r101字节
ASONE Tuhid

3

C ++:Visual C ++ 2013 = 423,g ++ 4.9.0 = 442

这永远不会成功,但是由于我已经决定将所有未来的软件项目都用这种很棒的语言编写,因此我需要一个解释器,而且我认为我也很乐意分享我所做的那个...

得分的区别是Visual C ++不需要第一个include,而g ++则需要。得分假设行尾数为1。

#include<string.h>
#include<string>
#define M(x)memset(x,0,99);
#define P o[i])
#define N(x)P;else if(n<x)(P==92?
#define O (o[++i]):(P==47?n++:
#define S std::string
int main(int n,char**m){S o=m[1];char p[99],*q=p,r[99],*s=r;M(p)M(r)for(int i=0,t;i<=o.size();++i){if(!N(3)putchar O putchar(N(4)*q++=O(*q++=N(5)*s++=O(*s++=P;if(n>4){for(;;){if((t=o.find(p,i+1))==S::npos)break;o=o.substr(0,t)+r+o.substr(t+strlen(p));}M(p)M(r)n=2;q=p;s=r;}}}

1
您可以重写if(!o[i]);if(P保存字符,还是我误解了#define的工作方式?
algorithmhark

@algorithmshark我是怎么想念它的?!如果(P是完美的,我会改变它。
杰里耶利米书

Pin main中的每个实例后面都有一个空格,因此您可以通过用分号替换这些空格并将其从中删除来保存字符#define。然后,如果你可以用#define其他的内部S,您可以节省更多一些通过改写N(x)(92==P代替o[i]==92O亦同。
algorithmhark

@algorithmshark您在此方面显然比我要好得多。感谢您的帮助。
杰里·耶利米

我知道这已经有4年的历史了,但是重写N(x)为as P;else if(n<x)(P==92?并相应地更改其调用N可以节省一些字节。
扎卡里

2

Python 2(236),Python 3(198?)

from __future__ import print_function
def d(i):
 t=0;p=['']*3+[1]
 while i:
  if'/'==i[0]:t+=1
  else:
   if'\\'==i[0]:i=i[1:]
   p[t]+=i[0]
  i=i[1:]
  print(end=p[0]);p[0]=''
  if t>2:
   while p[1]in i:i=i.replace(*p[1:])
   d(i);i=0

称为d(r"""/foo/Hello, world!//B\/\\R/foo/B/\R""")。仅当///程序包含换行符时才需要三引号:否则可以使用简单的引号。

编辑:此解释器现在按预期方式打印内容(以前它仅在最后打印,请参见注释)。对于Python 3,请删除第一行(但是我的旧安装中没有Python 3,因此无法确定没有其他更改)。


解释器直到终止出现问题才打印任何内容。在///中编写无限循环是可能的,因此您的解释器在非终止但仍然打印内容的程序上失败。
自豪的haskeller 2014年

@proudhaskeller已修复。
布鲁诺·勒弗洛赫

实际上,这不是固定的,它不会打印任何内容/a/ab/bbaa/abb
Beta Decay's

@BetaDecay /a/ab/bbaa/abb将陷入无休止的循环,而不会打印任何内容,因为第一个替换是a=> ab。正确的a/ab/bbaa/abb广告宣传。
algorithmhark

@BetaDecay:除了algorithmshark建议的更改之外,您可能还需要包括命令行选项-u以强制取消对输出缓冲区的缓冲。
Bruno Le Floch 2014年

2

眼镜蛇-226

sig Z as String
def f(l='')
    m=Z(do=[l[:1],l=l[1:]][0])
    n as Z=do
        if'/'<>(a=m())>'',return if(a=='\\',m(),a)+n()
        else,return''
    print n()stop
    p,s=n(),n()
    if''<l
        while p in l,l=l[:l.indexOf(p)+1]+s+l[p.length:]
        .f(l)

2

红宝石119110字节

异常终止

r=->s,o=$>{s[k=s[0]]='';k==?/?o==$>?s.gsub!([r[s,''],e=r[s,'']][0]){e}:t=o:o<<(k==?\\?s[0]+s[0]='':k);t||redo}

在线尝试!

干净终止(116字节)

r=->s,o=$>{s[k=s[0]||exit]='';k==?/?o==$>?s.gsub!([r[s,''],e=r[s,'']][0]){e}:t=o:o<<(k==?\\?s[0]+s[0]='':k);t||redo}

在线尝试!


1

Python 2/3(211字节)

以下代码基于Bruno Le Floch的答案,与Python 2和Python 3兼容。

而且,它是迭代而不是递归的,不会冒Python的最大递归深度的风险。

def S(c):
 while c:
  B=["","",1]
  for m in 0,1,2:
   while c:
    if"/"==c[0]:c=c[1:];break
    if"\\"==c[0]:c=c[1:]
    if m:B[m-1]+=c[0]
    else:yield c[0]
    c=c[1:]
  while c and B[0]in c:c=c.replace(*B)

您好,欢迎来到PPCG。您可以打高尔夫球in(0,1,2)in 0,1,2[""]*2+[1]["","",1],导致211个字节
乔纳森·弗雷奇

我已链接到所引用的答案,并添加了“字节”一词。如果您不同意我的编辑,请随时回滚。
乔纳森·弗雷奇

感谢乔纳森,非常欢迎您提出建议!
卡洛斯·卢纳

0

培根391个 387 395字节

从本页的贡献中,我只有Python程序可以工作。其他的只用于某些///样本,或者根本不起作用。因此,我决定添加我的版本,它是BASIC中的一种实现。

与BASIC参加CodeGolf竞赛并不容易,因为BASIC使用长字作为陈述。BASIC中常见的唯一缩写是“?” 标志,表示PRINT。

因此,以下程序可能永远不会赢,但至少它可以与Codegolf页面和Esolangs Wiki上的所有演示代码一起使用。包括所有版本的“ 99瓶啤酒”。

p$=""
r$=""
INPUT i$
WHILE LEN(i$)
t$=LEFT$(i$,1)
i$=MID$(i$,2)
IF NOT(e) THEN
IF t$="\\" THEN
e=1
CONTINUE
ELIF t$="/" THEN
o=IIF(o<2,o+1,0)
IF o>0 THEN CONTINUE
FI
FI
IF o=1 THEN
p$=p$&t$
ELIF o=2 THEN
r$=r$&t$
ELIF o=0 THEN
IF LEN(p$) THEN i$=REPLACE$(i$,p$,r$)
IF NOT(INSTR(t$&i$,"/")) THEN
?t$;
BREAK
ELSE
?LEFT$(i$,INSTR(i$,"/")-1);
i$=MID$(i$,INSTR(i$,"/"))
FI
p$=""
r$=""
FI
e=0
WEND
?i$

添加了INPUT语句以获取用户输入。
彼得
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.