我想知道我是否可以改善自己的程序,因为我经常重复自己(例如3 x复制并粘贴“比较器”功能):
max == 255
LF == 10
space == 32
'0' == 48
'9' == 57
':' == 58
Memory: counter
':' space LF
char max&1 cmp1 0 0
num1 '9'&1 cmp2 0 0
num2 '9'&1 cmp3 0 0
num3 '9'&1 cmp4 0 0
+++++ +++
[
> +++++ ++
> ++++
> +
>
++++
[
> +++++ +++
< -
]
>>>>
> +++++ +
> +++++ ++
>>>
> +++++ +
> +++++ ++
>>>
> +++++ +
> +++++ ++
<<<<< <<<<< <<<<< <<<<< -
]
> ++
>> ++
>>
>>>>> ++
>>>>> ++
>>>>> ++
<<<<< <<<<< <<<<< <<<<<
Memory: 0
58 32 10
0 256 0 0 0
48 57 0 0 0
48 57 0 0 0
48 57 0 0 0
>>>>> >
+
[
-
>>> .
>>>>> .
>>>>> .
<<<<< <<<<< <<<<< <<<
.
> .
>> .
< .
Number increasing logic
>>>>> >>>>> >>>>> >
+
Comparer: num1 num2 out 0 0
[
-
>-
>>>+
<<<<
]
>>+ set equal flag
< if num1 != num2
[
>- clear equal flag
]
> if num1 == num2
[
>
]
> go to last and put numbers back
[
<<<+
<+
>>>>-
]
<<<< reset pointer
>>
[
<< ----- -----
<<<<< +
>>>>> >> -
]
<<
<<<<<
Comparer: num1 num2 out 0 0
[
-
>-
>>>+
<<<<
]
>>+ set equal flag
< if num1 != num2
[
>- clear equal flag
]
> if num1 == num2
[
>
]
> go to last and put numbers back
[
<<<+
<+
>>>>-
]
<<<< reset pointer
>>
[
<< ----- -----
<<<<< +
>>>>> >> -
]
<<
<<<<< <<<<<
+
Comparer: num1 num2 out 0 0
[
-
>-
>>>+
<<<<
]
>>+ set equal flag
< if num1 != num2
[
>- clear equal flag
]
> if num1 == num2
[
>
]
> go to last and put numbers back
[
<<<+
<+
>>>>-
]
<<<< reset pointer
>>
Inverter: in/out 0 0
[
> +
< -
]
+
>
[
< -
> -
]
<
]
#1 楼
恭喜你按照Brainfuck标准,这是非常易读的代码。我能够遵循它。通过将
max
单元初始化为256,您已经使代码可移植,也可以在非包装的Brainfuck解释器上使用。如果您只是想让它在一个包装好的Brainfuck解释器上工作(即以256为模的解释器),则可以将单元格设置为0。值,但是事实证明,当机器以256为模时,255次循环比256次循环要容易得多。您已经有了一个比较例程,因此再次使用它很容易。 (尽管最好不要复制和粘贴代码,有关更多信息,请参见下文。)虽然可能有更简单的方法将数字打印为十进制字符串,但您还是要求优化可读性,所以我选择保持与原始策略接近。
可读性的关键是添加策略性注释。在Brainfuck中,将忽略除八个命令(
><+-.,[]
)以外的任何其他字符。您还可以添加格式为[-][Any comment text goes here]
的注释,其中有两个警告:只要方括号成对出现,任何字符都可以出现在里面。您的注释的主要目的应该是弄清楚:每个代码块的目的是什么。
,我认为,只要您能理解全局,就能遵循每个微操作并不重要。 />
什么是内存布局,以及为什么要这样布局?
我为所有重要的单元格赋予了“变量名”。在下面的代码中,关于我所谓的“框架”或连续单元的组也有很长的解释性注释。
该单元在程序中的任何位置都是当前单元。
如果单元格包含错误的值,这是一个相对较小的错误。但是,如果指针不在预期的位置,则程序将以引人注目的方式崩溃。因此,我开发了一种表示法,用于在整个代码中关键位置的括号中标记当前指针的位置。在您的代码中。处理头脑混乱的方法是让例行程序逐帧地沿着单元格爬行,就像在高速公路上工作的建筑工人一样。只需放置适当的路标,让它知道在哪里停止爬网。
但是,您可能必须为
not_all_done
和all_done
重用同一单元格,这会影响可读性。#2 楼
分析我用Brainduck分析了您的代码,发现了一些改进。首先,让我们看看Brainduck所说的。我不会在这里发布完整的输出,只是我发现最有趣的部分。
首先,在运行时您的程序执行大约1.47百万条命令。
循环时-49到58
我的分析显示出while循环执行次数的有趣模式。我发现您的某些while循环具有明确的顺序: 51,以此类推,直到58,然后又下降到49次,然后是50,然后是51,依此类推... while循环是对
if (a == b)
的比较。正在比较if (a == b)
,但是在Brainfuck中这是一个很长的操作,因为您需要来回移动内存,所以它的运行时复杂度为\ $ O(n)\ $,其中\ $ n \ $是a
的值。模数10
还有一个有趣的while循环:
[49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, (repeat 49 - 58 a couple of times...), 49, 50, 51, 52, 53, 54]
这是您决定是否还是不要增加磁带中的下一个数字,例如,当最后一个数字从
9
更改为0
时,是时候将第二个数字从x
更改为x + 1
。 Brainfuck倒数到零而不是倒数\ $ x \ $。 不是将计数从49一直计数到58,而是将
x
一直计数到58,而是将附近的存储单元初始化为10,然后递减为零,当它达到零时,就该将其重置为10并增加下一个数字(从09
到10
)。可以将相同的原理应用于结束条件,将值初始化为256,然后始终与256比较。而是初始化256并将其减少1,直到达到零为止,然后是时候结束循环了。
其他小变化
您的代码包含一些
>><<
序列没有变化,这使得可以简化。我建议您简化这些操作,而不是改进有关当前磁带位置的注释。结果代码这里的变化是,我从不进行比较,而是总是将某些值减小到0。这适用于您的
if (a == b)
检查和if (x == 58)
检查。对于8位Brainfuck解释器
200_success提到您的代码也支持8位Brainfuck解释器。您的
if (x == 256)
检查。当我在此处删除该检查时,以上代码不适用于8位自动包装的BF解释器。但是,这可以通过不具有一个256值来解决,而可以拥有2个值128。替换内存初始化:
[1, 1, 1, 1, 1, 1, 1, 1, 1, 0, (1 x 9 times), 0, (1 x 9 times), 0, (1 x 9 times), 0, (1 x 6 times)]
with
max == 255
LF == 10
space == 32
'0' == 48
'9' == 57
':' == 58
Memory: counter
':' space LF
char max&1 cmp1 0 0
num1 '9'&1 cmp2 0 0
num2 '9'&1 cmp3 0 0
num3 '9'&1 cmp4 0 0
+++++ +++
[
> +++++ ++
> ++++
> +
>
++++
[
> +++++ +++
< -
]
>>>>
> +++++ +
> +
>>>
> +++++ +
> +
>>>
> +++++ +
> +
<<<<< <<<<< <<<<< <<<<< -
]
> ++
>> ++
>>
>>>>> ++
>>>>> ++
>>>>> ++
<<<<< <<<<< <<<<<
Memory: 0
58 32 10
0 (Current Ascii) 256 (Countdown) 0 0 0
48 10 0 0 0
48 10 0 0 0
48 10 0 0 0
positioned at 256 countdown
[
->
>>> .
>>>>> .
>>>>> .
<<<<< <<<<< <<<<< <<<
.
> .
>> .+
< .
Number increasing logic
>>>>> >>>>> >>>>> >
+
>-
>+ set equal flag
< if num1 != 0
[
>- clear equal flag
]
> if num1 == 0
[
Reset this counter to 10 and decrease digit to 0 again
<+++++ +++++
<----- -----
Increase the next number by one
<<<<< +
> -
Reset the equal flag
>>>> >> - >
]
<<[<] positioned at the digit
<<<< goto next
>>+ set equal flag
< if num1 != 0
[
>- clear equal flag
]
> if num1 == 0
[
Reset this counter to 10 and decrease digit to 0 again
<+++++ +++++
<----- -----
Increase the next number by one
<<<<< +
> -
Reset the equal flag
>>>> >> - >
]
<<[<] positioned at the digit
<<<<< <<<
]
这将初始化2个值为128的单元格,而不是一个值为256的单元格。幸运的是,我的代码未使用256值之后的单元格,因此这是一个简单的更改。
循环检查:
替换256循环的开始:
++++
[
> +++++ +++
< -
]
with
++++
[
> ++++
> ++++
<< -
]
,并将最后一行的
if (x == 256)
替换为]
。一旦完成了最里面的循环,它将检查下一个单元格上的值,如果有一个非零值,它将在之前将其移至该单元格,然后将其用于循环并继续减小直到达到零为止这将使您的循环执行
]>[-<+>]<]
次,在这种情况下,x和y均为128,总共为256。当然,可以将这些值调整为x + y
,以便将255 + 1
移至y
的位置为执行速度更快。结果性能
分析表明,此代码在支持8位解释器的情况下,在运行时仅执行30 934次操作,约占2%在您的147万中因此,该程序将比您的原始程序快50倍。
#3 楼
我认为这是一种更有效的方法(至少对于可打印的ASCII字符而言):++++[->++++++++<] Use cell 0 for loop and cell 1 for ASCII codes
++++[->>++++++++<<]>>> Use cell 2 for the space
+++>++> Use cells 3 and 4 for the number equivalent
++++++++<<<<< Use cell 5 for incrementing the number equivalent
++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++ Get 95 for the loop
+++++++++++++++
[>.+>.>.>.+>-[----------<<->>>]<++++++++++<<+ Output ASCII & space & equivalent ASCII code
<<<] Go back to cell 0
告诉我这是如何工作的。我不确定您为什么需要该比较功能。另外,请注意,我确实使用单元格6,但只是为了转义循环(因为它的值为0)。
固定代码与您的问题相同的原始格式:
++++++++[>+++++++>++++>+>++++[>++++++++<-]>>>>>++++++>+++++++>>>>++++++>+++++++>>>>++++++>+++++++<<<<<<<<<<<<<<<<<<<<-]>++>>++>>>>>>>++>>>>>++>>>>>++<<<<<<<<<<<<<<<<<<<<>>>>>>+[->>>.>>>>>.>>>>>.<<<<<<<<<<<<<<<<<<.>.>>.<.>>>>>>>>>>>>>>>>+[->->>>+<<<<]>>+<[>-]>[>]>[<<<+<+>>>>-]<<<<>>[<<----------<<<<<+>>>>>>>-]<<<<<<<[->->>>+<<<<]>>+<[>-]>[>]>[<<<+<+>>>>-]<<<<>>[<<----------<<<<<+>>>>>>>-]<<<<<<<<<<<<+[->->>>+<<<<]>>+<[>-]>[>]>[<<<+<+>>>>-]<<<<>>[>+<-]+>[<->-]<]
评论
\ $ \ begingroup \ $
这里有一些好主意,但这实际上不起作用。例如对于空格字符,您打印ASCII字节0x03(ETX)和0x02(STX),而不是0x33(3)和0x32(2)。
\ $ \ endgroup \ $
– 200_success
2014年1月15日,0:25
\ $ \ begingroup \ $
该代码中的一个好主意是保持一个计数器使单位数字递增10倍,而不是与最大值进行比较。比较将需要更多操作。但是,它与原始版本相去甚远。当指针移至单元格-1时,它会崩溃(因为在第8行中,内括号<<和>>>不匹配)。我看起来像个拙劣的“ if(x!= 0)”。由于BF仅提供“ while”,要实现“ if”,您必须将x复制到两个临时单元格,其中一个在执行主体时将被清零,而另一个需要将x恢复到其原始值。
\ $ \ endgroup \ $
– 200_success
2014年1月15日上午11:02
\ $ \ begingroup \ $
是的,对这个空间感到抱歉...没有意识到那件事。
\ $ \ endgroup \ $
– Timtech
2014年1月15日,11:51
\ $ \ begingroup \ $
您在Rev 3中引入的固定代码有效。但是,这是Code Review,而不是Programming Puzzles&Code Golf。请解释您的答案:OP所要求的方面,它的工作原理以及对原始问题代码的改进。
\ $ \ endgroup \ $
– 200_success
2014年1月16日20:49
\ $ \ begingroup \ $
这里的第一个代码块只会导致乱码,它会显示`! “#$%&'`。您正在使用的单元格相加且没有意义。为什么要在循环中使用95?该循环的目的是什么?为什么单元格1也包含值32与单元格2一样吗?另外,您显示的“固定代码”不过是原始问题中代码的最小化版本,其中所有注释都被删除了。
\ $ \ endgroup \ $
–西蒙·福斯伯格
2015年10月17日10:56
#4 楼
这似乎很复杂。我在您的代码中注意到的第一件事是许多“ <<<< >>”命令,这些命令可以缩写为“ <<”。它只能在包装8位解释器时运行,但更容易理解。评论
\ $ \ begingroup \ $
如果以此替换数字打印例程,它还将打印前导零:>>>>>>>>>>>>-<<<<<<<< ++ [[-] <[ -> + <[-> + <[-> + <[-> + <[-> + <[-> + <[-> + <[-> + <[-> + <[-> [-]] >> +> + <<<<]]]]]]]]] <] >> [>] ++++++ [-<++++++++++] >> +] <<< [。[-] <<<]
\ $ \ endgroup \ $
–多里安(Dorian)
18年7月4日在9:26
评论
\ $ \ begingroup \ $
我很惊讶有这种语言的专家。或“可读性”的概念在其中有所含义。人类的思维可以做的令人惊奇。
\ $ \ endgroup \ $
–韦恩·康拉德
2014年1月15日19:54
\ $ \ begingroup \ $
@WayneConrad:不,我认为。一旦意识到只将指针移动到一个大的已分配内存块中(用C术语:++ pointer; --pointer; ++ * pointer;-* pointer),就很容易理解了。或者,如果您更喜欢这个比喻,可以想象Brainfuck是磁带的一种语言。
\ $ \ endgroup \ $
– Bobby
2014年1月16日上午8:12
\ $ \ begingroup \ $
@WayneConrad也许找到了没有生成器的最长的程序-49-6-dev.net/ftotwen.htm
\ $ \ endgroup \ $
– Timtech
2014年1月16日15:36
\ $ \ begingroup \ $
您说它被要求进行优化以提高可读性,但是我看到的是被要求进行优化以避免代码中的重复。
\ $ \ endgroup \ $
–西蒙·福斯伯格
15年10月17日在11:17
\ $ \ begingroup \ $
@SimonForsberg在删除的注释中提到了可读性(尽管不是OP所为)。
\ $ \ endgroup \ $
– 200_success
2015年10月17日15:37