我今天制作了我的第一个Brainfuck程序,它打印数字0-255和相应的字符。

我想知道我是否可以改善自己的程序,因为我经常重复自己(例如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_doneall_done重用同一单元格,这会影响可读性。

评论


\ $ \ 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

#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并增加下一个数字(从0910)。

可以将相同的原理应用于结束条件,将值初始化为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