解释您的语言,但不能解释您自己?


21

说“解释X”有很多挑战,其中X是一种简单的语言。我认为这太无聊了。为了给互联网上所有拖延的人做一些有趣的事情,您可以尝试做以下挑战:

挑战

选择一种语言$LANG$LANG可以是任何图灵完整的编程语言或图灵完整的子集。请注意,如果您在$LANG解释中忽略了语言的功能,则也不要在自己的程序中使用它,因为您的提交也必须用编写$LANG

写一个编译器/解释器$LANG编写的$LANG。您可以使用eval可用于编写此编译器的所有语言功能(包括和朋友)。为了使任务更具挑战性,存在一个限制:您的程序应该能够解释/编译$LANG除解释器/编译器本身之外的所有有效程序。如果碰巧要解释/编译的程序是您的解释器或编译器本身(无论文件名如何),则您的程序应该执行与解释器或编译器的功能完全无关的操作(例如,打标或打印Hello, world!)。

为了使此任务更加复杂,您的程序在编译或解释时不得读取其自身的源代码。

技术指标

  • 此任务是代码高尔夫。字符最少的提交是正确的。如果出现平局,则首先提交的解决方案将获胜。
  • 您的程序/脚本应从文件读取要解释的程序。您可以硬编码其路径和名称。读取文件后,您可以将文件编译为另一个文件(该文件必须在您的系统上是可执行文件)或直接运行它。如果$LANG缺少文件读取功能,则可以选择另一种方式来读取适合的代码$LANG。您可能没有选择$LANG作为另一种语言的子集,但已删除了文件读取功能。
  • 通常使用代码高尔夫球规则。那就是:如果解决方案使用它变得微不足道(例如定义一个完全实现该解决方案的单字符程序),那么为解决该挑战而编写的个人宠物语言将被禁止。鼓励滥用规则。

只要可以完成定义,我们就可以为此定义一种语言吗?
Cruncher 2013年

@Cruncher是的,你是。有关更多详细信息,请参见规格的最后一个要点。
FUZxxl

Answers:


8

红宝石,63岁

b=$<.read
t="b=$<.read\nt=%p\nb!=t%%t&&eval(b)"
b!=t%t&&eval(b)

只要没有更小的解决方案,答案就会被接受。
FUZxxl 2011年

11

Perl,89个字符,不作弊

$_=q($_=q(Q);s/Q/$_/;($q=join"",<>)eq$_?die:eval$q);s/Q/$_/;($q=join"",<>)eq$_?die:eval$q

请注意,此代码对于什么才算“本身” 非常挑剔。特别是,如果输入中有任何结尾的换行符或其他多余的空格,它将无法识别自己。要对其进行测试,请将其保存到名为(例如)的文件中,unquine.pl然后执行以下操作:

$ perl unquine.pl unquine.pl
Died at unquine.pl line 1, <> line 1.

请记住,该unquine.pl文件的长度应恰好为 89个字节,不要多也不要少。使用其他Perl脚本作为输入来运行它只会执行其他脚本,如下所示:

$ perl unquine.pl hello.pl
Hello, world!

顾名思义,该实现基于一个quine,具体来说就是:

$_=q($_=q(Q);s/Q/$_/);s/Q/$_/

该代码设置为$_等于自身。程序的其余部分(当然必须在里面重复$_)仅与$_输入进行比较,如果它们匹配则死亡,否则评估输入。


您可以替换&&/ ;与三元(一个字符关,通过quining加倍)对。好主意和实施!
JB

@JB:很好!现在降至89个字符。
Ilmari Karonen 2011年

5

GolfScript,30个字符

{`".~"+"#{$<.read}".@=!{~}*}.~

该程序读取命令行上命名的文件的内容,如果它与上面的代码不完全相同,则将其解释为GolfScript。如果输入与上面的代码完全相同,则将直接按原样打印(末尾附加换行符)。

这是对该自我识别程序的相当直接的改编。特别:

  • { } 是GolfScript中的代码块文字。
  • .~应用于代码块,复制该块并执行复制。

在代码块内:

  • ` 字符串化代码块的副本。
  • ".~"+.~向其附加字符,产生一个包含程序源代码的字符串。
  • "#{$<.read}"是有记录的黑客,可在GolfScript中执行Ruby代码。在这种情况下,它执行Ruby语句$<.read(从Lowjacker的Ruby解决方案中偷偷偷走了),该语句读取并返回命令行上指定的任何文件的内容。之所以需要这种技巧,是因为GolfScript本身不提供显式的文件I / O功能。
  • .@ 复制并重新排列堆栈顶部的元素,以便堆栈包含文件内容的两个副本以及此程序的源代码。
  • =! 比较堆栈上的前两个项目(即文件内容和源),如果不同则返回1,如果相同则返回0。
  • {~}*仅在比较结果为1时,才将文件内容的其余副本评估为GolfScript代码。(从技术上讲,它执行代码块{~}的次数与堆栈上给出的次数相同,即0或1次。)块~是GolfScript eval运算符。)

附言 如果允许从stdin读取要执行的代码,则可以用21个字符解决此难题,而不必使用Ruby:

{`".~"+1$=!{""\~}*}.~

该程序将从stdin读取输入字符串,如果不匹配其自身的源,则执行它(输入为空)。像上面的程序一样,与源匹配的输入也会被简单地回显。


看起来不错,但看起来好像您不是从文件中读取输入的。
FUZxxl 2014年

已修复,现在它可以像Lowjacker的解决方案一样从文件读取(完全)。
Ilmari Karonen 2014年

5

Python,第167个 130 118字节

这是我第一次打高尔夫球,所以去吧!它解释除自身以外的任何程序

改良版:

i=open(raw_input()).read();q='i=open(raw_input()).read();q=%s;i==q%%repr(q)and a;exec(i)\n';i==q%repr(q)and a;exec(i)

如果它自己获得,则它会发出以下消息:

Traceback (most recent call last):
  File "pygolf.py", line 1, in <module>
    i=open(raw_input()).read();q='i=open(raw_input()).read();q=%s;i==q%%repr(q)and a;exec(i)\n';i==q%repr(q)and a;exec(i)
NameError: name 'a' is not defined

我认为此解决方案的运作方式与Ilmari Karonen的运作方式大致相同,基本思路如下:

input = read_some_file()
if input == some_quine()
    barf()
interpret(input)

我使用的奎因就是基于这一点:

(lambda x: x + repr((x,)))('(lambda x: x + repr((x,)))',)

但自那以后,我意识到一个更短的quine是:

q='q=%s;q%%repr(q)';q%repr(q)

如果允许交互式python shell,则可以更短,在这种情况下,您可以执行以下操作:

'%s;_%%repr(_)';_%repr(_)

由于python没有获得命令行args的捷径,因此我使用了raw_input()(它仍然很长,但没有

import sys;sys.argv[1]

用法是:

echo "foobar.py" | python quinterpretter.py

要么

python quinterpretter.py
<type filename and hit enter>

我发现可以使用较短的奎因,但这是我的旧版本(供后代使用):

i=open(raw_input()).read();a if i==(lambda x,y:x+repr((x,y))+y)('i=open(raw_input()).read();a if i==(lambda x,y:x+repr((x,y))+y)', ' else 1;exec(i)\n') else 1;exec(i)

用%r替换%s并删除代表。%r表示原始,基本上是同一件事。
Loovjo 2015年

4

我无法使用Java脚本精确地从文件中读取(好的,我可以使用HTML5 FileReader东西,但这使事情变得比我需要的复杂得多)。因此,该函数接受Javascript程序作为字符串并运行它。

这可能不像它可能的那样打高尔夫,但是无论如何这里是这样:

Javascript,252

function c(p){q='\"';s='\\';a="function c(p){q='\"';s='\\';a=%;a=a.slice(0,17)+s+a.slice(17,24)+a[23]+a.slice(24);a=q+a.replace('%',q+a+q)+q;alert(a);}";a=a.slice(0,17)+s+a.slice(17,24)+a[23]+a.slice(24);a=a.replace('%',q+a+q);alert(a);if(p!=a)eval(p)}

让我知道是否有人知道用Javascript形成木马的更好技术。


1
我根据您的代码和我的Perl解决方案在下面发布了一个135个字符的JS解决方案。+1启发!
Ilmari Karonen 2011年

2
read p<p;read c<c;[ "$p" = "$c" ]||. ./c

sh(POSIX shell)的45个字符。要运行的代码必须在文件中./c

解释器本身的代码必须在file中./p,所以我想我有点被骗,尽管挑战似乎并没有禁止它。还是这会使我的“语言”失去资格成为“图灵完整的编程语言”?

使用通常是外部可执行文件的工具,但理论上可以将其内置到shell中,可以缩短代码:

cmp -s p c||. ./c

那是18个字符,该-s位只是禁止显示一行,否则该行将始终为有效(非自已)程序打印。

然后,您始终可以构建一种外壳语言的版本,以更简洁的语法完成上述操作。

然后,当输入包含单个“。”时,您始终可以构建一个程序。-或该死的空字符串-以正常代码的形式评估另一个文件的内容,并将其称为编程语言。因此,空字符串将是您使用所构建的语言来应对挑战的解决方案。实际上,这里是这种语言的解释器:

read code; if [ "$code" ]; then eval "$code"; else . ./othercode; fi

使用以上脚本解释的语言,解决方案是空字符串。而且代码位置不再需要硬编码。

问题?


2
挑战确实表明“您的程序一定不能读取它自己的源代码”。
Ilmari Karonen 2011年

达尼特,那是浪费了一些时间。我什至看到它甚至说您不得使用将要忽略的功能。这将违反空字符串功能。再一次,如果编译器/解释器的代码本身在新语言中导致不同的行为,则解释器必须省略/更改功能。无论如何,我在写谬论时很有趣。
2011年

@TaylanUB好吧,实际上您必须解释除解释程序本身之外的所有有效$ lang程序。
FUZxxl 2011年

@FUZxxl是的,否则“ sh +空字符串”语言等效于sh(如果代码不是空字符串),并且其中编写的空字符串程序也会解释sh代码(必须放入./othercode),并执行如果代码为空字符串,则为空。我不应该将该文件命名为./othercode,这会产生误导;这只是用空字符串语言编写的解释器将解释的代码。
TaylanUB '12

2

JavaScript,135个字符

function c(p){q='function c(p){q=%27Q%27;p!=unescape(q).replace(/Q/,q)?eval(p):alert()}';p!=unescape(q).replace(/Q/,q)?eval(p):alert()}

Peter Olson的JavaScript解决方案启发了我尝试将Perl解决方案移植到JS。像他的解决方案一样,此代码定义了一个c接受字符串的函数,如果该函数与上面的代码不相等,则对其进行评估。

我花了一段时间才想出一种很好的方法来解决JavaScript中缺少平衡的字符串定界符的问题,直到我发现事后才发现明显的解决方案:unescape()

方便地,我的代码不包含任何反斜杠或双引号,因此可以安全地将其存储在双引号字符串中。这使测试变得容易:

e = "function c(p){q='function c(p){q=%27Q%27;p!=unescape(q).replace(/Q/,q)?eval(p):alert()}';p!=unescape(q).replace(/Q/,q)?eval(p):alert()}"
h = "alert('Hello, world!')"

eval(e)  // defines the function c()

c(h)     // evaluates h
c(e)     // does not evaluate e, alerts "undefined" instead

您可以替换alert()0以使其不执行任何操作,而不是发出警报undefined并保存13个字符。
彼得·奥尔森

@PeterOlson:是的,但是该任务确实指出,如果检测到自身,“您的程序应该执行完全无关的操作”。我认为这意味着它应该执行某些操作 -我认为最好是用户可见的操作。此外,我更喜欢这种方式。:)(Ps。Yay,外面在下雪!冬天终于来了!)
Ilmari Karonen 2011年

1
@Ilmari什么也不做与解释Java语言IMHO无关。
FUZxxl 2011年

您可以p=>...代替function c(p)
FireCubez '18

2

常见的Lisp,59岁

#+~ #.(#:a)(defun L(p)(compile-file p))(push :~ *features*)
  • 在新的Lisp REPL中,编译文件(例如sbcl --load
  • 现在您有了一个L可以编译Common Lisp文件的函数
  • 但是,如果调用(L <your file>),则在读取文件时会提示错误。

为什么?

因为这是您第一次将:~关键字推入*features*。现在,您的环境已了解该~功能,并且读取器宏#+在评估~ 功能表达式后将成功并读取以下形式,而不是像第一次那样跳过它。在您的文件中,以下格式为#.(#:a),要求(#:a)读取时求值并将结果值用作读取的代码。而是(#:a)调用与uninterned符号关联的函数#:a。由于#:a是未interinterned,因此它是未绑定到任何函数(即not fboundp)的新鲜符号。错误。


1

方案,48个或51个字符

Scheme是一种具有许多不同实现的语言。尽管实施方式必须符合最新的RnRS,但由于缺乏简约性,最新的工作标准(R6RS)仍然不受欢迎。R7RS很快将作为一种补救措施发布,同时将该语言分为2种。第一种语言功能强大且简约,第二种语言是第一种语言的超集,旨在为实现之间的互操作性提供功能扩展。在此之前,我们依靠SRFI(实现方案请求),它提供(如果是在主机实现中实现或手动实现(在方案中很常见))可移植地完成常见任务。综上所述,第一个代码段(51个字符)虽然保持了可移植性,但仍依赖SRFI-22(UNIX中的执行方案脚本)来访问命令行参数:

(define(main x y)(case y(x => error)(else => load)))

或更可读:

(define (main current-file arg)
  (case arg
    [current-file => error]
    [else => load]))

第二个(48个字符)是一种无文件解释方法,无法自我评估(在空环境中):

(define(e)(write(eval(read)null-environment))(e))

或更可读:

(define (interpret)
  (write (eval (read) null-environment))
  (interpret))

如果您复制解释器,则您的代码将不起作用。
FUZxxl 2011年

1
将其留给计划答案,以便在其散文中包含嵌套的括号。
Cyoce '16

1

Groovy,13个字节

{Eval.me(it)}

这应该解释Groovy的子集。

测试用例:

p={Eval.me(it)}

p'''
    (0..37).each{println"1234567890JIHGFEDCBAKLMNOPQRST!?,.ZYXWVU"[it..it+2]}
'''

p'''
    {Eval.me(it)}
'''

不幸的是,尽管它肯定会发声,但它以一种完全类似于解释器的方式进行,并且需要大量的输入。


您在哪一行阅读要解释的程序?您的代码很有趣,尽管它不是此任务的有效提交。
FUZxxl 2011年

我认为错误是类似于“超出了递归限制”?
Ilmari Karonen 2011年

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.