2012-03-13 2 views
19

Я хочу сделать функцию, которая будет возвращать факториал числа в БашРекурсивные функции в Баш

Вот текущий код, который не работает, может кто-нибудь сказать мне, что это не так и как это исправить? Я только начал изучать баш, и я этого не знаю.

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

ответ

24

Есть несколько синтаксис и совершенно очевидно, логика одна (возвращение 0)

рабочая версия ниже:

#!/bin/bash 

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

У вас не хватает:

  1. если синтаксис плохой

  2. Рекурсивный вызов плохо

  3. возвращение плохо (Eval синтаксис там плохо)

  4. shbang линия (это/бен/Баш не/Баш/бен)

+25

Коды возврата - это один байт, поэтому это прерывается при n> 5. Чтобы исправить это, вам нужно будет изменить функцию, чтобы эхо результат, а не s и код возврата. – tripleee

+8

+1 и +9000 - @tripleee. Функции в bash должны отражать результаты, а не возвращать их. – Sorpigal

+7

Синтаксис '$ [...]' устаревает десятилетиями. Прекратите использовать его. – geirha

14
#!/bin/bash 

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

Этот будет работать лучше.

(Она работает до 25, в любом случае, что должно быть достаточно, чтобы доказать пункт о рекурсии.)

Для более высоких чисел, Ьс бы инструментом для использования, делая девятую строчку выше:

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

, но вы должны быть немного осторожным с Ьсом -

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

был довольно нагрузка на моей бедной системе!

+0

Я нахожу, что ваш метод будет вызывать столько подоболочек, сколько $ 1, и это не обязательно и очень неэффективно. Как избежать создания подоболочек? – TorosFanny

+1

@TorosFanny Вы правы, что это можно переписать, чтобы избежать создания подоболочек - здесь есть хороший ответ: https://stackoverflow.com/questions/33568055/how-to-stop-bash-from-creating-subshells-when -recursively-call-a-function Спасибо за ваш комментарий, это помогло мне узнать что-то новое! – user1070300

0

Еще одна реализация с использованием echo вместо 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: Это просто переписывание ответа @ user1070300 с использованием устаревшего синтаксиса. –

+0

Почему echo 1 под if не печатает только один и ведет себя как возвращаемый тип? –

3

echo -ную результат может быть единственным способом, чтобы получить результат для п> 5, но захватывая результат требует через echo подоболочку, который означает, что рекурсия будет дорого стоить. Более дешевым решением является использование переменной:

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

Если вы хотите дополнительно убедиться в том, что val снята с охраны при запуске, используйте функцию оберточной:

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

Для сравнения:

# 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

Звуковой совет. Тем не менее, ваше измерение было бы еще более убедительным, если бы вы не использовали случайный ввод. Насколько нам известно, ваш метод сделал 100 подсчетов факториала (1), в то время как другой сделал 100 пунктов факториала (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 
Смежные вопросы