相互排斥的奎因


27

您的挑战很简单。编写两个互不输出字符的程序。

如果满足以下条件,则两个程序PQ是互斥的。

  1. P输出Q
  2. Q输出P
  3. 没有字符c属于PQ
  4. 每个程序PQ都是适当的
    1. 这会将空的quines和读取自己(或其他)源代码的quines视为invalid

更多规则

  • 这些程序的总长度最短者获胜。也就是说,大小(P)+大小(Q)是您的得分,最低得分获胜。
  • 两种程序使用相同的语言
  • 每个程序可以是完整的程序或功能,而不必相同。
    • 例如,P可以是完整程序,而Q可以是函数。

验证

可以在线进行!此处的代码段可以验证两个程序是否互斥。输入将放在前两个参数中。




3
我假设两个互相读取源代码的程序也被禁止。
朱塞佩

2
我很乐意看到针对此挑战的非伊索朗答案。(我对如何执行此操作有一些想法,但是到目前为止,我还没有看到一种方法。但是,在Forth中这可能是可行的,因为它不区分大小写,并且不非常依赖非字母字符。 )
纳撒尼尔(Nathaniel

1
如果我可以传递相同的参数,则不是传递给程序本身,而是传递给这两个程序的编译器。通常,如果您为它们付费,则允许使用编译器标志,但是对于此挑战,您可能会认为它违反了互斥规则。
BlackCap '18

Answers:


37

> <>得分:41 + 41 = 82

编辑:都包含一个3。固定

'd3*}>a!o-!<<8:5@lI55>@z:5ll55>>q:>|q::|,

"r00gr40g44++bb+0p64++?b6+0.22#eW4s )Z

在线尝试!(交换行以获取其他输出)这次进行验证!

><>这是一种特别难用的语言,因为只有一种输出字符的方法,即命令o。幸运的是,我们可以使用p ut命令放置一个o在我的执行过程中的源代码,就像在一个纯净的世界编程答案。

这需要大量的反复试验。我从两个互斥的程序开始:

'd3*}>N!o-!<<data

"r00gr40g8+X0pN+?Y0.data

每个人都用N变换自身及其数据,第一个相减,第二个相加。然后将其反向输出。关键是每个程序之后的数据是另一个相反的程序,其偏移量为N。(X是程序需要放入的单元格编号,oY是指针循环返回的单元格。?是其中o是PUT) 。

两者都遵循相同的结构,以不同的方式表示。他们在整个代码上运行字符串文字,并将其添加到堆栈中。他们重新创建他们使用的字符串文字命令,并将其放在堆栈的底部。它们遍历堆栈,为每个字符添加/减去N并打印出来。

第一个程序使用'字符串文字,并使用简单的d3*}方法创建值39并将其推入堆栈的底部。第二个"用作具有相同功能的字符串文字。它r整理堆栈,g将字符设置在单元0,0处,然后再次反转堆栈。然后g将值赋值到单元格4,0(g)上,并将其加8以获得o并将其放在X处。

两种程序都使用不同的循环方法。第一个程序使用skip命令(!)在向左移动时仅运行指令的一半,反转方向并运行另一半。第二个命令使用跳转命令(.)向后跳到单元格Y处的循环开始。这两个命令都会运行,直到堆栈上没有更多的项目并且程序错误为止。

我遇到了N的大多数较低值的许多问题,因为移动一个字符会将其转换为对该程序必不可少的另一个字符(因此不能用作另一个程序的数据)或从另一个字符中获取两个字符。两个程序将转换为相同的字符。例如:

  1. ++1 = ,=- -1
  2. .+2 = 0
  3. * = - -3
  4. g+4 = k= o-4

等等

最终我达到了10(a),可以避免这些问题。可能会有一个较短的版本,其中移位相反,第一个程序加N,第二个程序减N。但是,这可能会更糟,因为第一个程序通常位于ASCII范围的低端,因此最好进行减法以避免冲突。


19

Forth(64位little-endian gforth),428 + 637 = 1065字节

s"	:	l	bl	-	;	:	m	l	emit	;	:	s	space	;	:	z	m	m	m	m	s	;	:	p	.	't	'i	'm	'e	z	;	'e	'r	'e	'h	z	:	q	>r	char	l	bl	l	do	dup	@	.	'L	m	s	cell+	loop	r>	.	;	:	n	'e	'p	'y	't	z	;	q	;	's	p	'B	l	p	#tab	p	'p	'u	'd	'Q	char+	z	n	'B	l	p	n":	l	bl	-	;	:	m	l	emit	;	:	s	space	;	:	z	m	m	m	m	s	;	:	p	.	't	'i	'm	'e	z	;	'e	'r	'e	'h	z	:	q	>r	char	l	bl	l	do	dup	@	.	'L	m	s	cell+	loop	r>	.	;	:	n	'e	'p	'y	't	z	;	q	;	's	p	'B	l	p	#tab	p	'p	'u	'd	'Q	char+	z	n	'B	l	p	n
HERE 3245244174817823034 , 7784873317282429705 , 665135765556913417 , 7161128521877883194 , 682868438367668581 , 679209482717038957 , 680053688600562035 , 678116140452874542 , 682868623551327527 , 680649414991612219 , 682868636436227367 , 7136360695317203258 , 7809815063433470312 , 8458896374132993033 , 5487364764302575984 , 7810758020979846409 , 680166068077538156 , 4181938639603318386 , 8081438386390920713 , 8793687458429085449 , 2812844354006760201 , 7784826166316108147 , 676210045490917385 , 681493840106293616 , 7521866046790788135 , 679491013524025953 , 7928991804732031527 , 216 115 EMIT 34 EMIT 9 EMIT 2DUP TYPE 34 EMIT TYPE 

在线尝试!

验证脚本

感谢@Nathaniel提供使用Forth的想法-他在评论中提醒我,Forth不区分大小写。然后是情绪波动-我一直在寻找为什么这种方法行不通的原因,然后一次又一次地解决这些问题。在我的室内训练车旋转的同时,就像一个颠倒的,变形的坐骑旋转器(您只需要抓住车把的一端并将其倾斜一点)即可。

在编写这些程序之前,我草拟了哪个程序可以使用哪些字符。具体来说,第二个程序只能使用大写字母,十进制数字,制表符和逗号。这意味着第一个程序全部为小写字母,但是我使用了一些大写字母作为ASCII值。

由于制表符很笨拙,因此我将在解释中使用空格。

第一个程序采用以下形式:s" code"code- s"启动字符串文字,然后由第二个代码副本处理-标准quine框架。但是,它不会创建自己的源代码,而是创建另一个程序,如下所示:

  • HERE
  • 对于原始字符串中的每8个字节, 64-bit-number-literal ,
  • length-of-the-string
  • 115 EMIT 34 EMIT 9 EMIT 2DUP TYPE 34 EMIT TYPE

这将使用Forth的数据空间。HERE返回指向当前分配的数据空间区域末尾的指针,并向其,附加一个填充有数字的单元格。因此,前三个项目符号点可以看成是使用s"。要结束第二个程序:

  • EMIT 根据给定的ASCII值输出一个字符,因此:
    • 115 EMIT 打印小写 s
    • 34 EMIT 打印引号字符 "
    • 9 EMIT 打印标签
  • 2DUP 复制堆栈中的前两个元素 ( a b -- a b a b ),这是指向字符串的指针和字符串的长度
  • TYPE 打印一个字符串以输出代码的第一个副本
  • 34 EMIT 打印结束语 ",最后
  • TYPE 输出代码的第二个副本

让我们看看第一个程序是如何工作的。在许多情况下,必须避免使用数字,这可以通过使用'xgforth语法扩展来实现字符常量,有时还要减去空格的ASCII值,可以使用bl以下方法获得:

s" ..."      \ the data
: l bl - ;   \ define a word, `l`, that subtracts 32
: m l emit ; \ define a word, `m`, that outputs a character. Because 32 is
             \ subtracted using `l`, lowercase characters are converted to
             \ uppercase, and uppercase characters are converted to some
             \ symbols, which will become useful later
: z m m m m space ; \ `z` outputs four characters using `m`, followed by a
                    \ space. This is very useful because all words used in the
                    \ second program are four characters long
: p . 't 'i 'm 'e z ; \ define a word, `p`, that, given a number, outputs that
                      \ number, followed by a space, `EMIT`, and another space
'e 'r 'e 'h z \ here is where outputting the second program starts - `HERE `
: q \ define a helper word, `q`, that will be called only once. This is done
    \ because loop constructs like do...loop can't be used outside of a word.
  >r \ q is called with the address and the length of the data string. >r saves
     \ the length on the return stack, because we don't need it right now. While
     \ it might seem like this is too complicated to be the best way of doing
     \ this for codegolf, just discaring the length would be done using four
     \ characters - `drop`, which would give you the same bytecount if you could
     \ get the length again in... 0 characters.
  char l \ get a character from after the call to q, which is `;`, with the
         \ ASCII value of $3B, subtract $20 to get $1B, the number of 64-bit
         \ literals necessary to encode the string in the second program.
  bl l \ a roundabout way to get 0
  do   \ iterate from 0 (inclusive) to $1B (exclusive)
    \ on the start of each iteration, the address of the cell we are currently
    \ processing is on the top of the stack.
    dup @ . \ print the value. The address is still on the stack.
    'L m space \ the ASCII value of L is exactly $20 larger than the one of ,
    cell+ \ go to the next cell
  loop
  r> . \ print the length of the string
;
: n 'e 'p 'y 't z ; \ define a word, `n`, that outputs `TYPE`
q ; \ call q, and provide the semicolon for `char` (used to encode the length
    \ of the string in 64-bit words). Changing this to an uppercase U should
    \ make this work on 32-bit systems, but I don't have one handy to check that
's p \ print the code that outputs the lowercase s
'B l p \ likewise, 'B l <=> $42 - $20 <=> $22 <=> the ASCII value of a comma
#tab p \ print the code that outputs a tab
'p 'u 'd 'Q char+ z \ char+ is the best way to add 1 without using any digits.
                    \ it is used here to change the Q to an R, which can't be
                    \ used because of `HERE` in the second program. R has an
                    \ ASCII value exactly $20 larger than the ASCII value of 2,
                    \ so this line outputs the `2DUP`.
n 'B l p n \ output TYPE 34 EMIT TYPE to finish the second program. Note the
           \ that the final `n` introduces a trailing space. Trying to remove
           \ it adds bytes.

要结束此过程,我想说我尝试使用EVALUATE,但是第二个程序变得比上面介绍的两个都更大。无论如何,这里是:

: s s" ; s evaluate"s" : l bl - ; : m l emit ; : d here $b $a - allot c! ; : c here swap dup allot move ; : q bl l do #tab emit dup @ bl l u.r cell+ #tab emit 'L m loop ; here bl 'B l 's bl 's bl 'Z l d d d d d d d -rot c bl 'B l 's 'B l d d d d s c 'B l d c 'e 'r 'e 'h m m m m 'A q #tab emit 'e 'p 'y 't m m m m"; s evaluate

如果您设法解决这个问题而无法超越我的s" ..."...方法,请继续将其发布为您自己的答案。


1
大!我很高兴我的评论引发了这个解决方案!
纳撒尼尔'18

16

Perl,(311 + 630 = 941字节) 190 + 198 = 388字节

这两个程序都将打印到标准输出。

第一个perl程序主要包含可打印的ASCII字符和换行符,并以一个换行符结尾,但是两个字母ÿ表示非ASCII字节\ xFF:

@f='^"ÿ"x92;@f=(@f,chr)for 115,97,121,36,126,191,153,194,216,113;print@f[1..5,5,10,5..9,0,9,0,5]'^"ÿ"x92;@f=(@f,chr)for 115,97,121,36,126,191,153,194,216,113;print@f[1..5,5,10,5..9,0,9,0,5]

第二个主要包含非ASCII字节,包括一些高级控制字符,在本文中被星号替换,根本没有换行符:

say$~~q~¿*ÂØ¡Ý*Ý*ÆÍÄ¿*Â׿*Ó***Ö***ßÎÎÊÓÆÈÓÎÍÎÓÌÉÓÎÍÉÓÎÆÎÓÎÊÌÓÎÆËÓÍÎÉÓÎÎÌÄ*****¿*¤ÎÑÑÊÓÊÓÎÏÓÊÑÑÆÓÏÓÆÓÏÓʢءÝ*Ý*ÆÍÄ¿*Â׿*Ó***Ö***ßÎÎÊÓÆÈÓÎÍÎÓÌÉÓÎÍÉÓÎÆÎÓÎÊÌÓÎÆËÓÍÎÉÓÎÎÌÄ*****¿*¤ÎÑÑÊÓÊÓÎÏÓÊÑÑÆÓÏÓÆÓÏÓÊ¢~

第一个程序的十六进制转储xxd为:

00000000: 4066 3d27 5e22 ff22 7839 323b 4066 3d28  @f='^"."x92;@f=(
00000010: 4066 2c63 6872 2966 6f72 2031 3135 2c39  @f,chr)for 115,9
00000020: 372c 3132 312c 3336 2c31 3236 2c31 3931  7,121,36,126,191
00000030: 2c31 3533 2c31 3934 2c32 3136 2c31 3133  ,153,194,216,113
00000040: 3b70 7269 6e74 4066 5b31 2e2e 352c 352c  ;print@f[1..5,5,
00000050: 3130 2c35 2e2e 392c 302c 392c 302c 355d  10,5..9,0,9,0,5]
00000060: 275e 22ff 2278 3932 3b40 663d 2840 662c  '^"."x92;@f=(@f,
00000070: 6368 7229 666f 7220 3131 352c 3937 2c31  chr)for 115,97,1
00000080: 3231 2c33 362c 3132 362c 3139 312c 3135  21,36,126,191,15
00000090: 332c 3139 342c 3231 362c 3131 333b 7072  3,194,216,113;pr
000000a0: 696e 7440 665b 312e 2e35 2c35 2c31 302c  int@f[1..5,5,10,
000000b0: 352e 2e39 2c30 2c39 2c30 2c35 5d0a       5..9,0,9,0,5].

第二个程序的十六进制转储为:

00000000: 7361 7924 7e7e 717e bf99 c2d8 a1dd 00dd  say$~~q~........
00000010: 87c6 cdc4 bf99 c2d7 bf99 d39c 978d d699  ................
00000020: 908d dfce ceca d3c6 c8d3 cecd ced3 ccc9  ................
00000030: d3ce cdc9 d3ce c6ce d3ce cacc d3ce c6cb  ................
00000040: d3cd cec9 d3ce cecc c48f 8d96 918b bf99  ................
00000050: a4ce d1d1 cad3 cad3 cecf d3ca d1d1 c6d3  ................
00000060: cfd3 c6d3 cfd3 caa2 d8a1 dd00 dd87 c6cd  ................
00000070: c4bf 99c2 d7bf 99d3 9c97 8dd6 9990 8ddf  ................
00000080: cece cad3 c6c8 d3ce cdce d3cc c9d3 cecd  ................
00000090: c9d3 cec6 ced3 ceca ccd3 cec6 cbd3 cdce  ................
000000a0: c9d3 cece ccc4 8f8d 9691 8bbf 99a4 ced1  ................
000000b0: d1ca d3ca d3ce cfd3 cad1 d1c6 d3cf d3c6  ................
000000c0: d3cf d3ca a27e                           .....~

在第二个程序中,带引号的字符串(长189个字节,由波浪号分隔)是除最后一个换行符以外的整个第一个程序,仅通过按位补全每个字节进行编码。第二个程序通过对每个字节进行补码来简单地解码字符串,~操作员在perl中执行此操作。程序打印解码后的字符串,后跟换行符(该say方法添加换行符)。

在这种结构中,第二个程序的解码器仅使用六个不同的ASCII字符,因此第一个程序实际上可以是任意的,只要它仅包含ASCII字符并排除这六个字符即可。不使用这五个字符就不难编写任何perl程序。因此,实际的奎因逻辑在第一个程序中。

在第一个程序中,quine逻辑使用一个11字长的字典@f,并组合这些字的输出。第一个单词重复第一个程序的大部分源代码。其余单词是特定的单个字符。例如,单词5是波浪号,它是第二个程序中两个字符串文字的分隔符。方括号中的数字列表是按什么顺序打印单词的方法。这是一个非常普通的用于quines的构造方法,在这种情况下,唯一的变化是第一个字典单词的打印字节已按位补充。


14

Haskell,306 + 624 = 930字节

程序1:带有伪参数并返回字符串的匿名函数。

(\b c()->foldr(\a->map pred)b(show()>>c)`mappend`show(map(map fromEnum)$tail(show c):pure b))"İĴİóđđđÝöÝâÝæÝääē××êääē××İēÀħđĮâħēĕóİóòòĮááħááđéêâéêēááĮÀħ""(\b c()->foldr(\a->map pred)b(show()>>c)`mappend`show(map(map fromEnum)$tail(show c):pure b))"

在线尝试!

程序2:q[[40,...]]最后是一个匿名函数,它带有一个虚拟参数并返回一个字符串。

z~z=[[['@','0'..]!!4..]!!z]
q[x,q]_=z=<<x++q++[34,34]++x
q[[40,92,98,32,99,40,41,45,62,102,111,108,100,114,40,92,97,45,62,109,97,112,32,112,114,101,100,41,98,40,115,104,111,119,40,41,62,62,99,41,96,109,97,112,112,101,110,100,96,115,104,111,119,40,109,97,112,40,109,97,112,32,102,114,111,109,69,110,117,109,41,36,116,97,105,108,40,115,104,111,119,32,99,41,58,112,117,114,101,32,98,41,41,34],[304,308,304,243,273,273,273,221,246,221,226,221,230,221,228,228,275,215,215,234,228,228,275,215,215,304,275,192,295,273,302,226,295,275,277,243,304,243,242,242,302,225,225,295,225,225,273,233,234,226,233,234,275,225,225,302,192,295]]

在线尝试!

字符集1(包括空格):

 "$()-:>E\`abcdefhilmnoprstuw×ÝáâäæéêñòóöđēĕħĮİĴ

字符集2(包括换行符):

!'+,.0123456789<=@[]_qxz~

由于仅集合1包含非ASCII字符,因此它们的UTF-8字节也不相交。

怎么运行的

  • 程序1通常用lambda表达式,空格和括号编写,并免费使用内置的字母数字函数,并以quine数据作为字符串文字结尾。

    • 只需将程序1自身的核心代码用引号引起来,就可以将其转换为字符串文字数据。
      • 为了支持这一点,每个反斜杠后面都跟有ab,这形成了有效的转义序列,可以通过show
      • 另一个小好处是ab并且c是唯一的ASCII码小于100的小写字母,从而在程序2使用的数字编码中节省了一个数字。
    • 通过使用非ASCII Unicode可以使程序2的核心代码的字符串文字编码更加模糊:每个字符的代码点都添加了182,以确保与原始字符没有重叠。
      • 182曾经是128,直到我意识到我可以滥用182长度是程序1的代码的字符串文字长度的两倍以缩短解码的事实。(作为奖励,程序2可以使用换行符。)
  • 程序2通常用顶级函数方程式(最后一个匿名方程式除外),字符文字和十进制数字,列表/范围语法和运算符编写,并以quine数据作为Ints 列表的末尾。

    • 程序1的核心代码被编码为其代码点的列表,并带有最终的双引号。
    • 程序2的核心代码被编码为程序1中使用的字符串文字的代码点列表,但仍向上移动182。

演练,程序1

  • bc分别是程序2和1的字符串文字的值,作为lambda表达式的最终参数给出。()仅是为了满足PPCG规则(程序应定义函数)的伪参数。
  • foldr(\a->map pred)b(show()>>c)通过对该字符串b应用map pred等于show()>>c == c++c或的长度的次数,将该字符串解码为程序2的核心代码182
  • tail(show c)将字符串转换c为程序1的核心代码,并附加最后的双引号。
  • :pure b将其与字符串结合在列表中b
  • map(map fromEnum)$ 将字符串转换为代码点列表。
  • `mappend`show(...) 序列化结果列表列表,最后将其附加到程序2的核心代码中。

演练,程序2

  • 顶层z~z=[[['@','0'..]!!4..]!!z]是将代码点转换回字符的函数(由于并非所有字符toEnum都可用,因此必须编写)。
    • 其代码点参数也称为z。懒惰标记~在该位置无效,但避免使用空格字符。
    • ['@','0'..] 是一个从ASCII代码64开始的后退步进列表范围,然后每步向下跳16。
    • !!4对此应用一个\NUL字符。
    • 将其包装在一个[ ..]范围内可得到所有字符的列表,并进行!!z索引。
    • 角色最终被包裹在一个单例列表中。这允许z使用=<<代替不可用的map和映射列表上的功能<$>
  • 最高层 q[x,q]_=z=<<x++q++[34,34]++x是从quine数据列表构建程序1的功能。
    • x是程序1核心的数据(包括最后的双引号),而内部q是程序2核心的模糊数据。_是另一个伪参数,仅使最终匿名函数成为函数,而不仅仅是字符串。
    • x++q++[34,34]++x 将片段连接起来,包括两个带ASCII码34的双引号。
    • z=<<z通过在串联上进行映射以从代码点转换为字符来构造程序1 。
  • 最后q[[40,...]]是一个匿名函数q,与quine数据结合在一起。

5

果冻128 90 87 86 85 79 16 + 32 = 48个字节

“OṾ⁾ọṙŒs”OṾ⁾ọṙŒs

在线尝试!

79,7806,8318,7885,7769,338,115ỌṘ

在线尝试!

第一个程序执行以下操作:

“OṾ⁾ọṙŒs”OṾ⁾ọṙŒs
“OṾ⁾ọṙŒs”          String literal: 'OṾ⁾ọṙŒs'
         O         ord: [79, 7806, 8318,...]
          Ṿ        Uneval. Returns '79,7806,8318,7885,7769,338,115'
           ⁾ọṙ     Two character string literal: 'ọṙ'
              Œs   Swap case the two char literal: 'ỌṘ'.

剩下的弦79,7806,8318,7885,7769,338,115ỌṘ作为链的两个参数,它们被隐式连接起来并在最后打印出来。

第二个程序计算返回的数字列表的chrOṾ⁾ọṙŒs打印“OṾ⁾ọṙŒs”(带引号)并返回输入,而保留“OṾ⁾ọṙŒs”OṾ⁾ọṙŒs为完整输出。


5

Gol> <>23 + 23 = 46 22 + 22 = 44 20 + 20 = 40字节

"lF{3+|3d*HqlJJJQpp2

在线尝试!

'5ssTMMMotK-g6.6~Io

在线尝试!

在线验证!

他们如何工作

"lF{3+|3d*HqlJJJQpp2

"..."        Push everything to the stack
 lF{3+|      Add 3 to everything on the stack
       3d*   Push 39 `'`
          H  Print everything on the stack (from the top) and halt

'5ssTMMMotK-g6.6~Io

'...'        Push everything to the stack
 5ss         Push 37 (34 `"` + 3)
    T    t   Loop indefinitely...
     MMMo      Decrement 3 times, pop and print
               At the end, `o` tries to print charcode -3, which is fishy (thanks Jo King)
               Program terminates

改编自Jo King的> <>答案。有更多的替代命令可用于输出和重复,因此不需要g或不需要p,并且两个主体变得更短。

另一个主要区别是,我直接在堆栈顶部生成了对手的报价。这样,保留的不变性会稍微容易一些quote + my code + opponent code(reversed and shifted)

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.