改造一个原始世界


16

这项挑战基于Helka Homba的编程原始世界 ”问题。从这个问题出发,原始程序的定义是:

让我们将原始程序定义为本身没有任何错误,但是如果您通过删除任何连续的N个字符的子字符串来修改它会出错的程序,其中1 <= N < program length

例如,三个字符的Python 2程序

`8`

是一个原始程序谢谢Sp),因为删除长度为1的子字符串所导致的所有程序都会产生错误(实际上是语法错误,但是任何类型的错误都可以):

8`
``
`8

并且由于删除长度为2的子字符串而产生的所有程序也会导致错误:

`
`

例如,如果`8曾经是一个没有错误的程序,那么`8`它将不会是原始的,因为所有删除子字符串的结果都必须出错。

笔记:

  • 编译器警告不算作错误。
  • 错误的子程序可以接受输入或给出输出,或执行其他任何操作,只要它们最终会出错就可以。

您的任务是创建一个长度不为零的程序,该程序准确地打印其自身的源代码,并遵循适当规则,并且原始。

每种语言的最短答案(以字节为单位)获胜。


我假设没有错误的语言无法竞争?
ATaco

@ATaco不幸的是。其他语言(例如lisp)的语法结构使得不可能制作有用的原始程序。
谢尔瓦库

RIP实际上/严重
ATaco

“每种语言的最短答案以字节为单位获胜。” 我不确定短时间是原始程序的最佳方法。
P. Siehr'7

@ P.Siehr您会推荐什么?
谢尔瓦库

Answers:


6

Haskell,132个字节

q x=if length x==132then putStr x else fail[];main=q$(++)<*>show$"q x=if length x==132then putStr x else fail[];main=q$(++)<*>show$"

在线尝试!

这是奎纳的延伸

main=putStr$(++)<*>show$"main=putStr$(++)<*>show$"

通过将数据字符串与show自身的带引号的版本(使用)连接并打印结果来工作。但是,这并不是原始数据,因为可以删除数据字符串中的任何字符而不会失败,并且也$(++)<*>show$可以(++)<*>部分而不会破坏程序。

为了解决这个问题,定义了一个自定义打印函数,该函数q检查给定字符串的长度,fail如果长度小于132 ,则调用该函数。这捕获了从数据字符串中删除任何序列以及$(++)<*>show$或的删除(++)<*>,因为在两种情况下,结果字符串传递给q较短。

q132可以缩短为113322,但在每种情况下再次fail被调用。

据我所知,除去任何其他子字符串会导致语法或类型错误,因此该程序甚至根本不会编译。(Haskell的严格类型系统在这里派上用场了。)

编辑:感谢ØrjanJohansen和Shelvacu指出缺陷!


恐怕fail[]|length x/=122可以将其移除。fail[]:[putStr x|length x==122]可能会更好。
与Orjan约翰森

啊,不,然后|length x==122可以将其删除。if length x==122 then putStr x else fail[]也许?
与Orjan约翰森

@ØrjanJohansen很好,我之前有if then else过,但我想可以缩短它。
莱科尼

2
putStr x可以变成p x,当我在系统上尝试运行很长一段时间后才杀死它时,我怀疑尾调用递归已优化,因此这是一个无限循环。我对haskell的了解不足,无法就如何解决问题提供任何建议。
谢尔瓦库

@Shelvacu糟糕。重命名pq应解决此问题。
与Orjan约翰森

4

Python 3,113字节

for[]in{113:[]}[open(1,"w").write((lambda s:s%s)('for[]in{113:[]}[open(1,"w").write((lambda s:s%%s)(%r))]:a'))]:a

在线尝试!

怎么运行的

由于第二条语句可能会被删除,因此我们不能轻易使用多个语句,因此我们从单表达式quine开始:

print((lambda s:s%s)('print((lambda s:s%%s)(%r))'))

为了防止子字符串被删除,我们使用open(1,"w").write代替print。在Python 3中,write返回书面字符数,我们将验证该字符数113以确保没有删除字符串的任何部分。为此,我们在字典中查找返回值{113:[]},然后使用循环遍历结果for[]in…:a,如果没有得到一个空的Iterable或for删除了该语句,这将失败。


1
您能否解释一下代码的工作方式?
Shelvacu

@Shelvacu是的,添加了。
Anders Kaseorg '17

3

Ruby,78个字节

eval(*[($>.write((s=%{eval(*[($>.write((s=%%{%s})%%s)-78).chr])})%s)-78).chr])

当我想到挑战以确保可能实现时,我写了这篇文章。它使用了对原始原始挑战的回答之一中的相同“包装器” 。

说明:

  • eval(*[ expr ])

    这将评估作为ruby程序返回的所有代码。这可以有效地测试代码返回的字符串是有效的ruby程序。方便的是,ruby程序可以为空白,也可以仅包含空格。

    “ splat”运算符*允许您将数组用作函数的参数。这也意味着如果eval将其删除,则生成的程序为(*[ expr ]),这不是有效的红宝石。

  • ($>.write( 力量 )-78).chr

    $> 是STDOUT的简短变量。

    $>.write(foo) 将foo写入STDOUT,并且重要的是,对于此代码,返回写入的字节数。

    $>.write(foo)-78: 这里 78是程序的长度,因此,如果程序没有被篡改,也将是写入的字节数。因此,在无干扰的情况下,它将返回零。

    num.chr返回num作为字符,例如0.chr将返回包含单个空字节的字符串。在无损程序中,这将为给出一个带有单个空字节的字符串eval,这是一个有效的红宝石程序,是无操作的。

    另外,程序可以删除子字符串,使其为just eval(*[(78).chr])eval(*[(8).chr]),这意味着数字常量不能以任何数字(0、4、9、10、11、12、13、26、32、35、48 ,49、50、51、52、53、54、55、56、57、59、64、95),因为它们是有效的单字符红宝石程序的ASCII码。

  • %{ 力量 }

    这是Ruby中字符串文字的鲜为人知的语法。这里使用它的原因是{}可以在字符串中使用平衡对,这意味着该语法可以包含自身。例如,%{foo{bar}}与相同"foo{bar}"

  • (s=%{ 数据 })%s

    这定义了变量 s该数据条数据为printf字符串。

    红宝石中的分配返回分配的内容,因此这与首先分配s然后运行s%s

    %字符串上的语法糖相当于红宝石的sprintf。的%s该数据中的数据本身应该被嵌入表示。

    这部分代码定义了卷的数据部分,并将其嵌入自身中以创建完整的代码。


3

标准ML(MLton) 204个 182 189字节

val()=hd[(fn s=>let val$ =s^"\""^String.toString s^"\"]"val(189,%)=(size$,$)in print%end)"val()=hd[(fn s=>let val$ =s^\"\\\"\"^String.toString s^\"\\\")\"val(189,%)=(size$,$)in print%end)"]

在线尝试!

对于MLton,完整的SML程序要么是用;(例如print"Hello";print"World";)分隔和终止的表达式,要么是使用varfun关键字(例如var _=print"Hello"var _=print"World")的声明,其中_是通配符,也可以用任何变量名代替。

第一个选项对原始编程没有用,因为;它本身就是一个有效的程序(不执行任何操作,但也不会出错)。第二种方法的问题是,var _=print"Hello"可以将like的声明缩短为just var _="Hello"(甚至even var _=print),因为var只要右侧的声明是有效的SML表达式或值(SML是一种函数式语言,因此可以使用也用作值)。

在这一点上,我准备在SML不可能申报的原始设置,当一个偶然的机会,我偶然发现模式匹配val-declarations。事实证明,声明的语法不是val <variable_name> = <expression>but val <pattern> = <expression>,其中模式可以由变量名称,常量和构造函数组成。由于print函数具有类型string -> unit,我们可以在unit-value 上使用模式匹配()来强制将print函数实际应用于字符串:val()=print"Hey"。使用这种方法,删除print或会"Hey"导致Pattern and expression disagree-error。

有了这种原始打印方式,下一步就是写一个卷子,最后再添加一些保存保护功能。我以前使用了一种简单的SML quine技术(请参阅修订历史),但是Anders Kaseorg指出了另一种方法,可以节省一些字节。它使用内置String.toString函数来处理字符串转义,并且具有一般形式<code>"<data>",其中before "<data>"是转义的字符串code

val()=(fn s=>print(s^"\""^String.toString s^"\""))"val()=(fn s=>print(s^\"\\\"\"^String.toString s^\"\\\"\"))"

这是一个有效的方法,但还不是很原始。首先,Anders Kaseorg发现MLton接受单引号"作为代码而不会产生错误,这意味着我们不能使代码以上述引号结尾。防止这种情况的最短方法是将所有内容包装val()=在一对括号中,但是然后可以将代码简化为val()=()。我发现的第二个最短的方法是使用val()=hd[ ... ],也就是说,我们将所有内容包装到列表中并返回其第一个元素以使类型检查器满意。

为确保不会删除任何未删除的数据字符串部分,val-declaration中的模式匹配再次派上用场:要打印的最终字符串的长度(因此程序长度)应等于195,因此我们可以用抽象let val t=... val 195=size t in print t endfn代替print(...)。删除字符串的一部分会导致长度小于189,从而引发Bind异常。

仍然存在一个问题:整个val 195=size t支票都可以被删除。我们可以通过将支票扩大为与tuple:匹配来防止这种情况val t=... val(216,u)=(n+size t,t)in print u end,例如,删除支票会导致未绑定变量u

总之,这产生了以下195字节的解决方案:

val()=hd[(fn s=>let val t=s^"\""^String.toString s^"\")"val(195,u)=(size t,t)in print u end)"val()=hd[(fn s=>let val t=s^\"\\\"\"^String.toString s^\"\\\")\"val(195,u)=(size t,t)in print u end)"]

应用使用操作变量名状的高尔夫技巧!$%代替nt并且u为了节省一些空白(见本提示)导致最终的182字节的版本。

在解释中未明确说明的所有其他子字符串删除应导致语法或类型错误。

编辑1: length(explode t)size t
编辑2:感谢Anders Kaseorg提供的另一种方法,并指出了“漏洞”。


通过"\""直接写入并String.toString转义来使用−2字节
安德斯·卡塞格

等等,这太可怕了:MLton似乎接受了该程序",产生了空输出(TIO)。
安德斯·卡塞格

@AndersKaseorg Huh,这很奇怪。但是,应该可以通过使用另一个解决此问题let ... in ... end
Laikoni '18

@AndersKaseorg我已解决此问题,希望不引入新的“漏洞”。
Laikoni

实际上,我研究了MLton "作为程序接受的问题,并且似乎该提交中的错误已得到修复,因此只要您指定MLton的未发布Git版本,您的182或180就可以了。
安德斯·卡塞格
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.