Implementing a function that sums integers contained in a string in Python - python

So I've recently picked up John Guttag's Introduction to Computation and Programming Using Python,the revised and expanded edition, after having worked through most of LPTHW. I am using the book in conjunction with MIT OCW 006. Now, I was trying to complete one of the Finger Exercises listed in the book, specifically the one of page 85, chapter 7, where the author asks you to implement a function using a try-except block:
def sumDigits(s):
"""Assumes s is a string
Returns the sum of the decimal digits in s
For example, if is is'a2b3c' it returns 5"""
This is my code:
def sumDigits(s):
try:
total = 0
list1 = [s]
new_list = [x for x in list1 if x.isdigit()]
for e in new_list:
total += new_list[e]
return total
except TypeError:
print "What you entered is not a string."
When I run this program in the IDLE using a test input, the total is always computed to be zero, indicating that none of the elements of new_list are being passed to the accumulator. Could someone suggest why that is? Thanks.

It seems like the errors have been pointed out already by Rafael but it is still important to note that the more pythonic way to approach this would be:
return sum([int(x) for x in s if x.isdigit()])

There are actually several errors with your code.
Let's break them down in detail
The main problem is located in these lines:
list1 = [s]
new_list = [x for x in list1 if x.isdigit()]
You should loop directly over the string first
new_list = [x for x in s if x.isdigit()] #s is the input string
When you create a new list as you did, the variable x in x for x in list1 will take place as elements of the list. So, in your case, the list will have only one element, which happen to be whole string (because you defined the list as [s]. As the whole string is not a digit, new_list will be an empty list.
That is why you are getting 0 as a return.
However, if you loop through the string directly, x will take place as each letter in the string, and then it will be possible to check whether x is digit or not.
It is also important to highlight that new_list[e] will raise IndexError. You should correct that for e only. The sintax of for e in new_list makes the local variable e assume each value inside the list, so you do not have to get the value via indexes: you can use e directly.
Finally, in order to sum the values in your new_list, the values should be integers (int) and not string (str), so you have to cast the values to int before summing (or, you can cast each element to int during the list comprehension, by using int(x) for x in s if x.isdigit() instead of x for x in s if x.isdigit()). Also, in order to check if the input is a string or not, you better use isinstance(s, basestring) if you're in python2, or isinstance(s, str) if you're using python3.
So the whole code would look like this :
def sumDigits(s):
if isinstance(s, basestring):
total = 0
new_list = [x for x in s if x.isdigit()]
for e in new_list:
total += int(e)
return total
else:
print "What you entered is not a string."

I'm working through the same book and the MITx: 6.00.1x course on edX; here's my solution:
def sumDigits(s):
'''
Assumes s is a string
Returns the sum of the decimal digits in s
For example, if s is 'a2b3c' it returns 5
'''
result = 0
try:
for i in range(len(s)):
if s[i].isdigit():
result += int(s[i])
return result
except:
print('Your input is not a string.')
Since we are to assume that s is a string, the except block should handle those cases where s is not a string. So simple, but it was not obvious to me at first.

You can use reduce method
reduce( (lambda x, y: x + y), [int(x) for x in new if x.isdigit()] )

I'm working through the same book too. I think we should use the try-except block on determining whether characters of string convertible to an integer. So here is my solution.
def sumDigits(s):
"""Assumes s is a string
Returns the sum of the decimal digits in s
For example, if s is 'a2b3c' it returns 5"""
sum = 0
for i in s:
try:
sum += int(i)
except ValueError:
None
return sum

Related

How are values tracked with the 'for el in sequence' syntax?

I am trying to grasp what mechanism is keeping track of values being returned to the sum() function in the following:
def narcissisticNumber(value):
return value == sum(int(x) ** len(str(value)) for x in str(value))
From what I see, x ** y is being calculated for each character in the value string - however, since the 'for char in str' construct is within the sum function, the result of each of these computations is being returned to sum(). The return value of sum() accounts for all iterations, and I would like to know how sum() was able to track all the return values of the x ** y expression.
I am using Python 3.7.4. Coming from Java, I understand the function does not follow best practices, I would just like to know how it works in terms of Python.
What you are using is a Generator:
Generators are a simple and powerful tool for creating iterators. They
are written like regular functions but use the yield statement
whenever they want to return data. Each time next() is called, the
generator resumes where it left-off (it remembers all the data values
and which statement was last executed)
Check here for better understanding: https://wiki.python.org/moin/Generators
I think a good way of understanding it better would be to break it down to a more readable format:
def narcissisticNumber(value):
listOfValues = []
lenOfValue = len(str(value))
# Iterate through each character in your string
for x in str(value):
# Get the int-value of that character
# then raise it to the length of your string
xRaiseToLength = int(x) ** lenOfValue
# Append that value to your final list
listOfValues.append(xRaiseToLength)
# Compute the sum of the list using sum()
sumOfValues = sum(listOfValues)
return value == sumOfValues
References:
sum()
Okey lets break it into parts:
str(value) is casting value into a string (str)
len(str(value)) is getting the length of this string
... for x in str(value) is a generator expression. It will assign to x each element in str(value) (this is, each character) and evaluate the first clause. To simplify this, you can think that it returns an array. It actually doesn't, but conceptually yopu can think of it like that. It is actually creating a special construct that can be iterated over and that evaluates each element when they are requested, so it is way more memory efficent that creating an array.
int(x) is casting x (which is each of the characters of the string) into an integer (int).
int(x) ** len(str(value)) is calculating x to the power of the length of the complete string.
sum is a fucntion that accepts any iterable construct (in this case a generator, but it could be an array of some sort) and adds its elements.
So in conclussion, if you do narcissisticNumber(123) it will return 1^3 + 2^3 + 3^3 = 1 + 8 + 27 = 36.

writing a function that return the reversed number

I am trying to write a Python function that get a number as input and returns its reversed number as output. for example: 1234 returns 4321.
this is what I try, but it return only ''
def reverse(num):
L=[]
x=str(num)
L1=list(x)
for i in L1:
L.insert(0,i)
print 'the reversed num is:'
x=''
for i in L:
x.join(i)
return x
any ideas?
def reverse(num):
return str(num)[::-1]
or one line:
lambda x: str(x)[::-1]
Well, the easy solution is this one:
>>> int(str(1234)[::-1])
4321
Your code can be fixed by changing the part
for i in L:
x.join(i)
return x
to
for i in L:
x += i
return x
Alternatively, just replace that section by
return ''.join(L)
What was wrong with your code? Because of wrong indentation, you returned in the first iteration of the for loop. You never assigned a name to x.join(i) so the return value was lost. What you expected join to do I do not know.
First, there is an easier way by converting to string, slicing the string and converting it back to a number.
def reverse(num):
return int(str(num)[::-1])
Second, there are multiple errors in your code:
1) your return statement is in the loop, so it will return after the first iteration;
2) x does not change because x.join() creates a new string and does not modify the string x (which is immutable by the way)
3) no need to convert the string into a list since you can directly iterate over the string (for i in x: ...)
4) join() takes an iterator as an argument. No need for the second loop: return ''.join(L)
thank you all for the helpful ideas.
here is my solution:
def reverse(n):
reverse=0
while(n>0):
dig=n%10
reverse=reverse*10
reverse=reverse+dig
n=n/10
return reverse
def reverse(num)
return str(num)[::-1]
Reverse a string in Python
Other users already gave good answers. Here is a different one, for study purposes.
num = 1234
print "".join(reversed(str(num)))
# 4321
You can convert to int afterwards.

Why is my program showing a TypeError?

Every time I run my code I get this error
TypeError: sequence item 0: expected str instance, int found
The error shows up even when I have converted each element in the list to a string. Please help me fix this. The code is
def add_list(list1):
m=0
for x in list1:
m=m+x
return m
def summarize(list2):
list1 = list2
for x in list2:
x = "{}".format(x)
return "The sum of {} is {}.".format("".join(list2), add_list(list1))
summarize([1,2,3])
When you run a for loop over a list of immutable objects like strings or numbers it's actually making what's effectively a copy. It doesn't take the actual element itself out of the list. So that means
for x in list2:
x = "{}".format(x)
Changes nothing, because what happens could be written verbosely like this:
for x in list2:
>>> x = list[0]
x = "{}".format(x)
>>> x = list[1]
x = "{}".format(x)
>>> x = list[2]
x = "{}".format(x)
You constantly change x, but that doesn't do anything to the elements of the list. If you want to loop over the elements of a list you need to do this
for i,x in enumerate(list2):
list[i] = "{}".format(x)
In that format, 'i' will be set to the index of the element that x currently is so you can refer back to the actual position in the list and edit it there, which WILL result in the list itself changing.
Also you can just use str(x) to turn something into a string, it's much cleaner. It also works on any datatype so you can always print something
>>> str([12,1,"abba"])
"[12,1,'abba']"
>>> str(open("I'm a file.txt",'r'))
"<open file 'I'm a file.txt', mode 'r' at 0x0000000002ADC420>"
>>> str(None)
"None"
However I recommend trying some of the other solutions for how to print this, this isn't ideal. I just thought it was valuable for you to understand the error.
I think this is clearer
return "The sum of %s is %s." % (list2, add_list(list1))

Higher order function in Python exercise

I learning Python and during solution an exercise, function filter() returns empty list and i can't understand why. Here is my source code:
"""
Using the higher order function filter(), define a function filter_long_words()
that takes a list of words and an integer n and returns
the list of words that are longer than n.
"""
def filter_long_words(input_list, n):
print 'n = ', n
lengths = map(len, input_list)
print 'lengths = ', lengths
dictionary = dict(zip(lengths, input_list))
filtered_lengths = filter(lambda x: x > n, lengths) #i think error is here
print 'filtered_lengths = ', filtered_lengths
print 'dict = ',dictionary
result = [dictionary[i] for i in filtered_lengths]
return result
input_string = raw_input("Enter a list of words\n")
input_list = []
input_list = input_string.split(' ')
n = raw_input("Display words, that longer than...\n")
print filter_long_words(input_list, n)
Your function filter_long_words works fine, but the error stems from the fact that when you do:
n = raw_input("Display words, that longer than...\n")
print filter_long_words(input_list, n)
n is a string, not an integer.
Unfortunately, a string is always "greater" than an integer in Python (but you shouldn't compare them anyway!):
>>> 2 > '0'
False
If you're curious why, this question has the answer: How does Python compare string and int?
Regarding the rest of your code, you should not create a dictionary that maps the lengths of the strings to the strings themselves.
What happens when you have two strings of equal length? You should map the other way around: strings to their length.
But better yet: you don't even need to create a dictionary:
filtered_words = filter(lambda: len(word) > n, words)
n is a string. Convert it to an int before using it:
n = int(raw_input("Display words, that longer than...\n"))
Python 2.x will attempt to produce a consistent-but-arbitrary ordering for objects with no meaningful ordering relationship to make sorting easier. This was deemed a mistake and changed in the backwards-incompatible 3.x releases; in 3.x, this would have raised a TypeError.
I don't know what your function does, or what you think it does, just looking at it gives me a headache.
Here's a correct answer to your exercise:
def filter_long_words(input_list, n):
return filter(lambda s: len(s) > n, input_list)
My answer:
def filter_long_words():
a = raw_input("Please give a list of word's and a number: ").split()
print "You word's without your Number...", filter(lambda x: x != a, a)[:-1]
filter_long_words()

Find Integers in list

I have a list like this, named x (which I have already split):
['16','bob','2440', '34']
I want to write a code that checks to see if any of the numbers are negative. The code I tried does not work. This is what I have tried:
for num in x:
if num < 0:
print ("Negative number")
Your list contains only strings. So you should cast them to floats (or integers, whatever you need) first:
a = ['"16','bob','2440', '-2', '34"']
for x in a:
try:
if float (x) < 0: print ('negative')
except: continue
EDIT: I changes int to float as OP is asking for numbers and not exclusively integers.
You need to turn your numbers into integers first; use a predicate function to try to do this:
def negative(string):
try:
return int(string.strip('"')) < 0
except ValueError:
return False
The predicate function here also removes quotes; your input list looks like it was not cleaned up properly and you may want to do so first before testing for negative values.
Then use that to test for negative values:
negative_values = [v for v in a if negative(v)]
or test if there are any negative values:
if any(negative(v) for v in a):
print "No negative values please!"
How about checking for - sign in the beginning of an item and for the rest of an item to consist of digits? One-liner:
>>> a = ["-1", "aa", "3"]
>>> any(s.startswith('-') and s[1:].isdigit() for s in a)
True
Using any, because you've said that you want to write a code that checks to see if any of the numbers are negative.
Note: if there can be negative floats, then just replace s[1:] with s[1:].replace(".", "").
Hope that helps.
First, you need to understand that neither '"16' nor '2440' are numbers - they are strings.
Secondly, you need to figure out what you want to do with '"16' - it doesn't represent a number, but I assume you want it to. You could alter these strings, but you should probably just use an appropriate method of splitting in the first place.
That said, you can do this:
x = ['"16','bob','2440', '34"']
def int_is_negative(s)
try:
return int(s) < 0
except ValueError:
return False
is_negative_num = [int_is_negative(s) for s in x]

Categories

Resources