Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Consecutive addition of numbers in a string

I'm a new programmer learning python and am having trouble with how to approach this task:

So essentially I have a string of numbers to read imported from a file, and need to add the sum of the first number to the second and convert it to the correct ascii character. So, for example if I am reading the string:

'36 30 25 40 80 4' 

I would want to add 36 to 30, then 25 to 40, then 80 to 4 and so on, before converting them to the corresponding ASCII character and printing (so in this case 'CAT').

So far i recognize that i need to use a split command to split the string, and then a for loop to take the data for consecutive numbers. I can get the correct value for the first letter, but am having trouble 'telling' the program to atuomatically move on to add [2] to [3] after adding [1] to [0]. I also need to do this for multiple lines.

Again, I'm a new coder so any help is greatly appreciated. Thanks!

like image 545
chillbro Avatar asked Feb 06 '23 18:02

chillbro


2 Answers

You can use a special usage of zip to pair up elements:

s = '36 30 25 40 80 4'
ints = map(int, s.split()) #make into list of ints
sums = map(sum, zip(*[iter(ints)]*2))
print(''.join([chr(i) for i in sums]))

Edit: technically the iter is unnecessary here, but I write it because I sometimes forget when I do need it.

Edit 2: addressing jsbuenos points below:

map takes a function and an iterable (like a list) and applies the function to every element of that iterable. So list(map(int, ['1', '2'])) is the same as [int('1'), int('2')]. The list is there because map actually returns an iterator that produces the values one at a time instead of all at once.

The usage of zip here looks complex, but that's mostly all the punctuation. Let's try and break it down. As I mentioned, we don't need the iter here because ints is a map object, and thus already an iterator, so we don't have to make an iterator for it. So now it looks like

sums = map(sum, zip(*[ints]*2))

All the map is doing is adding up the pairs after we pair them up, so let's discard that too.

zip(*[ints]*2)

The trick to understanding this is understanding that list multiplication happens first

zip(*[ints, ints])

The * in front of an iterable is the unpacking operator. Think of it as the list dumping its arguments into the function

zip(ints, ints)

So what is zip actually doing? The normal usage of zip is to take two iterables and pair the elements up, like so.

list(zip([1,2,3], [4,5,6])) #put the results from zip into a list
[(1, 4), (2, 5), (3, 6)]

It works with more than two lists too, but that's not what we're here for.

It's important to note that in zip(ints, ints) the two ints arguments are the same object. This means that when zip makes the first pair, it first gets the next element from its first argument, ints. That's 36. Then it gets the next element from its second argument which is the same ints. So the next element is 30. It then yields those two elements in a tuple, and goes on to repeat the process for the rest of the iterator.

If you're asking yourself "Why write zip(*[iter(ints)]*2) when zip(ints, ints) means the same thing?" it's because you have to make sure that the arguments are the same object, or it doesn't work. Doing zip(iter(ints),iter(ints)) wouldn't work because each iter returns a different, though identical, iterator. I have trouble remembering when I have to use iter, so to play it safe I just use it all the time.

like image 89
Patrick Haugh Avatar answered Feb 08 '23 07:02

Patrick Haugh


Another method that is less opaque (though more verbose) than Patrick's proposal would be:

>>> x = '36 30 25 40 80 4'
>>> nums = [int(n) for n in x.split()]
>>> ''.join(chr(sum(nums[i:i+2])) for i in range(0, len(nums), 2))
'BAT'
like image 36
brianpck Avatar answered Feb 08 '23 07:02

brianpck