bashrecursionfibonaccibash4

Bash 4.3+ - Recursive fibonacci


I am working on my programming language which compiles into bash 4.3+ code. I am in the final stages of my language, but I have a small issue with recursion functions. Here's the bash code which is supposed to return the fibnacci number given an index.

#!/bin/bash

function fib() {
    local a=$1
    declare -n ret=$2
    if (( $a <= 2 )); then
        ret=1
        return
    fi
    fib $((a-1)) fib1
    fib $((a-2)) fib2

    ret=$((fib1+fib2))
    echo "fib($((a-1))) + fib($((a-2))) = $ret"
    return
}

num=5
fib $num result
echo 
echo "fib($num) = $result"

The problem in this code is that the fib(5) is giving 3 which is clearly wrong. What I think the problem is, when I pass fib1 and fib2 as a way to store the return value, they get overwritten by each call which assigns them. If that was the problem, how can I make fib1 and fib2 local to their execution scope.

Please note that I do not want to use a return statement to return the values, I want to try finding a solution using declare -n namerefs.

Thank you


Solution

  • What I think the problem is, when I pass fib1 and fib2 as a way to store the return value, they get overwritten by each call which assigns them.

    Yep, and you can see that by printing the value of fib1 between and after the recursive calls:

    fib $((a-1)) fib1
    echo "fib($a): fib1: $fib1"
    fib $((a-2)) fib2
    echo "fib($a): fib1: $fib1 fib2: $fib2"
    

    You should see the value of fib1 change during the second call. That's to be expected, since it wasn't declared local and there only is one global copy of fib1.

    If you make them local... it doesn't help much.

    Assume you start by calling fib 4 result. The first iteration will make fib1 local, and call fib 3 fib1. Now the second iteration will also make fib1 local, but it will also try to assign its return value to a variable of the same name. Since the access is by name, it saves the return value to its own copy of fib1.

    This can be seen with a somewhat simpler script too, this tries to return a fixed value up from the bottom of the recursion:

    #!/bin/bash  
    foo() {         
        declare -n ret=$2
        if (( $1 == 0 )); then
            echo "foo($1) returning"
            ret=end          # this is the value that should bubble up
            return
        fi
        local x=initial$1    # use $1 here to track the level the value came from
        foo $(($1 - 1)) x
        ret=$x
        echo "foo($1) = $x"
        return
    }
    foo 3 result
    echo "main:    $result"
    

    The workaround I can think of is to have a separate global variable for the return value, and to immediately copy it to a local variable:

    local fib1 fib2
    fib $((a-1)) retval
    fib1=$retval
    fib $((a-2)) retval
    fib2=$retval