Recursive function in bash
There are several syntax and a quite obvious logic one (return 0)
A working version is below:
#!/bin/bash
factorial()
{
if (( $1 <= 1 )); then
echo 1
else
last=$(factorial $(( $1 - 1 )))
echo $(( $1 * last ))
fi
}
factorial 5
You are missing:
return is bad (should use
echo
)shbang line (is /bin/bash not /bash/bin)
Can't do arithmetic outside of
(( ))
or$(( ))
(orlet
, but(( ))
is preferred)
echo
-ing a result may be the only way to get a result for n > 5, but capturing the echo'ed result requires a subshell, which means the recursion will get expensive fast. A cheaper solution is to use a variable:
factorial() {
local -i val=${val:-($1)}
if (( $1 <= 1 )); then
echo $val
return
fi
(( val *= $1 - 1 ))
factorial $(( $1 - 1 ))
}
If you want to be extra sure that val
is unset when you start, use a wrapping function:
factorial() {
local -i val=$1
_fact() {
if (( $1 <= 1 )); then
echo $val
return
fi
(( val *= $1 - 1 ))
_fact $(( $1 - 1 ))
}
_fact $1
}
For comparison:
# My Method
$ time for i in {0..100}; do factorial $(( RANDOM % 21 )); done > /dev/null
real 0m0.028s
user 0m0.026s
sys 0m0.001s
# A capturing-expression solution
$ time for i in {0..100}; do factorial $(( RANDOM % 21 )); done > /dev/null
real 0m0.652s
user 0m0.221s
sys 0m0.400s
#!/bin/bash
function factorial()
{
if (( $1 < 2 ))
then
echo 1
else
echo $(( $1 * $(factorial $(( $1 - 1 ))) ))
fi
}
This will work better.
(It works up to 25, anyway, which should be enough to prove the point about recursion.)
For higher numbers, bc would be the tool to use, making the ninth line above:
echo "$1 * $(factorial $(( $1 - 1 )))" | bc
but you have to be a bit careful with bc --
$ factorial 260
38301958608361692351174979856044918752795567523090969601913008174806\
51475135399533485285838275429773913773383359294010103333339344249624\
06009974551133984962615380298039823284896547262282019684886083204957\
95233137023276627601257325925519566220247124751398891221069403193240\
41688318583612166708334763727216738353107304842707002261430265483385\
20637683911007815690066342722080690052836580858013635214371395680329\
58941156051513954932674117091883540235576934400000000000000000000000\
00000000000000000000000000000000000000000
was quite a strain on my poor system!