6
社区成员




深奥的编程语言的目标是挑战常见的假设,源代码是一个扁平的逐行文本文件是其中最基本的。
Befunge 源代码是一个二维字符数组。让我们看看它长什么样!
有趣的是,与我们在上一集中尝试过的 Ada 不同,Befunge 在 OSX 上完全受支持,您只需要安装brew install befunge93
它的bef
解释器即可。
这是 Befunge 中的无限循环:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>>v
^<
</code></span></span>
这里发生了什么?
>
, <
, v
, ^
- 它早于 Unicode)告诉它改变方向令人惊讶的是,Befunge 带有一个内置的可视化调试器。如果您运行此代码,bef -d loop.befunge
您可以在屏幕上看到网格,突出显示在循环中四处移动。
Befunge 没有字符串,只有整数。我们将进入 Hello, World!,但最好先从一些数学开始。
这是一个打印的程序12
:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>48+.@
</code></span></span>
这里发生了什么:
4
像或 之类的单个数字8
是将数字压入堆栈的指令 - 可以直接获取 0 到 9 范围之外的数字+
将顶部的两个数字添加到堆栈并将结果压入那里 - 您可能会猜到什么*
, /
, -
, 和%
做什么.
将堆栈顶部打印为数字@
退出程序 - 如果它没有命中@
,程序将绕过屏幕并且永不停止(它不会转到下一行,它会再次转到同一行的开头)让我们实际进行一些二维编程。这是一个掷 6 面骰子的程序:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>> v
v < > v
3 6
v2?<?>?5v
1 4
> > > >.@
</code></span></span>
?
以随机方向发送程序-它可以向左走1-3,向右走4-6,顶部或底部-顶部显然会被击中v
并反弹回来,但是底部方向呢?事实证明,它会一直继续下去,直到到达程序的末尾,然后环绕,从同一列的屏幕顶部继续。顶部或底部将最终重新滚动?
。如果它向左走,它将朝随机方向走:
<
并弹回?
4-6 的右格是完全对称的。
一旦它到达底行,程序打印数字并.
退出@
。
顺便说一句,来自自制软件的 Befunge 解释器使用当前时间作为随机种子,所以如果你在同一整秒内多次运行它,你会得到相同的结果。
这个程序显然可以在很多方面进行“优化”。
打印 Hello, World! 有很多有趣的方法!
Befunge,
命令将栈顶数字作为 ASCII 值打印出来。我们不能直接在源码里放101
for ,只是等等。e
101
9*9 + 9*2 + 2
因此,让我们做一些非常简单的事情——每一行将计算“Hello, World!\n”的一个字符的 ASCII 值,然后下一行将返回到开头。几乎错过了 2D 编程的全部要点,但我们需要从某个地方开始:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>98* ,v
v <
>92+9*2+,v
v <
>93+9* ,v
v <
>93+9* ,v
v <
>93+9*3+,v
v <
>94*8+ ,v
v <
>93*5+ ,v
v <
>99*6+ ,v
v <
>93+9*3+,v
v <
>93+9*6+,v
v <
>93+9* ,v
v <
>92+9*1+,v
v <
>93*6+ ,v
v <
>91+ ,@
</code></span></span>
而且它不起作用!原来我们使用的原始 Befunge-93 将程序大小限制为 80x25。这是后来在 Befunge-98 中删除的限制。
好吧,让我们加倍空间。
让我们每行放两个:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>98* , 92+9*2+,v
v <
>93+9* , 93+9* ,v
v <
>93+9*3+, 94*8+ ,v
v <
>93*5+ , 99*6+ ,v
v <
>93+9*3+, 93+9*6+,v
v <
>93+9* , 92+9*1+,v
v <
>93*6+ , 91+ ,@
</code></span></span>
它有效!
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>$ bef hello.befunge
Befunge-93 Interpreter/Debugger v2.25
Hello, World!
</code></span></span>
让我们做一个更好的。首先,大多数 ASCII 码之间的距离很近,所以我们不需要从 0 一路计算。
最有用的命令是:
复制堆栈顶部的任何内容,这样我们就可以做到。
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>98* :, 93*+2+ :,v
v <
>7+ :, :,v
v <
>3+ :, 97*-4- :,v
v <
>9-3- :, 96*+1+ :,v
v <
>83*+ :, 3+ :,v
v <
>6- :, 8- :,v
v <
>93*6+ , 91+ ,@
</code></span></span>
除了最后两个字符 ( !\n
) 之外,我用先前数字的重复和差异替换了每个计算。对于小写字符,这是相当不错的。当我们从小写字母改为标点符号或大写字母时,我们实际上并没有节省太多。
好吧,让我们停止退货,好好利用每一行。奇数行将从左到右,偶数行将从右到左。削减的线数是一半。它还破坏了我们迄今为止的任何可读性,但对于 Befunge 这实际上是一件好事。
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>98* :, 93*+2+ :,v
v,: ,: +7<
>3+ :, 97*-4- :,v
v,: +1+*69 ,: -3-9<
>83*+ :, 3+ :,v
v,: -8 ,: -6<
>93*6+ , 91+ ,@
</code></span></span>
当然,您可以将说明排列成螺旋形、之字形、某些徽标或任何您想要的形式。箭头使它非常灵活。
还有一种“无聊”的方式来做“Hello, World!”。作为对所有可能的字符串需求的让步,Befunge 具有 ASCII 模式。"
命令进入 ASCII 模式,对于它遇到的每个字符,它只是将其值压入堆栈,直到遇到下一个"
。我们需要做的唯一两件特别的事情是计算新行的 10,并将所有内容倒过来。
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>55+"!dlroW ,olleH",,,,,,,,,,,,,,@
</code></span></span>
我将跳过斐波那契数列。Befunge-93 有一些严重的限制——它可以在堆栈上保存 32 位整数,但它只能对堆栈的顶部两个元素进行操作,可选择使用命令将顶部与第二个顶部交换\
。
它具有某种“内存”,p
可以g
放入和获取程序的任何单元格,但它只存储 ASCII 值 -128 到 +127。
我们需要 3 个变量以正常方式进行斐波那契计算。如果我们可以在内存中存储常规大小的整数,或者访问更深一点的堆栈,这些都可以用于合理的斐波那契程序。
我什至在网上查过 Befunge 中的一些 Fibonacci 程序,我能找到的最好的程序是将我们需要跟踪的两个数字都编码为512*a+b
,然后从本质上提取一个或另一个num%512
和num/512
。显然,它必须在数字达到 512 之前停止。
为了清楚起见,这里对其进行了一些编辑:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>1.01>:888***++\1+:67+`#@_\:888**%\888**/\:.v
^ <
</code></span></span>
它使用的额外命令是 greater-than `
、 skip next instruction#
和 "horizontal if" ,一旦达到限制,_
它将向左转并命中跳过的指令。@
在开始 FizzBuzz 之前,让我们做一些更简单的事情,打印 1 到 100 的所有数字。
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>0v
v< <
@
>1+:554**`|
v <
>:.55+, ^
</code></span></span>
第一行只是压入 0 并进入循环。程序的其余部分是逆时针的文字循环。在循环中,我们用 增加数字1+
,用 来检查它是否已经大于 100 :554**\`
,然后用 vertical 如果|
我们将程序向上发送到它的出口@
,或者向下发送以继续循环。
循环的底行用 打印数字:.
,然后用 打印换行符55+,
。
我想这都应该是可以理解的。
现在我们知道如何循环,如何打印数字和字符串,并且我们知道这%
是模运算,我们可以轻松地执行 FizzBuzz。
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>0v
v< <
@
>1+:554**`|
v <
>"zzuBzziF",,,,,,,,v
>:53*%!|
v <
>"zzuB",,,,v
>:5%!|
v <
>"zziF",,,,v
>:3%!|
v <
:
.
> > > 55+,^
</code></span></span>
它可能看起来很复杂,但它有一些我们已经知道的不同部分。
该程序有一个大循环。在循环期间,会发生以下事情:
1+
:554**`|
,将其发送到退出@
:53*%!|
,如果是,则打印 FizzBuzz 并转到底线,跳过其余的迭代:5%!|
,如果是,则打印 Buzz 并转到底线,跳过其余的迭代:3%!|
,如果是,则打印 Fizz 并转到底线,跳过其余的迭代:.
55+,
Befunge 是一种极具影响力的深奥编程语言,它催生了一个全新的二维编程语言家族。
大多数使用灵活的程序大小而不是固定的 80x25。正如我已经提到的,一些额外的指令允许在单元格中存储任意数字,或者访问堆栈而不是前两项,将大大改善可以表达的内容。
我绝对推荐你试一试。
关注我的博客,您将在其中获得提示、技巧和挑战,以保持您的技能敏锐。记得关注我哦!