无法读取,2199 2145 2134 2104 2087 2084字节
支持k
/ j
以及▲
/ ▼
语法。
按照良好的不可读传统,以下是按比例字体设置的程序,以消除撇号和双引号之间的区别:
'“”“”“”“”“”“”“”“”“”“”“”“”“”“'”“”“”“”“”“”“”“”“”“”“” “”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“” “'”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”'“”“”'“”“”“”“”“”“”“”“”“”“”“”'“”“”“”“”“”“”“”“”“”“” '“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“” “”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“'”“'”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“ '“”“”“”“”“”“”“”“”“”“”“”“”“”“”'“”“”“”“”“”“”“”“”“” “”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“” “”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“” “”“”'“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”'“”'“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“” “”“”'“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“” “”“”'“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“'”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“” “”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“” “'”“'”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“'“”'“”“”“”“”“”“”“”“”“”“”“”“”'“”“”“”“”“”“”“”“”“” “”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“” '“”'“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“” '“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“” “'”“'”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“'“”“'”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“” '“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“” “”“”'“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”'“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“” '“”'“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“” “”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“” “”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“” '“”“'”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”'“”“”“”“”“”“”“”“”“”“” “'”“”“”“”“”“”“”“”“”“”“”“”“”“'”“”“”“”“”“”“”“”“” '“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“” “”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“” “”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“” '“”'“”'“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“” “”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“'”“”“”
这是一个了不起的挑战。感谢您的发表!
说明
为了了解不可读的功能和不可读的功能,可以想象Brainfuck在两个方向上都有无限长的磁带,但是您可以通过取消引用指针来访问任何一个存储单元,而不必每次移动一个存储单元。尽管必须手动完成其他算术运算(包括模),这在此解决方案中非常方便。
这是带有导演注释的伪代码程序:
// Initialize memory pointer. Why 5 will be explained at the very end!
ptr = 5
// FIRST PASS:
// Read all characters from stdin, store them in memory, and also keep track of the
// current line number at each character.
// We need the +1 here so that EOF, which is -1, ends the loop. We increment ptr by 2
// because we use two memory cells for each input character: one contains the actual
// character (which we store here); the other will contain the line number at which the
// character occurs (updated at the end of this loop body).
while ch = (*(ptr += 2) = read) + 1:
// At this point, ch will be one more than the actual value.
// However, the most code-economical way for the following loop is to
// decrement inside the while condition. This way we get one fewer
// iteration than the value of ch. Thus, the +1 comes in handy.
// We are now going to calculate modulo 4 and 5. Why? Because
// the mod 4 and 5 values of the desired input characters are:
//
// ch %5 %4
// ^ 1
// v 2
// k 3
// j 4
// ▲ 0 2
// ▼ 0 0
//
// As you can see, %5 allows us to differentiate all of them except ▲/▼,
// so we use %4 to differentiate between those two.
mod4 = 0 // read Update 2 to find out why mod5 = 0 is missing
while --ch:
mod5 = mod5 ? mod5 + 1 : -4
mod4 = mod4 ? mod4 + 1 : -3
// At the end of this loop, the value of mod5 is ch % 5, except that it
// uses negative numbers: -4 instead of 1, -3 instead of 2, etc. up to 0.
// Similarly, mod4 is ch % 4 with negative numbers.
// How many lines do we need to go up or down?
// We deliberately store a value 1 higher here, which serves two purposes.
// One, as already stated, while loops are shorter in code if the decrement
// happens inside the while condition. Secondly, the number 1 ('""") is
// much shorter than 0 ('""""""""'""").
up = (mod5 ? mod5+1 ? mod5+3 ? 1 : 3 : 2 : mod4 ? 3 : 1)
dn = (mod5 ? mod5+2 ? mod5+4 ? 1 : 3 : 2 : mod4 ? 1 : 3)
// As an aside, here’s the reason I made the modulos negative. The -1 instruction
// is much longer than the +1 instruction. In the above while loop, we only have
// two negative numbers (-3 and -4). If they were positive, then the conditions in
// the above ternaries, such as mod5+3, would have to be mod5-3 etc. instead. There
// are many more of those, so the code would be longer.
// Update the line numbers. The variables updated here are:
// curLine = current line number (initially 0)
// minLine = smallest linenum so far, relative to curLine (always non-positive)
// maxLine = highest linenum so far, relative to curLine (always non-negative)
// This way, we will know the vertical extent of our foray at the end.
while --up:
curLine--
minLine ? minLine++ : no-op
maxLine++
while --dn:
curLine++
minLine--
maxLine ? maxLine-- : no-op
// Store the current line number in memory, but +1 (for a later while loop)
*(ptr + 1) = curLine + 1
// At the end of this, minLine and maxLine are still relative to curLine.
// The real minimum line number is curLine + minLine.
// The real maximum line number is curLine + maxLine.
// The total number of lines to output is maxLine - minLine.
// Calculate the number of lines (into maxLine) and the real minimum
// line number (into curLine) in a single loop. Note that maxLine is
// now off by 1 because it started at 0 and thus the very line in which
// everything began was never counted.
while (++minLine) - 1:
curLine--
maxLine++
// Make all the row numbers in memory positive by adding curLine to all of them.
while (++curLine) - 1:
ptr2 = ptr + 1
while (ptr2 -= 2) - 2: // Why -2? Read until end!
*ptr2++
// Finally, output line by line. At each line, we go through the memory, output the
// characters whose the line number is 0, and decrement that line number. This way,
// characters “come into view” in each line by passing across the line number 0.
while (--maxLine) + 2: // +2 because maxLine is off by 1
ptr3 = 5
while (ptr -= 2) - 5:
print (*((ptr3 += 2) + 1) = *(ptr3 + 1) - 1) ? 32 : *ptr3 // 32 = space
ptr = ptr3 + 2
print 10 // newline
对于程序逻辑来说是如此。现在,我们需要将其转换为“不可读”,并使用其他一些有趣的高尔夫技巧。
变量始终在“不可读”中以数字形式取消引用(例如,a = 1
变为*(1) = 1
)。一些数字文字比其他数字长;最短的是1,然后是2,以此类推。要显示负数有多长,以下是从-1到7的数字:
-1 '""""""""'""""""""'""" 22
0 '""""""""'""" 13
1 '""" 4
2 '""'""" 7
3 '""'""'""" 10
4 '""'""'""'""" 13
5 '""'""'""'""'""" 16
6 '""'""'""'""'""'""" 19
7 '""'""'""'""'""'""'""" 22
显然,我们希望将变量#1分配给代码中最频繁出现的变量。在第一个while循环中,这肯定是mod5
,它出现了10次。但是mod5
在第一个while循环之后我们不再需要了,因此我们可以将相同的内存位置重新分配给以后使用的其他变量。这些是ptr2
和ptr3
。现在,该变量总共被引用21次。(如果您要自己计算发生次数,请记住要计数a++
两次,一次用于获取值,一次用于设置值。)
我们只有一个变量可以重用。在我们计算出模数值之后,ch
就不再需要了。up
并dn
出现相同的次数,所以两者都可以。让我们ch
与合并up
。
总共留下了8个唯一变量。我们可以将变量0分配给7,然后在8处启动内存块(包含字符和行号)。但是!由于代码中7的长度与-1的长度相同,因此我们也可以使用变量-1至6并在7处启动存储块。这样,在代码中对存储块起始位置的每个引用都稍短一些!剩下的工作如下:
-1 dn
0 ← ptr or minLine?
1 mod5, ptr2, ptr3
2 curLine
3 maxLine
4 ← ptr or minLine?
5 ch, up
6 mod4
7... [data block]
现在,这在最顶层解释了初始化:它是5,因为它是7(存储块的开始)减去2(在第一个while条件中的强制性增量)。在最后一个循环中,其他两次出现5的情况相同。
需要注意的是,因为0和4中的代码相同的长度,ptr
并minLine
倒过来,可以任意分配。...或者他们可以吗?
倒数第二个while循环中的神秘2怎么样?这不应该是6吗?我们只想减少数据块中的数字,对吗?一旦达到6,我们就位于数据块之外,应该停止!这将是缓冲区溢出错误错误失败安全漏洞!
好吧,想想如果我们不停下来会发生什么。我们递减变量6和4。变量6为mod4
。这仅在第一个while循环中使用,在这里不再需要,因此不会造成任何危害。那么变量4呢?您如何看待,变量4应该ptr
还是应该minLine
?是的,minLine
现在也不再使用!因此,变量#4是minLine
,我们可以放心地减小它,并且不造成损坏!
更新1!通过实现从2199 Golfed到2145个字节dn
可以也可以用合并mod5
,即使mod5
在该值的计算仍然使用dn
!现在,新的变量分配为:
0 ptr
1 mod5, dn, ptr2, ptr3
2 curLine
3 maxLine
4 minLine
5 ch, up
6 mod4
7... [data block]
更新2!通过意识到从2145字节到2134字节,因为mod5
现在与处于同一变量dn
,并且在while循环中被计数为0,mod5
因此不再需要将其显式初始化为0。
更新3!通过实现两件事从2134字节达到2104字节。首先,尽管“负模”的想法是值得的mod5
,同样的逻辑并不适用于mod4
因为我们从来没有对测试mod4+2
等。因此,转变mod4 ? mod4+1 : -3
到mod4 ? mod4-1 : 3
把我们带到了2110个字节。其次,由于mod4
始终为0或2,我们可以将其初始化mod4
为2而不是0,然后反转两个三元数(mod4 ? 3 : 1
而不是mod4 ? 1 : 3
)。
更新4!通过意识到计算模数值的while循环始终至少运行一次,可以将其从2104字节扩展到2087字节,在这种情况下,“不可读”使您可以在另一个表达式中重用最后一个语句的值。因此,while --ch: [...]; up = (mod5 ? mod5+1 ? [...]
我们现在有了up = ((while --ch: [...]) ? mod5+1 ? [...]
(在while循环内,我们mod4
首先计算,所以这mod5
是最后一条语句)。
更新5!通过实现2087到2084个字节,我意识到不必写常数32
和10
(空格和换行符),而是可以将数字10存储在(现在未使用的)变量#2中(我们称之为ten
)。而不是ptr3 = 5
我们写的ten = (ptr3 = 5) + 5
,然后32
变成ten+22
和print 10
变print ten
。