object Luhn {
def valid(input: String): Boolean = {
input
.foldRight((1, 0)) { (chr, tup) =>
chr match {
case chr if chr.isDigit => {
val num = chr.asDigit
tup match {
case (pos, sum) if pos % 2 == 0 =>
(pos + 1, sum + (if (num > 4) (num * 2) - 9 else num * 2))
case (pos, sum) => (pos + 1, sum + num)
}
}
case chr if chr.isSpaceChar => tup
case _ => (-100, 0)
}
} match {
case (pos, sum) => pos > 2 && sum % 10 == 0
}
}
}
This approach starts by calling the foldRight()
method on the String
input.
It is initialized with 1
for the starting position and 0
for the sum, both wrapped in a tuple.
The tuple and each character is passed into the lambda where the character is tested in a match
expression. The pattern matching first checks if the character is a digit.
If so, an Int
is set from the character and the tuple is then matched to whether the position is even or odd.
A new tuple is passed into the next iteration of foldRight()
, with the sum increased according to the position,
and the position increased by 1
.
Note that the original tuple does not have its position or sum mutated, but a new tuple is created from the operations
on the position and sum, thus supporting immutability.
If the character is not a letter, then it is tested for being a space. If it is a space, then the tuple is passed into the next iteration as is, without changing the position or the sum.
It the character is neither a digit nor a space, then the tuple is recreated with a position that is so far negative
that it won't pass the final match
.
After foldRight()
is done, the final tuple is passed to a match
where the position is tested to be greater than 2
and the sum is checked to be evenly divisible by 10
.