Reputation: 2443
I have created a bash script on my Mac named Armstrong.sh
.
This is a function which checks for a number to be an armstrong number.
# 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
}
When I write the following code,
armstrong 1 # this is an armstrong number
armstrong 153 # This is an armstrong number
armstrong 24 # This is not an armstrong number
Then it's output is as follows,
1 is an armstrong number
153 is an armstrong number
24 is not an armstrong number
This was good till now.
But the PROBLEM lies here.
When I try to print all armstrong numbers within a range using a loop like this:
# 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
So my questions are:
1> How do I get the loop working as desired?
2> Is there any way I can write the code without using $temp
in armstrong()
function?
Just like sum += Math.pow(num%10, 3);
in Java?
3> Please give me a cleaner way to write the armstrong
function.
Upvotes: 0
Views: 170
Reputation: 3646
The function is only checking for armstrong numbers of 3 digit length.
Armstrong numbers, also known as Narcissistic Numbers, are numbers that are the sum of each digit raised to the power of the number's length.
As @CharlesDuffy pointed out, to avoid unexpected behavior, variables inside the function should be defined as local
variables. Unless, of course, they need to be accessed globally.
Also, when using bash
's limited integer expressions, larger numbers will break any test
or calculation of the number.
To fix that, you can use pattern matching for tests, and bc
for calculations:
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
}
Alternatively, you could iterate over each single digit in the num
variable using parameter expansion:
for ((i=0;i<=$((num_digits-1));i++)); do
sum=$(echo "$sum + (${num:$i:1}^$num_digits)" | bc)
done
Upvotes: 1
Reputation: 295373
Your function uses the variable num
without declaring it local, so it's changing the same shell-global variable referred to by the loop, thus resetting the loop's state and preventing it from completing.
Inside the function, change
num=$1
to
local num=$1
...and, ideally, do the same for all other variables inside of functions unless you explicitly want your function to modify variables in global scope.
Upvotes: 5