2014-09-24 1 views
0

Я создал сценарий bash на моем Mac с именем Armstrong.sh.Контур неожиданно бежит бесконечно в сценарии Bash

Это функция, которая проверяет число, которое является номером armstrong.

# This function works properly 
armstrong() { 

    num=$1 # Making a copy of the received number. 
    sum=0  # This will store the sum of cubes of each digit from $num 



    while [ $num -gt 0];  # This loop runs while $num is greater than 0 
    do 
     temp=`expr $num % 10`      # Extract the last digit of the number 
     sum=`expr $sum + $temp \* $temp \* $temp` # Cube the last digit and add it to $sum 
     num=`expr $num/10`      # Remove the last digit of the number 
    done 



    if [ $sum -eq $1 ]; # If $sum == $1, i.e., If the number is armstrong 
    then 
     echo "$1 is an armstrong number"  # print the number 
    else 
     echo "$1 is not an armstrong number" 
    fi 
} 

Когда я пишу следующий код,

armstrong 1  # this is an armstrong number 
armstrong 153 # This is an armstrong number 
armstrong 24 # This is not an armstrong number 

Тогда это выход следующим образом,

1 is an armstrong number 
153 is an armstrong number 
24 is not an armstrong number 

Это было хорошо до сих пор.

Но ПРОБЛЕМА находится здесь.
Когда я пытаюсь напечатать все номера Армстронгом в диапазоне с помощью цикла, как это:

# Accept start and end point of the range 
echo -n "Enter start = " 
read start 
echo -n "Enter end = " 
read end 

# Loop from start to end point and call the armstrong() function 
for ((num = $start; num <= $end; num++)) 
do 
    armstrong $num # Calling the function. 
done 

Так что мои вопросы:
1> Как получить цикл работы в качестве желаемого?
2> Есть ли способ написать код, не используя $temp в armstrong() функция?
Как sum += Math.pow(num%10, 3); в Java?
3> Пожалуйста, дайте мне более чистый способ написать функцию armstrong.

+0

BTW , поскольку вы нацеливаетесь на bash, подумайте об использовании математических контекстов больше - 'if ((sum == $ 1))' немного легче читать, чем 'if [" $ sum "-eq" $ 1 "]'. –

+0

... также современный синтаксис POSIX sh: 'temp = $ ((num% 10))' и 'sum = $ ((sum + temp * $ temp * $ temp))'; нет необходимости использовать 'expr' для математики, если таргетинг на pre-POSIX Bourne (что почти неслыханно в наши дни). –

+1

Рассмотрите возможность использования http://shellcheck.net/ для статического анализа ваших сценариев оболочки, кстати. –

ответ

5

Ваша функция использует переменную num, не объявляя ее локальной, поэтому она меняет ту же самую глобально-глобальную переменную, на которую ссылается контур, таким образом, сбросив состояние цикла и не дополнив его.

Внутри функции, изменить

num=$1 

в

local num=$1 

... и, в идеале, сделать то же самое для всех других переменных внутри функций, если вы явно не хотите, чтобы ваши функции изменить переменные в глобальном масштабе.

+0

Спасибо. Это сработало. :) Кстати, я должен делать переменную '$ sum'' local' тоже? –

+0

@Aditya, да, вы также должны также делать 'sum' и' temp' local. Другой способ сделать это - просто положить строку вверху функции: «local num sum temp» –

+0

@Aditya, ... btw, в отличие от perl, '' 'не является частью имени переменной в оболочке ; вместо этого он является оператором расширения (используется, как следует из названия, при расширении переменной для его содержимого), но не используется в тех случаях, когда указана переменная без ее расширения. –

1

Функция проверяет только количество армированных цифр 3-значной длины.

Номера Армстронга, также известные как Narcissistic Numbers, являются числами, которые являются суммой каждой цифры, поднятой до мощности длины номера.

Как заметил @CharlesDuffy, чтобы избежать неожиданного поведения, переменные внутри функции должны быть определены как переменные local. Если, конечно, они не должны быть доступны во всем мире.

Кроме того, при использовании ограниченных целочисленных выражений bash большие числа будут разорвать любые test или подсчет числа.

Чтобы исправить это, вы можете использовать pattern matching для испытаний и bc для расчетов:

armstrong() { 

    # Initialize all local variables 
    local num=$1 sum=0 num_digits=${#1} 

    # Make sure number is greater than 0 
    while [[ $num == [1-9]* ]] 
    do 
     # Raise the last digit to the length of $num and add it to $sum 
     sum=$(echo "$sum + (($num % 10)^$num_digits)" | bc) 

     # Remove the last digit of the number 
     num=$(echo "scale=0; $num/10" | bc) 
    done 

    if [[ $sum == $1 ]] 
    then 
     echo "$1 is an armstrong number" 
    else 
     echo "$1 is not an armstrong number" 
    fi 

} 

В качестве альтернативы, вы можете пройти по каждой отдельной цифры в переменной num с помощью parameter expansion:

for ((i=0;i<=$((num_digits-1));i++)); do 
    sum=$(echo "$sum + (${num:$i:1}^$num_digits)" | bc) 
done 
Смежные вопросы