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:
fibn =: (-&2 +&$: -&1)^:(1&<) M."0
Compare with a more canonical (pseudocode) definition:
fib(n) = fib(n-1) + fib(n-2) if n > 2 else n
First, instead of using [ `
with @. (>&1)
, which uses a gerund, it's better to use ^:(1&<)
. For f(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, split n
into two, namely n-2
and n-1
, and second, add together the fibn
of those two numbers.
And, lastly, "0
for list handling and M.
for memoizing. M.
is rather important from a performance perspective, as a straightforward implementation of the canonical definition will call fib(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.
Okay, I found it. I ran only the recursive block through tacit generator and got this block.
13 : '(f y-1) + (f y-2)'
([: f 1 -~ ]) + [: f 2 -~ ]
Then I inserted that to the original piece, getting this.
fib =: [ ` (([: $: 1 -~ ]) + [: $: 2 -~ ]) @. (>&1)
And that works like a charm. I also inserted " 0
to the end to make it accept lists.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With