Ackermann's Function in Brainfuck
---------------------------------
Ackermann's function is usually written in →declarative← style as three cases:
ack 0 n → n+1
ack m 0 → ack (m-1) 1
ack m n → ack (m-1) (ack m (n-1))
where '→' is pronounced "reduces to".
We can transform this definition into tail-recursive form with an additional
argument [a], which is a list of values-to-be-processed:
ack ⍬,0 n → n+1
ack a,0 n → ack a,n+1
ack a,m 0 → ack a,(m-1) 1
ack a,m n → ack a,(m-1) m (n-1)
The pattern-matching for a null argument list (⍬,0 n) may be made a little more
regular if we mark the list with a special terminating value of, say, ¯1:
ack a,¯1 n → n
ack a, 0 n → ack a,n+1
ack a, m 0 → ack a,(m-1) 1
ack a, m n → ack a,(m-1) m (n-1)
This simplification entails one additional final reduction, so instead of:
ack ⍬ 0 n → n+1
we have:
ack ¯1 0 n → ack ¯1 (n+1) → n+1
Coded in D and using ⍺ as the accumulator:
ack←{⍺←¯1
a←¯2↓⍺,⍵
m n←¯2↑⍺,⍵
m=¯1:n
m=0:a ∇ n+1
n=0:a ∇(m-1)1
a ∇(m-1)m(n-1)
}
ack 3 3 ⍝ ack 3 3 → 61
61
We can now code this tail-recursive version of Ackermann's function in BF using
→mac← our simple macro processor.
The following small function collects character vectors into a newline-separated
line-vector in buffer ⍙:
∆←{{}⍙,←⍵,⎕ucs 13} ⍝ accumulate into ⍙ buffer.
⍙←'' ⍝ null accumulation buffer.
So:
⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝
⍝ ⍝
⍝ Macros: ⍝ Ackermann's function. ⍝
⍝ ⍝ ⍝
∆' ;=(, ∆=/- >6∆[+<8∆>]< )· · ' ⍝ input digit ⍝
∆' :=( ∆=/+ >6∆[-<8∆>]< .)· · ' ⍝ output digit ⍝
∆' ' ⍝ ⍝
∆' {=/>+/<[/>-/< · · · · · · · ' ⍝ if ⍝
∆' ⋄=/>]/>[-/< · · · · · · · · ' ⍝ else ⍝
∆' }=/>/>]/</< · · · · · · · · ' ⍝ fi ⍝
∆' ' ⍝ ⍝
∆' ≥=( ' ⍝ dup ⍺ right ⍝
∆' [-/>+>+</<]/>> ' ⍝ m .. 0 → 0 .. m m ⍝
∆' [-</<+/>>]</< ' ⍝ m .. m ← ⍝
∆' ) · ' ⍝ ⍝
∆' ' ⍝ ⍝
∆' ←=[-/<+/>] · · · · · · · · ' ⍝ move ⍺ left ⍝
∆' →=[-/>+/<] · · · · · · · · ' ⍝ move ⍺ right ⍝
∆' ' ⍝ ⍝
⍝ Code: ⍝ ⍝
∆' ' ⍝ ⍝
∆' - ' ⍝ (¯1) ⍝
∆' >; ' ⍝ ¯1(m) ⍝
∆' >;< ' ⍝ ¯1(m)n ⍝
∆' +[- ' ⍝ a(m)n ⍝
∆' 2{ ' ⍝ if m≠0: ⍝
∆' >1{ ' ⍝ if n≠0: ⍝
∆' <3≥ ' ⍝ a(m)n 0 m ⍝
∆' >1→ ' ⍝ a m(0)n m ⍝
∆' >>2← ' ⍝ a m m n(0) ⍝
∆' <-<<->> ' ⍝ a(m-1)(m)(n-1) ⍝
∆' 1⋄ ' ⍝ else if n=0: ⍝
∆' <->+ ' ⍝ a(m+1)1 ⍝
∆' 1}< ' ⍝ fi ⍝
∆' 2⋄ ' ⍝ else if m=0: ⍝
∆' >1←<+< ' ⍝ a(a)(n+1)0 ⍝
∆' 2} ' ⍝ fi ⍝
∆' +] ' ⍝ until a(¯1)n ⍝
∆' >: ' ⍝ output n ⍝
ack ⍙←⍙ '' ⍝
⍝ ⍝
⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝
The program inputs two single digits and outputs a single digit result. Unless
we use the memory dump, this restricts the possible arguments to:
m n→0 1 2 3 4 5 6 7 8
↓ ┌───┬───┬───┬───┬───┬───┬───┬───┬───┐
0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │
├───┼───┼───┼───┼───┼───┼───┼───┼───┘
1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │
├───┼───┼───┼───┼───┴───┴───┴───┘
2 │ 3 │ 5 │ 7 │ 9 │
├───┼───┴───┴───┘
3 │ 5 │
└───┘
bf mac ack ⍝ ack 2 3 → 9
2
3
9
The following function removes all non-BF "white space", which was preserved by
the macro processor:
dark ← ∩∘'[]<>+-,.' ⍝ without white space.
and this optimiser removes cancelling <> and >< sequences.
opt ← {↑{({⍵⍱¯1⌽⍵}⍺⍷⍵)/⍵}⍣≡/'><' '<>'⍵} ⍝ cancelling >< pairs removed.
⍴opt dark mac ack ⍝ size of optimzed BF code.
181
This function wraps and indents BF loops for easier reading:
pretty←{
nl←⊃⌽⎕TC
dents←{∊¨(0⌈⍵+1 ¯1)⍴¨⊂⊂4↑'·'}
0{
nice(next nasty)←⍵
'∘'=next:∊nice
more less←dents ⍺
'['=next:(⍺+1)∇(nice,next,nl,more)nasty
']'=next:(⍺-1)∇(nice,nl,less,next)nasty
⍺ ∇(nice next)nasty
}↑{⍺ ⍵}/⍵,'∘'
}
You should be able to copy and paste the following output into any BF system:
pretty opt dark mac ack ⍝ Ackermann's function in Brainfuck.
->,>------[
· +<-------->
],>------[
· +<-------->
]<<+[
· ->>+<<[
· · >>-+<[
· · · >-<<[
· · · · ->>>+>+<<<<
· · · ]>>>>[
· · · · -<<<<+>>>>
· · · ]<<<[
· · · · ->+<
· · · ]>>[
· · · · -<<+>>
· · · ]<-<<->>>
· · ]>[
· · · -<<->+>>
· · ]<
· ]>>[
· · -<[
· · · -<+>
· · ]<+>>>
· ]<<<<+
]>>++++++[
· -<++++++++>
]<.
See also: bf mac balm
Back to: contents
Back to: Workspaces