I'm a newcomer to J and I've been trying to create a Fibonacci function as an exercise (always the second function I create when learning a language). I just can't figure out what exactly is wrong in my way of doing it. I have tried to define it as tacit, but it gets hung if argument is greater than one.
fib =: [ ` (($: (]-1)) + ($: (]-2))) @. (>&1)
I've also attempted to create it explicitly, and that worked fine.
fib =: 3 : 'if. y>1 do. (fib (y-1)) + (fib (y-2)) else. y end.'
I tried to create a tacit out of that by replacing 3 with 13, but it threw an error.
fib =: 13 : 'if. y>1 do. (fib (y-1)) + (fib (y-2)) else. y end.'
|spelling error
| if. y>1 do. (fib (y-1)) + (fib (y-2)) else. y end.
| ^
| fib=: 13 :'if. y>1 do. (fib (y-1)) + (fib (y-2)) else. y end.'
So, I'm asking for someone to explain what exactly I am doing wrong here.
Here's an alternative that I think is both clearer and more concise:
Compare with a more canonical (pseudocode) definition:
First, instead of using
[ `
with@. (>&1)
, which uses a gerund, it's better to use^:(1&<)
. Forf(n) if cond(n) else n
, using the^:
conjunction is more idiomatic;^:0
means "do nothing" and^:1
means "do once," so the intent is clear.@.
is better suited to nontrivial behavior.Second, using the
&
bond/compose conjunction simplifies the train significantly. Repeated uses of[:
and]
are rather confusing and opaque. Refactoring using&
puts together related operations: first, splitn
into two, namelyn-2
andn-1
, and second, add together thefibn
of those two numbers.And, lastly,
"0
for list handling andM.
for memoizing.M.
is rather important from a performance perspective, as a straightforward implementation of the canonical definition will callfib(2)
excessively. You can have your cake (a simple definition) and eat it too (good performance) with the built-in memoization adverb.Source for this particular definition:
f0b
on this page.