2012-03-13 28 views
19

Quiero hacer una función que devolverá el factorial de un número en bashfunción recursiva en bash

Aquí está el código actual que no funciona, alguien me puede decir lo que está mal y cómo corregirlo? Acabo de empezar a aprender bash y no sé mucho.

#!/bash/bin 
factorial() 
{ 
    let n=$1 
    if (("$n" <= "1")) 
    then return 1 
    else 
    factorial n-1 
    return $n*$? 
    fi 
    return 0 
} 
factorial 5 
echo "factorial 5 = $?" 

Respuesta

24

Hay varias sintaxis y una lógica bastante obvia (retorno 0)

Una versión de trabajo es el siguiente:

#!/bin/bash 

factorial() 
{ 
    if [[ $1 -le 1 ]] 
    then 
     echo 1 
    else 
     last=$(factorial $[$1-1]) 
     echo $(($1 * last)) 
    fi 
} 
factorial 5 

Te faltan:

  1. si la sintaxis es mala

  2. llamada recursiva es malo

  3. retorno es mala (sintaxis eval mal allí)

  4. línea shbang (es/bin/bash no/bash/bin)

+25

Los códigos de retorno son un byte único, por lo que se rompe para n> 5. Para solucionar esto, deberá cambiar la función para repetir un resultado, en lugar de s et un código de retorno. – tripleee

+8

+1 y +9000 a @tripleee. Las funciones en bash deben hacer eco de los resultados, no devolverlos. – Sorpigal

+7

La sintaxis '$ [...]' ha quedado obsoleta durante décadas. Deja de usarlo. – geirha

14
#!/bin/bash 

function factorial() 
{ 
    if (($1 < 2)) 
    then 
    echo 1 
    else 
    echo $(($1 * $(factorial $(($1 - 1))))) 
    fi 
} 

Este funcionará mejor

(Funciona hasta 25, de todos modos, que debería ser suficiente para probar el punto sobre la recursividad.)

Para los números más altos, BC sería la herramienta a utilizar, por lo que la novena línea anterior:

echo "$1 * $(factorial $(($1 - 1)))" | bc 

pero hay que ser un poco cuidadoso con BC -

$ factorial 260 
38301958608361692351174979856044918752795567523090969601913008174806\ 
51475135399533485285838275429773913773383359294010103333339344249624\ 
06009974551133984962615380298039823284896547262282019684886083204957\ 
95233137023276627601257325925519566220247124751398891221069403193240\ 
41688318583612166708334763727216738353107304842707002261430265483385\ 
20637683911007815690066342722080690052836580858013635214371395680329\ 
58941156051513954932674117091883540235576934400000000000000000000000\ 
00000000000000000000000000000000000000000 

era del todo una tensión en mi pobre sistema!

+0

Me parece que su método invocará tantas subcapas como $ 1, y esto no es necesario y muy poco eficiente. ¿Cómo evitar la creación de subcapas? – TorosFanny

+1

@TorosFanny Tiene razón en que se puede volver a escribir para evitar la creación de subcadenas. Aquí hay una buena respuesta: https://stackoverflow.com/questions/33568055/how-to-stop-bash-from-creating-subshells-when -recursively-call-a-function Gracias por tu comentario, ¡me ayudó a aprender algo nuevo! – user1070300

0

Otra aplicación utilizando en lugar de echoreturn

#!/bin/bash 

factorial() 
{ 
     if [ $1 -le 1 ] 
     then 
       echo 1 
     else 
       echo $[ $1 * `factorial $[$1-1]` ] 
     fi 
} 
echo "factorial $1 = " `factorial $1` 
+0

-1: Esto es solo una reescritura de la respuesta de @ user1070300 usando sintaxis obsoleta. –

+0

¿Por qué echo 1 en if no está imprimiendo solo uno y se comporta como un tipo de devolución? –

3

echo -ing resultado puede ser la única manera de conseguir un buen resultado para n> 5, pero capturar el resultado echo'ed requiere un subnivel, que significa que la recursión se volverá costosa rápidamente. Una solución más barata es utilizar una variable:

factorial() { 
    local -i val=${val:-($1)} 
    if (($1 <= 1)); then 
     echo $val 
     return 
    fi 
    ((val *= $1 - 1)) 
    factorial $(($1 - 1)) 
} 

Si quieres estar bien seguro de que val no está definida cuando se inicia, utiliza una función de envoltura:

factorial() { 
    local -i val=$1 
    _fact() { 
     if (($1 <= 1)); then 
      echo $val 
      return 
     fi 
     ((val *= $1 - 1)) 
     _fact $(($1 - 1)) 
    } 
    _fact $1 
} 

Para la comparación:

# 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 
+0

Consejo de sonido. Aún así, su medida habría sido aún más convincente si no hubiera utilizado una entrada aleatoria. Por lo que sabemos, su método hizo 100 recuentos de factorial (1) mientras que el otro hizo 100 recuentos de factorial (20) :) – LOAS

0
clear cat 

fact() 

{ 

     i=$1 
     if [ $i -eq 0 -o $i -eq 1 ] 
     then 
       echo 1 
     else 
       f=`expr $i \- 1` 
       f=$(fact $f) 
       f=`expr $i \* $f` 
       echo $f 
     fi 
} 

read -p "Enter the number : " n 

if [ $n -lt 0 ] 

then 

     echo "ERROR" 

else 

     echo "THE FACTORIAL OF $n : $(fact $n) " 
fi 
Cuestiones relacionadas