Answers:
每个答案只给一个小技巧,太多了。
在Brainfuck中学习思考。这与其他一切都非常不同。读写,重写,以及重写大量的Brainfuck程序。语言并没有给您太多帮助,因此灵活有效地使用它所提供的功能非常重要。不要让任何抽象介于您和语言之间-进入那里并努力解决它。
无损流量控制让您感到非常舒适。要退出决策循环,而不是通过将起始单元格复制到其他位置然后在离开循环后再将其复制回零的方法,通常最好将指针移到附近的预先存在的零位。是的,这意味着指针将根据您是否经过循环而位于不同的位置,但这也意味着这些位置附近零和非零的排列可能不同,您可以使用另一个循环使指针位置重新同步。这项技术是进行Brainfuck编程的基础,它的各种形式将不断被证明是有用的。
这和事实,即每一个>
或<
成本意味着内存布局的细节很重要。耐心尝试尽可能多的布局变化。请记住,您的内存布局不必是数据到位置的严格映射。它可以在执行过程中变形。
大规模考虑甚至尝试实现各种不同的算法。最初,确切的算法是什么并不明显。哪怕是最基本的方法都不是最好的方法,也可能与普通语言的最好方法有所不同。
如果要处理大型或可变大小的数据,请查看是否有任何方法可以在本地处理它,而不必跟踪它的大小或其中的数字位置。
相同的数据可以是两个不同的事物。(通常是数字或字符,也是一个非零的位置标记。但请参见random.b,其中位计数器会作为元胞自动机的一个单元格的值加倍。)
相同的代码可以做两种不同的事情,用一种与通用的语言编写代码要容易得多<+<
。警惕这种可能性。实际上,即使在看似编写良好的程序中,您有时也可能会注意到其中的一些小部分可以完全删除,没有添加任何内容,并且偶然地,事情仍然可以完美地运行。
在大多数语言中,您经常使用编译器或解释器来检查程序的行为。Brainfuck语言需要更好的概念控制。如果您需要一个编译器来告诉您程序的功能,则您对程序的掌握程度不够,并且可能需要更多地盯着它-至少如果您希望对程序有足够的了解,擅长高尔夫的类似项目的概念光环。通过练习,您将在尝试运行一个程序之前先生成十二个版本的程序,到那时,您将有95%的把握确保最短的一个程序可以正确运行。
祝好运!很少有人会费心地写出简洁的Brainfuck,但我认为这是该语言可能证明持续关注的唯一方法-一种令人惊讶的晦涩的艺术形式。
这里有一些提示:
该Esolangs'常量页面具有创造特定值的方式最短的一个非常有用的列表。我发现自己每个程序至少浏览两次此页面。
+++[[<+>>++<-]>]
这将磁带设置为3 * n ^ 2格式,看起来像
3 6 12 24 48 96 192 128 0 0'
为什么这个这么重要?
让我们往下看:
0
a
A
。从这一算法开始,我们实际上是在ASCII范围内的每个序列的开始,并且每个序列都有一个计数器,并且容易找到换行符。
一个实际的例子:
打印所有大写和小写字母和数字。
使用算法:
+++[[<+>>++<-]>]<<[-<->]<<<<++[->>+.>+.<<<]<--[>>.+<<-]
没有:
+++++++++++++[->+++++++>++>+++++>++++>+<<<<<]>+++++>[-<+.>>.+<]>>---->---[-<.+>]
在第二个示例中,我们花费了大部分字节来初始化磁带。其中的一些抵消了第一个示例中的额外动作,但是此方法显然具有优势。
同样,还有一些其他有趣的算法:
3 * 2 ^ n + 1:
+++[[<+>>++<-]+>]
Tape: 4 7 13 25 49 65 197 129 1 0'
这会将值偏移1,这完成了一些事情。它使12成为回车符,使64成为大写字母的实际开头,而使24接近26。
2 ^ n:
+[[<+>>++<-]>]
Tape: 1 2 4 8 16 32 64 128
因为64代表大写字母,所以32是ASCII空格,并且128可以用作26的计数器(130/5 = 26)。在某些不需要数字和小写字母的情况下,这可以节省字节。
+[[-<+>>+>+<<]>]
)或处理较大/负数。不利的一面是,某些通用方法(例如[-]
和[->+<]
不能依靠),以防万一该数为负数。在任何时候,您都应该对指针与其周围的数据有关的位置提出意见,并确保您知道每个单元格的可能值范围。当您在循环之前分割指针时,这一点尤其重要,因为您希望随后将这两种可能性重新结合在一起。
在任何时候,我的代码都会在其他每行上显示如下注释:
*0 *dat a_1 ? 0' !0 0*
or
*0 *dat 0' ap1 0 !0 0*
一些额外的建议是为符号赋予特殊含义。在上述例子中,'
是其中指针是,*
在该方向上的装置的重复,?
是指具有未知值的单元格,!0
表示一个非零细胞,_
为替代-
和p
为替代+
。or
这意味着磁带可能看起来像任何一种表示形式,都需要这样处理。
您的符号方案不必与我的符号方案相同(它有一些缺陷),而必须保持一致。这在调试时也非常有用,因为您可以在那之前运行它,并将实际的磁带与应有的磁带进行比较,这可以指出代码中的潜在缺陷。
在这个答案中,我将多次引用磁带上的特定单元。哪个单元格无关紧要,但是整个答案中的单元格相同。出于本文的目的,我将其称为“ Todd”。
尝试将单元格设置为恒定值时,有时需要立即完成操作,这是值得的。例如,假设您希望Todd包含30。稍后在代码中(这可能会修改Todd的值,但从不读取它),您回到Todd。如果Todd的值为0,则程序退出。否则,将永远打印Todd的值。
根据esolangs.org上关于Brainfuck常数的页面(可能本身就是小费的主题!),获得30的最短方法是>+[--[<]>>+<-]>+
。该行首>
仅可确保指针左侧的任何内容都不会被修改,但是在这种情况下,我们将假定我们对此并不在意并将其删除。使用该代码,您的代码将如下所示:
+[--[<]>>+<-]>+(MISC. CODE)(GO TO TODD)[.]
您可以想到以下第一段代码:
(SET TODD TO 30)(MISC. CODE)(GO TO TODD)[.]
但是请记住该块中的最后两个字符:>+
。以这种方式思考同样有效:
(SET TODD TO 29)(GO TO TODD)(ADD 1 TO TODD)(MISC. CODE)(GO TO TODD)[.]
注意你(GO TO TODD)
两次!您可以这样编写代码:
(SET TODD TO 29)(MISC. CODE)(GO TO TODD)(ADD 1 TO TODD)[.]
+[--[<]>>+<-](MISC. CODE)(GO TO TODD)+[.]
假设(GO TO TODD)
之前要占用的字节数相同,那么少移动==再少一个字节!有时候,您的起始位置发生了改变,这一事实确实会带走该利益,但并非总是如此。
没有输入的挑战的小技巧。如果需要快速清除单元格,可以使用,
代替[-]
,因为大多数解释器(包括TIO.run one)会将单元格内容设置为EOF表示为零。这使得程序有些不可移植,但是谁会在代码高尔夫中关心它呢?