Improve my "digit_sum" if it possible


#1

What's up, guys

Here is my code:


def digit_sum(n):
h = 10
summa = 0
x = 0
while (n % h) < n:
summa += (n % h) // (h / 10)
print (n % h)
h *= 10
else:
summa += (n % h) // (h / 10)
return summa


Is it possible to make it work without "else", only with while loop?


#2

Sure thing :slight_smile:

Variable x is defined, but never used so I deleted it.

def digit_sum(n):
    h = 10
    summa = 0
    while (h / 10) <= n:
        summa += (n % h) // (h / 10)
        h *= 10
    return summa

What I changed is the condition of the while. How does it work? Let's say that n = 1234:

n = 1234
h = 10
summa = 0

1 <= 1234		--> True
summa += 4 // 1
summa += 4		--> summa = 4
h *= 10			--> h = 100


10 <= 1234		--> True
summa += 34 // 10
summa += 3		--> summa = 7
h *= 10			--> h = 1000


100 <= 1234		--> True
summa += 234 // 100
summa += 2		--> summa = 9
h *= 10			--> h = 10000


1000 <= 1234		--> True
summa += 1234 // 1000
summa += 1		--> summa = 10
h *= 10			--> h = 100000

10000 <= 1234		--> False
return 10

We use here the relationship between a number of digits and being greater than a specific power of 10. For example -> we know that the three digit number must be greater than 10 to the power of 2.


But let's get one step further. Can we make this function work without a while? Or even with only a one line of code?

Yep.

A sum of digits is a sum (sum()) of list of digits ([x for str(x)]) turned into the numerical representation (int(x)):

def digit_sum(n):
    return sum([int(x) for x in str(n)])

#3

Just as a note you do not need to include the []. The sum function automagically iterates over what you give it including a generator.

sum(int(x) for x in str(n))

@surkoff

If you would like a solution that preforms the solution you have but as one line.

Python Code:

def digit_sum(number, total=0):
    return total if number <= 0 else digit_sum(number // 10, total+(number % 10))

The above uses recursion to complete the task.

You could also do it as an anonymous function aka a lambda.

Python Lambda

digit_sum = lambda number, total=0: total if number <= 0 else digit_sum(number // 10, total+(number % 10))

Although in this case I would refrain from using the lambda as it makes it harder to read, but it is nice you can change things around and still get the same result.

Also the number to string then back to int can be done as a lambda as well.

Python Lambda

digit_sum = lambda number: sum(int(num) for num in str(number))

All in all there are many solutions, you just have to use your best judgement for that particular problem.


#4

I would just like to point out the difference between list comprehension ([int(x) for x in str(n)]) and the generator expression ((int(x) for x in str(n)).

The first one will create a new list in the memory. The second one creates a generator that yields the next elements of the list, but the execution of the generator requires a separate execution frame.

When we are concerned about the memory usage (very long list) we should use the generator expression. If this is not the case -> list comprehension is usually faster and it returns a list.


That was just an explanation. In case of this function, the difference between list comprehension and generator comprehension is irrelevant.