Python Lists Append True Boolean - python

The following [incomplete] code is designed to take in an n to x number, of length x-n, and return the value of the next pandigital number. The code identifies which number between n and x is missing from the number passed in as an argument to the function, and returns (for the time being, until the function is further developed), two lists, the original number itself with its individual digits as members of a list, and a list, with the numbers n to x as members, with those numbers which are present in the original number of length x-n being replaced by the Boolean value True.
def nextPandigital(n,lower,upper):
digits = []
requiredDigits = []
#Completed loop
for digit in str(n):
digits.append(int(digit))
#Completed loop
for num in range(lower,upper+1):
requiredDigits.append(num)
for number in requiredDigits:
if str(number) in str(digits):
x = requiredDigits.index(number)
#requiredDigits[x] = 'True'
requiredDigits[x] = True
return digits, requiredDigits
Although, for the input parameters of nextPandigital(1023456789,0,9) in Enthought Canopy, the second list returned should read [True,True,True,True,True,True,True,True,True,True], the value of the second list returned is, in fact [True,1,True,True,True,True,True,True,True,True], with the 1 from the original requiredDigits list not being replaced by True.
I know that there is no issue with the loop, or the general flow of the code, for when the requiredDigits[x] = True line of code is commented, and the currently commented code is uncommented, the code works as it is intended to, with all digits in requiredDigits being replaced by the String value 'True.'
I have attempted to resolve this issue. However, I am not able to pinpoint its source. I have considered to fact that True == 1 returns True. However, when the value True is replaced by False, in the line requiredDigits[x] = True, the code still works as it is intended to.
Any answer/help/suggestion/advice on this matter would be highly appreciated. Thank you in advance.

The issue is with using index to find where to assign to. Since True is equal to 1, you're mistakenly thinking that the True you entered for 0 is the 1 you want to replace next.
A solution would be to use enumerate to get indexes as you iterate over your list, rather than needing to find them later using index:
for x, number in enumerate(requiredDigits):
if str(number) in str(digits):
requiredDigits[x] = True
A better solution in general would be to use a list comprehension to create the list in one go, rather than starting with numbers and replacing some of them later:
requiredDigits = [True if num in digits else num for num in range(lower,upper+1)]
I'm also getting rid of the unnecessary calls to str in the membership test against digits. You were doing substring testing, rather than testing if the numbers themselves were in the list. That probably wasn't going to cause errors, since the numbers you care about are all one digit long, and the string representation of a list doesn't have any extraneous digits. But in general, its not a good idea to use string operations when you don't need to.

You are checking the same list that you are updating and that is always dangerous.
At the second iteration your variables are:
number=1
requiredDigits = [True, 1, 2, 3, 4, 5, 6, 7, 8, 9]
So when you are doing requiredDigits.index(1) there is a match but it does not reach the 1 since it happens in True==1 so it returns that the index 0
But in general, this implementation is not much pythonic, better check Blckknght's answer

Related

when assigning to a variable list index out of range?

n = int(input())
numb = input()
lis = list(map(int, numb.split()))
lis.sort()
a = lis[n]
for i in (0,len(lis)):
if lis[i]=[a]
print (lis[i-1])
I tried this and when I enter input of 5 for n and the lis as 24689 it says there is an error in the line where a=lis[n] saying the list index is out of range.
The problem is probably with the line
for i in (0,len(lis)):
Lists are zero-based so the valid indexes are from 0 to len(lis) - 1. len(lis) is indeed out of range.
Also, notice that the above line of code means that i will receive only 2 values - 0 and len(lis). Is that what you really mean? If you meant to go over all the indexes, you will need to do
for i in range(len(lis)):
range(n) is an iterator that returns the values from 0 to n - 1.
There are several problems with this code. Firstly, you need to change your for statement to for i in range(0, len(lis)) because you're not currently calling the range function.
Also, on the first iteration, it will try to access index -1, which is the last element in the list. A better idea is to change your range function to range(0, len(lis) - 1), and changing the print statement on the last line to print(lis[i]) to fix the problem of receiving the last element by using the index -1.
P.S. Welcome to Stack Overflow!
Wow, Keep note that what you thought would happen on your third line wasn't the case and is 1 of the 2 problems of your error message. and that 1 problem was due to one single function you used which I have sequentially demonstration below;
I assume you were expecting each character of the string '24689' to be transformed to an integer by the map() function*, then converted to a list by the list() function*, then stored in the Variable ...lis... for later further usage. You were expecting something like [2,4,6,8,9] as Lis`.
Well you almost had that if not for your decision to use split() on Numb.
The Split function will run first and turn the '24689' into a
different iterable, in this case changing '24689' to a list ['24689'].
Then the map() will map every item in this iterable ['24689'] to an
integer. In this case only 1 item exist in the list which is '24689'.
So it maps that item as an integer.
Then the list() function stores this single item which is now an
integer in a list owned by the variable Lis. So you finally get
[24689]. And this is different from your [2,4,6,8,9] expectation.
As I've demonstrated above, so the cause of all this problem was your decision to use Split().
if you take away the split(), then;
the map() will no longer see just one Item but rather '2' '4' '6' '8'
'9' because a string '24689' is an iterable, with 5 individual items in
your case. So the map() runs over all these 5 items in your Numb string and
maps/transforms them to individual integers respectively. Then finally
the List() would have stored all these beautiful individual integers
in a list Owned by the variable Lis.And you get your desired [2,4,6,8,9] in return for the Variable Lis.
So the correct statement for that very line is:
lis = list(map(int, numb)) // This line should now produce [2,4,6,8,9]
The second problem of your error was the value you assigned to the index variable 'n' of list[n]. I assume you wanted to fetch the fifth item in the list. This 'n' is the linear count of items(in your case the individual numbers - 2,4,6,8,9). This counting start from 0 for the first letter instead of 1. Normally when you count items, the first letter is 1 but for index count in python list[n], the first letter is 0 instead of 1 so in your case the fifth item will be indexed as 4 since index count start from 0. So your 'n' input should be 4
These 2 solutions above solves your the specific error you mentioned.
BUT aside from your question above, your entire code will still run into another error even if the above solutions are implemented. This is Due to errors on these following lines as well:
On line 6:
for i in (0,len(lis)): //Wrong due to NO function name called before '(0, len(lis))'.
We will logically assume you want to use the range function
correct expression on LINE 6 is:
for i in range(0, len(lis)):
On Line 7:
if lis[i]=[a] //Three problems on this line...
First, you can't compare two objects with the operator '='. instead use '=='
Second, [a] is a meaningless value in your expression... Does comparing that to a number look right to you? I bet No. Remember [a] is not the same as the variable 'a'.
a = lis[n], while [a] = [lis[n]]
So the correct expression there should be 'a' and not '[a]'
Last, you need to add ':' after every "if", "for" or "while" expression..
Here is how LINE 7 should CORRECTLY look like,
if lis[i] == a:
All the above solutions should satisfy your need. So the complete correct code for your script should look like this:
n = int(input())
numb = input()
lis = list(map(int, numb))
lis.sort()
a = lis[n]
for i in range(0,len(lis)):
if lis[i] == a:
print (lis[i-1])

The number of differences between characters in a string in Python 3

Given a string, lets say "TATA__", I need to find the total number of differences between adjacent characters in that string. i.e. there is a difference between T and A, but not a difference between A and A, or _ and _.
My code more or less tells me this. But when a string such as "TTAA__" is given, it doesn't work as planned.
I need to take a character in that string, and check if the character next to it is not equal to the first character. If it is indeed not equal, I need to add 1 to a running count. If it is equal, nothing is added to the count.
This what I have so far:
def num_diffs(state):
count = 0
for char in state:
if char != state[char2]:
count += 1
char2 += 1
return count
When I run it using num_diffs("TATA__") I get 4 as the response. When I run it with num_diffs("TTAA__") I also get 4. Whereas the answer should be 2.
If any of that makes sense at all, could anyone help in fixing it/pointing out where my error lies? I have a feeling is has to do with state[char2]. Sorry if this seems like a trivial problem, it's just that I'm totally new to the Python language.
import operator
def num_diffs(state):
return sum(map(operator.ne, state, state[1:]))
To open this up a bit, it maps !=, operator.ne, over state and state beginning at the 2nd character. The map function accepts multible iterables as arguments and passes elements from those one by one as positional arguments to given function, until one of the iterables is exhausted (state[1:] in this case will stop first).
The map results in an iterable of boolean values, but since bool in python inherits from int you can treat it as such in some contexts. Here we are interested in the True values, because they represent the points where the adjacent characters differed. Calling sum over that mapping is an obvious next step.
Apart from the string slicing the whole thing runs using iterators in python3. It is possible to use iterators over the string state too, if one wants to avoid slicing huge strings:
import operator
from itertools import islice
def num_diffs(state):
return sum(map(operator.ne,
state,
islice(state, 1, len(state))))
There are a couple of ways you might do this.
First, you could iterate through the string using an index, and compare each character with the character at the previous index.
Second, you could keep track of the previous character in a separate variable. The second seems closer to your attempt.
def num_diffs(s):
count = 0
prev = None
for ch in s:
if prev is not None and prev!=ch:
count += 1
prev = ch
return count
prev is the character from the previous loop iteration. You assign it to ch (the current character) at the end of each iteration so it will be available in the next.
You might want to investigate Python's groupby function which helps with this kind of analysis.
from itertools import groupby
def num_diffs(seq):
return len(list(groupby(seq))) - 1
for test in ["TATA__", "TTAA__"]:
print(test, num_diffs(test))
This would display:
TATA__ 4
TTAA__ 2
The groupby() function works by grouping identical entries together. It returns a key and a group, the key being the matching single entry, and the group being a list of the matching entries. So each time it returns, it is telling you there is a difference.
Trying to make as little modifications to your original code as possible:
def num_diffs(state):
count = 0
for char2 in range(1, len(state)):
if state[char2 - 1] != state[char2]:
count += 1
return count
One of the problems with your original code was that the char2 variable was not initialized within the body of the function, so it was impossible to predict the function's behaviour.
However, working with indices is not the most Pythonic way and it is error prone (see comments for a mistake that I made). You may want rewrite the function in such a way that it does one loop over a pair of strings, a pair of characters at a time:
def num_diffs(state):
count = 0
for char1, char2 in zip(state[:-1], state[1:]):
if char1 != char2:
count += 1
return count
Finally, that very logic can be written much more succinctly — see #Ilja's answer.

Python memory error when searching substring

I am trying to find substring of very large string and getting memory error:
The code:
def substr(string):
le = []
st = list(string)
for s in xrange(len(string)+1):
for s1 in xrange(len(string)+1):
le.append(''.join(st[s:s1]))
cou = Counter(le)
cou_val = cou.keys()
cou_val.remove('')
return le, cou_val
I am getting error as ile "solution.py", line 31, in substr
le.append(''.join(st[s:s1]))
MemoryError
How to tackle this problem?
Answer
I noticed that your code prints all the possible substrings of string in a certain order. I suggest that instead of storing all of them in an array, you use code to return just the substring that you want. I tested the subroutine below with 'a very long string' and it always returns the same value as if you were to get an indexed value from an array.
string = 'a very long string'
def substr2(string,i):
return string[i//(len(string)+1):i%(len(string)+1)]
print(substr2(string,10))
Solution
The way you order the arguments for your for loops (s,s1) work similarly to a number system. s1 increments by 1 until it gets to a given value, then it resets to 0 and s increments by 1, repeating the cycle. This is seen in a decimal system (e.g. 01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,16 etc.)
The i//n div operator returns the integer value of i/n. (e.g. 14//10=1).
The i%n mod operator returns the remainder value of i/n. (e.g. 14%10 is 4).
So if we were to, for example, increment i by 1 and define (s,s1) as [i//10,i%10], we would get:
[0,0],[0,1],[0,2],[0,3],[0,4],[0,5],[0,6],[0,7],[0,8],[0,9],[1,0],[1,1],[1,2] etc.
We can utilize these to produce the same answer as in your array.
PS. My first answer. Hope this helps!
It seems that you are running out of memory. When the string is too large the code you posted seems to be copying it over and over again into the le list. As #Rikka's link suggests, buffer/memoryview may be of use for you but I have never used it.
As a workaround to your solution/code I would suggest that instead of storing each substring in le, store the indexes as a tuple. Additionally, I don't think that st list is required (not sure tho if your way speeds it up) so the result would be (code not tested):
def substr(string):
le = []
for s in xrange(len(string)+1):
for s1 in xrange(len(string)+1):
# Skip empty strings
if s!=s1:
le.append((s, s1))
cou = Counter(le)
cou_val = cou.keys()
cou_val.remove('')
return le, cou_val
Now, an example of how you can use the substr is (code not tested):
myString = "very long string here"
matchString = "here"
matchPos = False
indexes, count = substr(myString)
# Get all the substrings without storing them simultaneously in memory
for i in indexes:
# construct substring and compare
if myString[i[0],i[1]]==matchString:
matchPos = i
break
After the above you have start and end positions of the 1st occurrence of "here" into your large string. I am not sure what you try to achieve but this can easily be modified to find all occurrences, count matches, etc - I just post it as example. I am also not sure why the Counter is there...
This approach should not present the memory error, however, it is a trade-off between memory and CPU and I expect it to be bit slower on runtime since every time you use indexes you have to re-construct every substring.
Hope it helps
The solution:
The error in memory is always caused by out of range.And the slice technique also has some rules.
When the step is positive, just like 1, the first index must be greater than the second.And on the contrary, when negative, such as -1, the number of the index is shorter than the second, but it is actually the greater one.(-1 > -2)
So in your program, the index s is greater than s1 when step is one, so you access a place you have not applied for it.And you know, that is MemoryError!!!

My Python code is only selecting half of a list's contents?

I'm very new to Python, and I'm going through some example projects I found online but I'm stuck on my palindrome checker at the moment.
Right now, my code takes a word as an input, splits it in half, saves each part into separate variables, makes both of the variables lists, and from there it SHOULD reverse the second list so I can compare it to the first, but from what I've gathered trying to fix it, it's only appending half of the selection to the new list.
For example, if I enter "racecar", it'll split it into "race" and "ecar" just fine, but then when I go to reverse "ecar" it only gives me back "['c', 'e']". (Also, if I switch the variables around to reverse the first half, I get the same error)
I've been trying to figure it out for quite a while now and I'm not making any progress so some help would be very much appreciated!
Ninja Edit: If there's an easier way to do this (which I'm sure there is) I'd love to know, but I still want to figure out what I've done wrong in the code I already have so I can try to learn from it
Here's my code so far:
print "Please enter a word you want to check is a palindrome"
input = raw_input('> ')
#Gets lengths of input
full_length = len(input)
split_length = len(input) / 2
#If word has an even length split like this
if full_length % 2 == 0:
first_half = input[0: split_length]
second_half = input[split_length:full_length]
#If word does not have even length split like this
else:
first_half = input[0:split_length+1]
second_half = input[split_length:full_length]
#Make both halves lists
first_half_list = list(first_half)
print first_half_list
second_half_list = list(second_half)
print second_half_list
# Reverse second half
rev_second_half = []
for x in second_half_list:
current_letter = second_half_list[0]
second_half_list.remove(second_half_list[0])
rev_second_half.insert(0, current_letter)
print rev_second_half
"""
#Check to see if both lists are identical
#If they are identical
print "This word is a palindrome!"
#If they are not identical
print "This word is not a palindrome."
"""
And this is the output I get when I enter 'racecar':
racecar
['r','a','c','e']
['e','c','a','r']
['c', 'e']
There's a lot of unnecessary work going on. No need to convert to lists; the interpreter can manage this all for you. No need to manually reverse a string; use slicing. No need to manually declare the indices of the first and last characters in your string; the interpreter knows where they are. Here's a fixed version of the code; you can view a demo at IDE One:
input = 'racecar'
#Gets lengths of input
full_length = len(input)
split_length = len(input) / 2
#If word has an even length split like this
if full_length % 2 == 0:
first_half = input[:split_length]
second_half = input[split_length:]
#If word does not have even length split like this
else:
first_half = input[:split_length+1]
second_half = input[split_length:]
print first_half
print second_half
rev_second_half = second_half[::-1]
print rev_second_half
race
ecar
race
Notice the way that the second half is getting reversed, by using a slice with a negative iteration step? You can just do that once, to your source string, and compare the result to the original. Now you have a one line method to check if a string is a palindrome: input == input[::-1]
A bit more on slicing syntax (you might like to check out this question). input[::-1] is exactly the same as input[0:len(input):-1]. The colons separate the three arguments, which are start : end : step. The first two create a range which includes start and everything between it and end, but not end itself. Not specifying start or end causes the interpreter to assume you mean "use 0" and "use len", respectively. Not specifying step causes an assumption of 1. Using a negative step means "start at end and go backwards by magnitude of step".
If you want to omit arguments and specify a range with a slice, you need to include the colons, so the interpreter can tell which arguments are omitted. For example, input[-1] will return the last element of input, because no colons means you're specifying an index, and negative means "go backwards from the end", so print input[:-1] would yield "raceca" if your input was "racecar".
As for what was going wrong with your code, the problem is in your reversing loop.
for x in second_half_list:
current_letter = second_half_list[0]
second_half_list.remove(second_half_list[0])
rev_second_half.insert(0, current_letter)
You're removing items from the list you're iterating through. Don't do that, it's a great way to cause problems; it's why you're only getting half the list in this case. There's also needless copying going on, though that won't cause incorrect results. Finally, you're not using your iterated variable at all, which is a sure sign of some sort of problem with your loop code. Here, if you fixed the list mutation but continued using second_half_list[0], you'd get that letter repeated len(second_half_list) times. If you really need to actually reverse a list, you can do it like this instead:
for x in second_half_list:
rev_second_half.insert(0, x)
But you should only actually iterate the list if you need some sort of side effects during the iteration. For a pure reversal in python, you want this, which will perform better:
rev_second_half = [reversed(second_half_list)]
To reverse the string (not in place):
rev_second_half = second_half_list[::-1]
To extend:
I'd suggest keeping the halves as strings, as you can then just compare them with:== and the above reversing technique also works on strings.
The reason you're only getting two values is you're mutating your list while you iterate on it -- you just shouldn't do this, if only because it's a pain to reason about. As an example:
In [34]: nums = range(5) # [0, 1, 2, 3, 4]
In [35]: for num in nums:
....: print "num", num
....: print "nums", nums
....: nums.remove(nums[0])
....:
num 0
nums [0, 1, 2, 3, 4]
num 2
nums [1, 2, 3, 4]
num 4
nums [2, 3, 4]
Notice that this only looped three times. The first time through, everything's dandy, but you remove the first element. However, Python's looping logic thinks it has to go to the second item -- but you removed the first item! Does that mean the second item now, or the second item when things started? For Python's internals, it means the second item now -- which is the third item when things started (i.e. the value 2). From there, stuff just snowballs.
The lesson here is don't mutate a list while you iterate on it. Just use the other means for reversing folks have mentioned here.

Python function to sum digits

I want function to take the last digit of each number in the list and sum them up all together. So forexample, the function below would return "10".
def getSumOfLastDigits(numList):
x = sum(int(num[-1:]) for num in numList)
print x
getSumOfLastDigits([1, 23, 456])
>>>10
Here is what i receive instead of the expected out "10"
def getSumOfLastDigits(numList):
x = sum(int(num[-1:]) for num in numList)
print x
getSumOfLastDigits([1, 23, 456])
Too much work.
def getSumOfLastDigits(numList):
return sum(x % 10 for x in numList)
x = sum(num%10 for num in numList)
You can't index into a number; a number isn't a sequence of digits (internally, it isn't represented in base 10). You can obtain the last digit of a number using mathematical manipulation instead: take the remainder when dividing by 10. We do this with the % operator.
Also:
Don't print the value in your function, return it. Let the calling code decide what to do with the value. Calculation and output are separate tasks, and should be kept separate.
Avoid indicating data types in variable names - yes, even in Python. It's not a good idea to build in assumptions that aren't actually necessary. You could use any kind of sequence here, for example. The simplest way to indicate that you have more than one number is to use the plural, numbers. That also means you use a full word, and people don't have to think about what 'num' is short for.
There is no need to assign the result of an expression to a temporary variable, if you are just going to use it once, and right away. The name x doesn't tell us anything, so cut it out.
get is considered an ugly prefix for function names by most Pythonistas. It should already be obvious that the function calculates and returns a value. Use noun-type names for those functions, and verb-type names for functions that are primarily intended to manipulate some existing data.
Thus:
def sum_of_last_digits(numbers):
return sum(number % 10 for number in numbers)
def getSumOfLastDigits(numList):
total=0
for item in numList:
newItem=str(item)
length=newItem[len(newItem)-1]
total+=int(length)
return total

Categories