Checking words using "YNEOS" - python-3.x

In this problem, I take two strings from the user, the first string being s and the second string being t. If t is the reverse of s, I print "YES" else I print "NO".
Here is my code which gives me expected outputs:
s = input()
t = input()
if t == s[::-1]:
print("YES")
else:
print("NO")
But I found another approach that I am curious to understand, but the slicing part is making me confused. Here the code goes:
print("YNEOS"[input()!=input()[::-1]::2])
Trying to find a good explanation, so StackOverflow is what came to my mind before anything else.

Let's first extract the parts of that expression that concern the input/output and the string reversal. We then get this solution:
s = input()
t = input()
trev = t[::-1]
result = "YNEOS"[s != trev::2]
print(result)
The focus of the question is on the expression "YNEOS"[s != trev::2]
Now we get to the "trick" that is performed here. The expression s != trev can be either False or True. This boolean value becomes the first part in the slicing. You'd expect to have the start index of the slice at this position. But the boolean value will also work, as booleans are a subclass of integers, and False is 0 and True is 1. So the above expression evaluates to either:
"YNEOS"[0::2]
or
"YNEOS"[1::2]
The 2 serves as the step, and so "YNEOS"[0::2] will take the characters at indices 0, 2 and 4 ("YES"), while "YNEOS"[1::2] takes the characters at indices 1 and 3 ("NO").
I hope this clarifies it.

Related

Palindrome problem - Trying to check 2 lists for equality python3.9

I'm writing a program to check if a given user input is a palindrome or not. if it is the program should print "Yes", if not "no". I realize that this program is entirely too complex since I actually only needed to check the whole word using the reversed() function, but I ended up making it quite complex by splitting the word into two lists and then checking the lists against each other.
Despite that, I'm not clear why the last conditional isn't returning the expected "Yes" when I pass it "racecar" as an input. When I print the lists in line 23 and 24, I get two lists that are identical, but then when I compare them in the conditional, I always get "No" meaning they are not equal to each other. can anyone explain why this is? I've tried to convert the lists to strings but no luck.
def odd_or_even(a): # function for determining if odd or even
if len(a) % 2 == 0:
return True
else:
return False
the_string = input("How about a word?\n")
x = int(len(the_string))
odd_or_even(the_string) # find out if the word has an odd or an even number of characters
if odd_or_even(the_string) == True: # if even
for i in range(x):
first_half = the_string[0:int((x/2))] #create a list with part 1
second_half = the_string[(x-(int((x/2)))):x] #create a list with part 2
else: #if odd
for i in range(x):
first_half = the_string[:(int((x-1)/2))] #create a list with part 1 without the middle index
second_half = the_string[int(int(x-1)/2)+1:] #create a list with part 2 without the middle index
print(list(reversed(second_half)))
print(list(first_half))
if first_half == reversed(second_half): ##### NOT WORKING BUT DONT KNOW WHY #####
print("Yes")
else:
print("No")
Despite your comments first_half and second_half are substrings of your input, not lists. When you print them out, you're converting them to lists, but in the comparison, you do not convert first_half or reversed(second_half). Thus you are comparing a string to an iterator (returned by reversed), which will always be false.
So a basic fix is to do the conversion for the if, just like you did when printing the lists out:
if list(first_half) == list(reversed(second_half)):
A better fix might be to compare as strings, by making one of the slices use a step of -1, so you don't need to use reversed. Try second_half = the_string[-1:x//2:-1] (or similar, you probably need to tweak either the even or odd case by one). Or you could use the "alien smiley" slice to reverse the string after you slice it out of the input: second_half = second_half[::-1].
There are a few other oddities in your code, like your for i in range(x) loop that overwrites all of its results except the last one. Just use x - 1 in the slicing code and you don't need that loop at all. You're also calling int a lot more often than you need to (if you used // instead of /, you could get rid of literally all of the int calls).

How to convert a conditional statement to a simple expression? Is compounding a return this way Acceptable practice?

The exercise I am doing was given by a book that takes a dictionary argument and asks for me to give a return value of True or False. I am new to Python 3 and as a personal exercise for learning I want to convert all the conditions of a "valid dictionary as a chessboard into" a single return value. I haven't actually tested this code for errors as it isn't finished, but I did run it through an online validator I found https://extendsclass.com/python-tester.html.
I want to know how I can convert the following 2 code blocks into simple expressions to be used in the return statement in my function below, You can see below that I've converted most expressions into the return value already with "and" because "ALL expressions must == True"
for pieces in dictionary.values():
if all(pieces.startswith('b')) or \
all(pieces.startswith('w')):
return True
else:
return False
The above code block loops through the dictionary keys passed to function as "pieces",
and compares each key individually to determine if it starts with a value of 'b' or 'w'. So if any key does not start with 'b' or 'w' the dictionary "chessboard" is false as it contains an improper piece. Ok I think I see an error in this I'm going to look into it and try to figure it out. Ok I noticed some errors in the above code that need to be addressed I am currently researching how to properly execute the above code.
for square in dictionary:
try:
if int(square[:0]) <= 8 and \
square[-1] <= 'h':
return True
else:
return False
except ValueError:
return False
I worked on the above code block a very long time and am still not sure that's the "best" implementation of what I want it to do. But I am still new and did my best.
Anyway it slices the dictionary key and compares the first char in the key to make sure it isn't over 8 which is the maximum "valid range" if over valid range it returns false and anything not int is obviously automatically False and returned as such by the "exception".
Then it slices the dictionary key to get the last char of the dictionary key and compares it to <= 'h' as that is the valid range and anything over 'h' or not a valid type value will return as False.
And then it compares the results of True/False "and" True/False with "and" because both conditions must be True.
Here is the function as it currently is with a test dictionary at the end:
def cBV(dic): # (c)hess(B)oard(V)alidator
Err = 'Error: Invalid Board -'
if not isinstance(dic, type({})):
raise TypeError('Object passed to cBV is not of type <class dict>')
chess_pieces = {'bk pieces': 0, 'wh pieces': 0,
'bk pawns': 0, 'wh pawns': 0}
# converts dictionary values into keys and assigns those keys "counn of values"
for squares, pieces in dic.items:
if pieces.startswith('b'): # alt if pieces[:0] == 'b':
if pieces.startswith('bpawn'): # alt if pieces == 'bpawn':
chess_pieces['bk pawns'] += 1
chess_pieces['bk pieces'] += 1
elif pieces.startswith('w'):
if pieces.startswith('wpawn'):
chess_pieces['wh pawns'] += 1
chess_pieces['wh pieces'] += 1
return 'wking' in dic.values() and \
'bking' in dic.values() and \
chess_pieces['bk pieces'] <= 16 and \
chess_pieces['wh pieces'] <= 16 and \
chess_pieces['bk pawns'] <= 8 and \
chess_pieces['wh pawns'] <= 8 and \
dict = {'8h': 'wking', '2c': 'bking', '3a': 'wpawn', '3b': 'wpawn', '3c': 'wpawn',
'3d': 'wpawn', '3e': 'wpawn', '3f': 'wpawn', '3g': 'wpawn', '3h': 'wpawn', '4b': 'wpawn'}
test = cBV(dict)
print('Is this a valid chessboard? ' + str(test))
What you have now is good, and you should feel proud - there are more fancy techniques for making things more concise, and you'll get more used to them as you wrap your head around how the various data structures work.
for pieces in dictionary.values()
if pieces.startswith('b') or \
pieces.startswith('w'):
return True
else:
return False
can be converted to the one-liner
return all(
piece.startswith('b') or piece.startswith('w')
for piece in dictionary.values()
)
which does a few things.
The all() function takes any iterable object, and returns True if all of the values in that iterable are truthy. If even one of them is not, then it 'short-circuits' and returns False instead.
As our argument to all(), we give a "comprehension". A comprehension is essentially a one-line for loop, of the form f(element) for element in iterable: it performs whatever f(element) is, for every element in the iterable.
In our case, the iterable is dictionary.values(), which returns all of the values (but not the keys) in dictionary (which, here, is 'wking', 'wpawn', ...). In this case, these are strings.
piece is what we assign each element of, for each 'iteration' of the comprehension. It'll run for piece = 'wking', then for piece = 'wpawn', etc.
piece.startswith('b') or piece.startswith('w') is the function that we perform for each piece. This outputs either True or False, depending on whether the conditions are met.
You can wrap a comprehension in square-brackets [] to have it output as a regular list. However, if you give a comprehension as an argument to a function like all(), which is what we're doing here, then it will end up as a "generator", a slightly more efficient object that only calculates one object at a time. For our purposes, this isn't important.
The comprehension, overall, produces a series containing either True or False, that all() will consume.
Similarly with your second code snippet. You have the basics down, but can be more concise. Your code:
def allSquaresAreInRange(dictionary):
for square in dictionary:
try:
if int(square[:0]) <= 8 and \
pieces[-1] <= 'h':
return True
else:
return False
except ValueError:
return False
can be turned into
def allSquaresAreInRange(dictionary):
try:
return all(
(1 <= int(row) <= 8) and ('a' <= col <= 'h')
for (row, col) in dictionary
)
except ValueError:
return False
Here we make use of a few things:
As before, we use all(), and as before, we use a comprehension. But this time, we iterate through dictionary directly
Iterating through a dict is functionally identical to iterating through dict.keys(). So, we're iterating through the keys '8h', '2c', ...
Each key is a two-character string. Strings are iterable, just like lists are, and most iterables have an interesting property called "multiple assignment": if we assign exactly as many variables as the iterable has elements, then those elements get split up.
(row, col) = '8h', for example, is functionally identical to row = '8h'[0] and col = '8h'[1]. In both cases, we're left with row = '8' and col = 'h'.
This produces a ValueError if the number of elements on either side is mismatched - for example, if the key has only one character, or only three characters. A byproduct of this is that row and col are guaranteed to be exactly one-character long strings, if that error doesn't happen.
Our condition checks if the row is between 1 and 8, and whether the col is between A and H, using greater than/less than signs. This returns True or False, once again.
As you seemed to discover, using int() on something that doesn't represent an integer will also throw a ValueError.
This new snippet keeps the try/except blocks you came up with in yours, because they work just fine.
Python has a bit of a culture surrounding it that prides 'efficiently-written' code. Which is to say, code that looks as fancy as possible, and follows Functional Programming paradigms. Comprehensions, as well as all() and any(), are a big part of that, and so they're probably the 'correct' solution for any problem which they are a solution for (if they can be written concisely).
Similarly, the snippet
if condition:
return True
else:
return False
can almost always be condensed to
return condition
(or return bool(condition), in the case that condition deals with a value that has truthiness but isn't a boolean, such as None or an empty list or string). If this is applicable, it's good practice (but again, it's not always applicable).
The most important thing, though, is that your code works the way you want it to, and that it's clear enough for you to come back to it a few months down the line and figure out what you were doing, and why you were doing it that way. There are some cases where things can be written as comprehensions but that makes them extremely complicated and unreadable - and in those cases, it's sometimes a good idea to not write them as comprehensions, and do it the more verbose way. Just keep that in mind as you're continuing to develop, and you'll do fine.

Find a repeated character in a string (python)

I managed to find this code online which showed me how to find and print a repeated character in a string. I'm confused as to how it's working though. I don't understand what the h[i] = 0 part is technically doing. Can someone please explain?
a = 'abcdeab'
h = {}
for i in a:
if i in h:
print(i)
else:
h[i] = 0
I understand how it's iterating over the string, but I don't understand how it's being added to the dictionary in order to be checked if it already exists in that dictionary or not. Setting h[i] = 0 is what's throwing me off. I don't understand why it's being set to 0.
I'm adding this after the problem was answered:
I ended up creating a different solution and thought I would post it in case anyone else was looking into the same problem. It is as follows (using a list instead of a dictionary):
a = 'abcdeab'
h = []
for i in a:
if i in h:
print(i)
else:
h.append(i)
Also, if you're looking for ONLY the first occurrence of a repeated character, you would add break after print(i). In this case, it would only print a instead of both a and b.
The variable h has been defined to be a dictionary. For each letter in your input string, if it be present in the map, it gets printed, otherwise the map gets assigned a (key, value) pair of (letter, 0). That is, an entry is made into the map with the letter as the key, and zero as the (arbitrary) value. Here is your loop with some comments:
for i in a:
if i in h: # if the key 'i' exists in the dictionary
print(i)
else:
h[i] = 0 # otherwise add an entry for this letter

Find characters inside strings from the elements of a list in python

I just started to use python 3. I want to find specific characters inside a string that is part of a list. Here is my code:
num = ["one","two","threex"]
for item in num:
if item.find("x"):
print("found")
So, I want to print "found" if the character "x" is inside of one of the elements of the list. But when I run the code, it prints 3 times instead of one.
Why is printing 3 times? Can someone help me?
find() returns -1 if the character is not found in the string. Anything that is not zero is equal to True. try if item.find("x") > -1.
You can use in again for strings:
num = ["one","two","threex"]
for item in num:
if "x" in item:
print("found")
Think in Strings as a list of chars like "ext" -> ['e', 'x', 't']
so "x" in "extreme" is True
find returns Index if found and -1 otherwise.
num = ["one","two","threex"]
for item in num:
if item.find("x"):
print item.find("x")
i hope that you got the solution from above post ,here you know the reason why
You need to break out of looping through the strings if 'x' is found as otherwise, it may be found in other strings. Also, when checking if 'x' is in the string, use in instead.
num = ["one","two","threex"]
for item in num:
if "x" in item:
print("found")
break
which outputs:
found
And if I modify the num list so that it has no x in any of the elements:
num = ["one","two","three"]
then there is no output when running the code again.
But why was it printing 3 times before?
Well simply, using item.find("x") will return an integer of the index of 'x' in the string. And the problem with evaluating this with an if-statement is that an integer always evaluates to True unless it is 0. This means that every string in the num list passed the test: if item.find("x") and so for each of the 3 strings, found was printed. In fact, the only time that found wouldn't be printed would be if the string began with an 'x'. In which case, the index of 'x' would be 0 and the if would evaluate to False.
Hope this clears up why your code wasn't working.
Oh, and some examples of testing the if:
>>> if 0:
... print("yes")
...
>>> if 1:
... print("yes")
...
yes
>>> if -1:
... print("yes")
...
yes

Error: maximum recursion depth exceeded in comparison

I am trying to find a character in an alphabetized string... Here is the code
def isIn(char, aStr):
middleChar = len(aStr)//2
if char == aStr[middleChar]:
return True
elif char < aStr[middleChar]:
LowerHalf = aStr[:middleChar]
return isIn(char, LowerHalf)
elif char > aStr[middleChar]:
UpperHalf = aStr[middleChar:]
return isIn(char, UpperHalf)
else:
return False
print(isIn('a', 'abc'))
It returns True. But When I put
print(isIn('d', 'abc'))
it returns this error: maximum recursion depth exceeded in comparison; instead of False.
I don't understand whats wrong. Please tell me where is the logical mistake I am doing.
With d, The program splits the string from abc and picks out UpperHalf bc. Then it searches the new string bc. It then returns 'c' from 'bc' as expected. Since d > c, the program goes chooses that condition and once again returns the upper half of string 'c', which is c. Hence the recursion. To fix this, you need a separate way of handling length 1 strings.
The last else is useless - it will never be executed.
The end of binary search is when the array becomes of one item - if this item isn't the searched one, the searched item isn't in the array.

Resources