2012-03-13 21 views
19

chcę zrobić funkcję, która zwróci się silnię liczby w bashfunkcji rekurencyjnej w bash

Oto aktualny kod, który nie działa, może ktoś mi powiedzieć co jest nie tak i jak to poprawić? Właśnie zacząłem się uczyć basha i nie wiem zbyt wiele.

#!/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 = $?" 

Odpowiedz

24

Istnieje kilka składnia i dość oczywista logika jeden (return 0)

Wersja robocza jest poniżej:

#!/bin/bash 

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

Brakuje:

  1. jeśli Składnia jest zła

  2. rekurencyjne wywołanie jest zły

  3. powrotu jest złe (eval składnia jest źle)

  4. linia shbang (jest/bin/bash nie/bash/bin)

+25

Kody powrotne to jeden bajt, a więc dla n> 5. Aby to naprawić, należy zmienić funkcję, aby uzyskać wynik, a nie s et a kod powrotu. – tripleee

+8

+1 i +9000 do @tripleee. Funkcje w bashu powinny wywoływać echa, a nie zwracać ich. – Sorpigal

+7

Składnia '$ [...]' jest przestarzała od dziesięcioleci. Przestań z niego korzystać. – geirha

14
#!/bin/bash 

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

ten będzie działać lepiej.

(Działa do 25, w każdym razie, co powinno wystarczyć do udowodnienia punkt o rekursji.)

Dla wyższych numerach, bc byłoby narzędzie do wykorzystania, co dziewiąty wiersz powyżej:

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

ale trzeba być nieco ostrożny z bc -

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

był całkiem obciążać moim złym systemie!

+0

Uważam, że twoja metoda wywoła tyle podpowłok, ile wynosi 1 $, a to nie jest konieczne i bardzo nieefektywne. Jak uniknąć tworzenia podpowłok? – TorosFanny

+1

@ TorosFanny Masz rację, że można to napisać od nowa, aby uniknąć tworzenia podpowłok - tutaj jest dobra odpowiedź: https://stackoverflow.com/questions/33568055/how-to-stop-bash-from-creating-subshells-when -recursively-call-a-function Dzięki za komentarz, pomogło mi to nauczyć się czegoś nowego! – user1070300

0

Kolejny realizacja za pomocą echo zamiast return

#!/bin/bash 

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

-1: To jest po prostu przepisanie odpowiedzi @ user1070300 przy użyciu przestarzałej składni. –

+0

dlaczego echo 1 w, jeśli nie jest drukowanie tylko jeden i zachowuje się jak typ zwrotu? –

3

echo -ing wynik może być jedynym sposobem, aby uzyskać wynik dla n> 5, ale uchwycenie echo'ed rezultat wymaga podpowłoce, co oznacza, że ​​rekurencja szybko stanie się kosztowna. Tańszy rozwiązaniem jest użycie zmiennej:

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

Jeśli chcesz być bardzo pewny, że val jest ustawiona po uruchomieniu, należy użyć funkcji dozowania:

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

Dla porównania:

# 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

Porada dźwiękowa. Mimo wszystko - Twój pomiar byłby jeszcze bardziej przekonujący, gdybyś nie używał losowego wprowadzania danych. Z tego co wiemy, twoja metoda wykonała 100 zliczeń silni (1), podczas gdy druga zrobiła 100 zliczeń silni (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 
Powiązane problemy