import math.{log10, pow}
object ArmstrongNumbers {
def isArmstrongNumber(num: Int): Boolean =
if (num < 10) true
else {
val len: Double = log10(num).toInt + 1
Iterator
.iterate((num, 0.0, false))(tup =>
(tup._1 / 10, tup._2 + pow(tup._1 % 10, len), tup._1 == 0)
)
.takeWhile(tup => !tup._3)
.to(Seq)
.last
._2 == num
}
}
This approach starts by importing from the packages for what is needed.
The isArmstrongNumber()
method uses an if
and else
expression.
Given that the test input is never less than 1
, if the input Int
is less than 10
, then true
is returned.
Otherwise the result of the else
expression is returned.
The else
expression starts by using the log10()
method to calculate the number of digits in the input Int
.
The iterate()
method is called with a three-element tuple for a start value.
The starting tuple is made of the input Int
, a Double
value of 0.0
for the total, and a Boolean
false
to stop.
The tuple is passed into the lambda where a new tuple is generated for the next iteration.
Since pow()
take two Double
arguments,
the length of the String
was set to the Double
type to keep from doing a widening conversion of Int
to Double
on every call
to pow()
.
For an input number of 153
, the iterations would give the following tuples
(153,0.0), (15,27.0), (1,152.0), (0,153.0)
The call to iterate()
is chained to the takewhile()
method, which uses its lambda to determine
what iterations it will ask for.
In this case it will keep taking iterations until the third element of the tuple is true
, which is not set until
creating the tuple for the iteration after the first element number has been calculated down to 0.
We might be tempted to use a tuple of only two elements and takeWhile(tup => tup._1 > 0)
,
but this will omit the last iteration where the tuple generated is (0,153.0)
.
The tuple is generated, but since it has a first element of 0
, it is not taken.
This is why we need the Boolean
flag to stop after we've taken the tuple with a first element of 0
.
As of this writing, Scala 3 is not yet supported on the Scala track. With Scala 3 we could destructure the tuple in the lambda like so:
.iterate((num, 0.0, false))((nbr, total, stop) =>
(nbr / 10, total + pow(nbr % 10, len), nbr == 0)
)
.takeWhile((_, _, stop) => !stop)
which may be considered a bit more readable.
After all of the qualifying iterations are taken, The method returns whether the last
of the sequence of tuples has its second element
total equalling the original number.