Given that I have boolean expressions which I know will evaluate to True that have a number of variables (1...n) and I also know that (1...n) of the variables are True, how can I determine which of the True values actually had an impact on the expression (i.e. causing it to evaluate it to True)?
Basic example: let's assume I have (A and B) or (C and D) with A = True, B = True and C = True. In this case the value of C is irrelevant for the evaluation of the boolean expression as the right part of the expression always evaluates to false given D = False.
I would need a method (e.g. find_relevant_values()) to determine that [A, B] are relevant.
A = True
B = True
C = True
D = False
bool_exp = "(A and B) or (C and D)"
print(eval(bool_exp)) #True
# is this doable?
print(find_relevant_values(bool_exp)) #[A, B]
I would need a generic way (e.g. find_relevant_values()) to determine the relevant values. In the example above [A, B] are the relevant values.
The solution below works in three steps: First, the input string is parsed to generate an expression tree. Second, the tree is traversed to find any instances of an or operator. If any of the or operands evaluate to True, the operand is either yielded if it is a terminal value (A, B, C, or D) or recursively traversed by find_relevant. Lastly, if no or operators are found, then the entire expression is evaluated and if the result is True, then the tree is traversed for all the relevant expressions that triggered the True value:
import re, collections as cl
m, op = {'A':True, 'B':True, 'C':True, 'D':False}, {'or':lambda x, y:x or y, 'and':lambda x, y:x and y}
#create the expression tree
def parse_expr(d):
while (n:=next(d, None)) not in {None, ')'}:
if n != '(':
yield n
else:
yield list(parse_expr(d))
#evaluate the tree
def rel_eval(expr):
while len(expr) > 1:
_a, b, _c = [expr.popleft() for _ in range(3)]
a = m.get(_a, _a) if not isinstance(_a, list) else rel_eval(collections.deque(_a))
c = m.get(_c, _c) if not isinstance(_c, list) else rel_eval(collections.deque(_c))
expr.appendleft(op[b](a, c))
return expr[0]
#search for the relevant parts of the expression
def find_relevant(d):
f = False
for i in range(len(d)-1):
if d[i] == 'or':
f = True
if (isinstance(d[i-1], list) and rel_eval(cl.deque(d[i-1]))) or (not isinstance(d[i-1], list) and m.get(d[i-1], False)):
yield from ([d[i-1]] if not isinstance(d[i-1], list) else find_relevant(d[i-1]))
break
elif (isinstance(d[i+1], list) and rel_eval(cl.deque(d[i+1]))) or (not isinstance(d[i+1], list) and m.get(d[i+1], False)):
yield from ([d[i+1]] if not isinstance(d[i+1], list) else find_relevant(d[i+1]))
break
if not f and rel_eval(cl.deque(d)):
for i in filter(lambda x:x != 'and' and x != 'or', d):
yield from ([i] if not isinstance(i, list) else find_relevant(i))
Putting it all together:
def find_relevant_values(s):
#parse the input and create the tree
r = list(parse_expr(iter(re.findall('\(|\)|\w+', s))))
#consume the generator results from find_revelant
return list(find_relevant(r))
print(find_relevant_values('(A and B) or (C and D)'))
print(find_relevant_values('(A and (B or C)) and (C or D)'))
Output:
['A', 'B']
['A', 'B', 'C']
Related
I don't understand this syntax.
Python program to demonstrate ternary operator
a, b = 10, 20
Use tuple for selecting an item
print( (b, a) [a < b] )
Use Dictionary for selecting an item
print({True: a, False: b} [a < b])
PS: I guess this is from older version of Python, because in newer versions (don't know from which version) True and False are reserved keywords, so they can't be assigned a value.
lamda is more efficient than above two methods
because in lambda we are assure that only one expression will be evaluated unlike in
tuple and Dictionary
print((lambda: b, lambda: a)[a < b]())
Syntax should be:
[on_true] if [expression] else [on_false]
So how does
print( (b, a) [a < b] )
print({True: a, False: b} [a < b])
print((lambda: b, lambda: a)[a < b]())
fit this syntax?
What's the meaning of [a<b] after tuple/dictionary/lambda? I have never seen this syntax before. It also works when list [b, a] precedes [a<b].
I would expect it to look like this
print( a if a < b else b )
Link to resource:
https://www.geeksforgeeks.org/ternary-operator-in-python/
First note that all these give the minimum value of a and b:
a, b = 10, 20
res1 = (b, a)[a < b] # 10
res2 = {True: a, False: b}[a < b] # 10
res3 = (lambda: b, lambda: a)[a < b]() # 10
We can consider these in turn:
res1 constructs a tuple of 2 integers. a < b returns a Boolean value, in this case True. In Python, True == 1, as bool is a subclass of int. Finally, [] is syntactic sugar for __getitem__, which is the method called for positional indexing. Since Python starts counting from 0, the first index is a. You can also confirm this yourself: (b, a).__getitem__(1) returns 10.
res2 constructs a dictionary mapping Boolean values to a & b. Calling __getitem__ on a dict object returns the value for a given key. Here the key, as before, is True. Since the dictionary maps True to a, this is the value returned.
res3 constructs a tuple of anonymous (lambda) functions, each returning scalars, namely b and a. As per res1, an item can be extracted from a tuple via integer indexing. The only additional requirement is to actually call the lambda functions via ().
Note none of these operate the same way as the ternary operator which is applied at compile time (see comments) via an in-line if / else:
res4 = a if a < b else b
a<b is a bool (True or False). Since bool is a subtype of int, it can be used in a context where an integer is expected, e.g. as a list/tuple index:
>>> True == 1
True
>>> False == 0
True
>>> isinstance(True, int)
True
>>> ('a', 'b')[True] # True == 1
'b'
>>> ('a', 'b')[1<2] # 1<2 == True == 1
'b'
>>> ('a', 'b')[2<1] # 2<1 == False == 0
'a'
For dict keys, it is similar, but the type coercion is not even needed:
>>> {True: 'a', False: 'b'}[1<2] # 1<2 == True
'a'
Your misconception might be regarding a < b .
In all these cases the boolean result of evaluating a < b is used as a key for the object in before.
In the case of
print( (b, a) [a < b] )
and
print((lambda: b, lambda: a)[a < b]())
the object is a tuple containing either the variables themselves or very simple anonymus functions, that return these variables.
In the case of
print({True: a, False: b} [a < b])
the expression is evaluated and used as a key for the dictionary, which has both True and False as keys. The assumption that this means, that it must be an older Python version is incorrect thoug, because a dictionary does not represent a reassignment of values, but is merely a data structure, where a key maps to a value. True and False are valid keys and this circumstance is precisely, what is being used here.
Finally:
print( a if a < b else b )
Is a nice and concise may of expressing the same thing and in fact the line of code I would use in this case
So I have been trying to solve the Easy questions on Leetcode and so far I dont understand most of the answers I find on the internet. I tried working on the Isomorphic strings problem (here:https://leetcode.com/problems/isomorphic-strings/description/)
and I came up with the following code
def isIso(a,b):
if(len(a) != len(b)):
return false
x=[a.count(char1) for char1 in a]
y=[b.count(char1) for char1 in b]
return x==y
string1 = input("Input string1..")
string2 = input("Input string2..")
print(isIso(string1,string2))
Now I understand that this may be the most stupid code you have seen all day but that is kinda my point. I'd like to know why this would be wrong(and where) and how I should further develop on this.
If I understand the problem correctly, because a character can map to itself, it's just a case of seeing if the character counts for the two words are the same.
So egg and add are isomorphic as they have character counts of (1,2). Similarly paper and title have counts of (1,1,1,2).
foo and bar aren't isomorphic as the counts are (1,2) and (1,1,1) respectively.
To see if the character counts are the same we'll need to sort them.
So:
from collections import Counter
def is_isomorphic(a,b):
a_counts = list(Counter(a).values())
a_counts.sort()
b_counts = list(Counter(b).values())
b_counts.sort()
if a_counts == b_counts:
return True
return False
Your code is failing because here:
x=[a.count(char1) for char1 in a]
You count the occurrence of each character in the string for each character in the string. So a word like 'odd' won't have counts of (1,2), it'll have (1,2,2) as you count d twice!
You can use two dicts to keep track of the mapping of each character in a to b, and the mapping of each character in b to a while you iterate through a, and if there's any violation in a corresponding character, return False; otherwise return True in the end.
def isIso(a, b):
m = {} # mapping of each character in a to b
r = {} # mapping of each character in b to a
for i, c in enumerate(a):
if c in m:
if b[i] != m[c]:
return False
else:
m[c] = b[i]
if b[i] in r:
if c != r[b[i]]:
return False
else:
r[b[i]] = c
return True
So that:
print(isIso('egg', 'add'))
print(isIso('foo', 'bar'))
print(isIso('paper', 'title'))
print(isIso('paper', 'tttle')) # to test reverse mapping
would output:
True
False
True
False
I tried by creating a dictionary, and it resulted in 72ms runtime.
here's my code -
def isIsomorphic(s: str, t: str) -> bool:
my_dict = {}
if len(s) != len(t):
return False
else:
for i in range(len(s)):
if s[i] in my_dict.keys():
if my_dict[s[i]] == t[i]:
pass
else:
return False
else:
if t[i] in my_dict.values():
return False
else:
my_dict[s[i]] = t[i]
return True
There are many different ways on how to do it. Below I provided three different ways by using a dictionary, set, and string.translate.
Here I provided three different ways how to solve Isomorphic String solution in Python.
from itertools import zip_longest
def isomorph(a, b):
return len(set(a)) == len(set(b)) == len(set(zip_longest(a, b)))
here is the second way to do it:
def isomorph(a, b):
return [a.index(x) for x in a] == [b.index(y) for y in b]
I'm currently working my way through a "List" unit and in one of the exercises we need to create an anagram (for those who don't know; two words are an anagram if you can rearrange the letters from one to spell the other).
The easier solution that comes to mind is:
def is_anagram(a, b):
return sorted(a) == sorted(b)
print is_anagram('god', 'dog')
It works, but it doesn't really satisfy me. If we were in this situation for example:
def is_anagram(a, b):
return sorted(a) == sorted(b)
print is_anagram('god', 'dyog') #extra letter 'd' in second argument
>>> False
Return is False, although we should be able to to build the word 'god' out of 'dyog'. Perhaps this game/problem isn't called an anagram, but I've been trying to figure it out anyway.
Technically my solution is to:
1- Iterate through every element of b.
2- As I do, I check if that element exists in a.
3- If all of them do; then we can create a out of b.
4- Otherwise, we can't.
I just can't seem to get it to work. For the record, I do not know how to use lambda
Some tests:
print is_anagram('god', 'ddog') #should return True
print is_anagram('god', '9d8s0og') #should return True
print is_anagram('god', '_#10d_o g') #should return True
Thanks :)
Since the other answers do not, at time of writing, support reversing the order:
Contains type hints for convenience, because why not?
# Just strip hints out if you're in Python < 3.5.
def is_anagram(a: str, b: str) -> bool:
long, short = (a, b) if len(a) > len(b) else (b, a)
cut = [x for x in long if x in short]
return sorted(cut) == sorted(short)
If, in future, you do learn to use lambda, an equivalent is:
# Again, strip hints out if you're in Python < 3.5.
def is_anagram(a: str, b: str) -> bool:
long, short = (a, b) if len(a) > len(b) else (b, a)
# Parentheses around lambda are not necessary, but may help readability
cut = filter((lambda x: x in short), long)
return sorted(cut) == sorted(short)
If you need to check if a word could be created from b you can do this
def is_anagram(a,b):
b_list = list(b)
for i_a in a:
if i_a in b_list:
b_list.remove(i_a)
else:
return False
return True
UPDATE(Explanation)
b_list = list(b) makes a list of str objects(characters).
>>> b = 'asdfqwer'
>>> b_list = list(b)
>>> b_list
['a', 's', 'd', 'f', 'q', 'w', 'e', 'r']
What basically is happening in the answer: We check if every character in a listed in b_list, when occurrence happens we remove that character from b_list(we do that to eliminate possibility of returning True with input 'good', 'god'). So if there is no occurrence of another character of a in rest of b_list then it's not advanced anagram.
def is_anagram(a, b):
test = sorted(a) == sorted(b)
testset = b in a
testset1 = a in b
if testset == True:
return True
if testset1 == True:
return True
else:
return False
No better than the other solution. But if you like verbose code, here's mine.
Try that:
def is_anagram(a, b):
word = [filter(lambda x: x in a, sub) for sub in b]
return ''.join(word)[0:len(a)]
Example:
>>> is_anagram('some','somexcv')
'some'
Note: This code returns the common word if there is any or '' otherwise. (it doesn't return True/False as the OP asked - realized that after, but anyway this code can easily change to adapt the True/False type of result - I won't fix it now such there are great answers in this post that already do :) )
Brief explanation:
This code takes(filter) every letter(sub) on b word that exists in a word also. Finally returns the word which must have the same length as a([0:len(a)]). The last part with len needs for cases like this:
is_anagram('some','somenothing')
I think all this sorting is a red herring; the letters should be counted instead.
def is_anagram(a, b):
return all(a.count(c) <= b.count(c) for c in set(a))
If you want efficiency, you can construct a dictionary that counts all the letters in b and decrements these counts for each letter in a. If any count is below 0, there are not enough instances of the character in b to create a. This is an O(a + b) algorithm.
from collections import defaultdict
def is_anagram(a, b):
b_dict = defaultdict(int)
for char in b:
b_dict[char] += 1
for char in a:
b_dict[char] -= 1
if b_dict[char] < 0:
return False
return True
Iterating over b is inevitable since you need to count all the letters there. This will exit as soon as a character in a is not found, so I don't think you can improve this algorithm much.
I would like to test multiple inequalities at once, i.e.
if (a < b < c < ...)
which is fine when all the values are present. However sometimes the numeric value of one or more of the variables to be compared may be missing/unknown; the correct behaviour in my context is to assume the associated inequality is satisfied. Let's say I assign the special value None when the value is unknown: the behaviour I want from the < operator (or an alternative) is:
>>> a = 1; b = 2; c = 3
>>> a < b < c # this works fine, obviously
True
>>> b = None
>>> a < b < c # would like this to return True
False
So I want to get True if one variable is truly smaller than the other, or if one variable is missing (takes any particular pre-decided non-numerical value), or if both variables are missing, and I want to be able to string the comparisons together one go i.e. a < b < c < ...
I would also like to do this with <= as well as <.
Thanks
You want to test if your sequence – bar the undefined values – is in ascending order:
import operator
def isAscending(strictly, *seq):
cmp_op = operator.lt if strictly else operator.le
seq = [e for e in seq if e is not None]
return all(cmp_op(a, b) for a, b in zip(seq, seq[1:]))
a, b, c = 1, None, 2
print isAscending(True, a, b, c) # strictly ascending ?
Edited for spelling, and to use comparison operators as suggested.
This looks like you are actually trying to test if your values are unique and in sorted order which could be replaced by something like:
>>> def my_test(l):
>>> filt_l = [v for v in l if not v is None]
>>> return (sorted(filt_l) == filt_l) and (len(filt_l) == len(set(filt_l)))
>>> my_test([1,2,3])
True
>>> my_test([1,None,3])
True
>>> my_test([1,4,3])
False
>>> my_test([1,1,3])
False
Edit: including timings it seems that the function suggested by sebdelsol is even faster
>>> %timeit isAscending([int(1000*random.random()) for i in xrange(10000)])
100 loops, best of 3: 3.44 ms per loop
>>> %timeit my_test([int(1000*random.random()) for i in xrange(10000)])
100 loops, best of 3: 5.67 ms per loop
You could create your own type overloading comparison methods (as in this question: python overloading operators)
E.g.
class N(object):
def __init__(self, value):
self.value = value
def __lt__(self, other):
return (self.value is None or self.value < other.value)
...
a = N(1); b = N(None); c = N(3)
print a < b < c
If you have your values in a list ([a, b, c]), then you can filter the None values from it, pair them up using zip(), apply the operator to all the pairs and see if all them hold.
In code:
import operator # For operator.lt, which is < ("less than")
def mass_comparify(op, *args):
return all(op(a, b) for a, b in zip(args, args[1:])
if a is not None and b is not None)
print(mass_comparify(operator.lt, 1, None, 3)) # Prints True because 1 < 3
I don't think you have a better option than to define a comparison function which does the comparison as you want, and then write the inequalities as
comp(a,b) and comp(b,c) and ...
I don't know if it fits perfectly, but you can use reduce:
>>> import operator
>>> reduce(operator.__lt__, [1, None, 3])
True
>>> reduce(operator.__lt__, [1, None, 0])
False
or, much more solid, as it explicitly ignores None values:
>>> import operator
>>> reduce(operator.__lt__, filter(None, [1, None, 3]))
True
>>> reduce(operator.__lt__, filter(None, [1, None, 0]))
False
guys, I wrote a function to test if two inputs (a and b) have the same data structure, this way:
print same_structure([1, 0, 1], [a, b, c])
#>>> True
#print same_structure([1, [0], 1], [2, 5, 3])
>>> False
#print same_structure([1, [2, [3, [4, 5]]]], ['a', ['b', ['c', ['d', 'e']]]])
>>> True
#print same_structure([1, [2, [3, [4, 5]]]], ['a', ['b', ['c', ['de']]]])
>>> False
This function has (in my implementation) to use recursion. I'm very beginner in recursion and I still have difficulty on thinking recursively. In addition, to avoid cheating the answer, I want to use my own (messy) code and through it learn to use the recursive call (using this code line: 'same_structure return (a [i], b [e])' properly). Could someone show how to properly use the recursion in code below?
Thanks in advance for any help!!!
def is_list(p):
return isinstance(p, list)
def same_structure(a,b):
if not is_list(a) and not is_list(b):
print '#1'
return True
else:
if is_list(a) and is_list(b):
print '#2'
if len(a) != len(b):
print '#3'
return False
if len(a) == len(b):
print '#4'
for e in range(len(a)):
print 'e = ', e, 'a[e]= ', a[e], 'b[e]=', b[e]
return same_structure(a[e], b[e])
else:
return False
The following works:
def same_structure(a, b):
if isinstance(a, list) and isinstance(b, list) and len(a) == len(b):
return all(same_structure(A, B) for A, B in zip(a, b))
return (not isinstance(a, list) and not isinstance(b, list))
When writing recursive functions, you first need to come up with the base case, where you just return a value instead of calling any recursion. The base case here is one of the following conditions:
a is a list and b isn't, or vice-versa: return False
a and b are both lists, but they have different lengths: return False
neither a or b are lists: return True
If a and b are both lists and they have the same length, we need to now check each element of these lists recursively. zip(a, b) provides a convenient way to iterate over the elements from each list together, and if the result of same_structure() is False for any two sub-elements, we want the entire function to return False. This is why all() is used, if you are unfamiliar with all() it is equivalent (but more efficient) to the following loop:
match = True
for A, B in zip(a, b):
if not same_structure(A, B):
match = False
break
return match
Here is how you could rewrite your function without changing too much, the logic is actually very similar to my solution, but just below the print '#4' you were returning from that loop too early:
def same_structure(a,b):
if not is_list(a) and not is_list(b):
print '#1'
return True
else:
if is_list(a) and is_list(b):
print '#2'
if len(a) != len(b):
print '#3'
return False
if len(a) == len(b):
print '#4'
for e in range(len(a)):
print 'e = ', e, 'a[e]= ', a[e], 'b[e]=', b[e]
if not same_structure(a[e], b[e]):
return False
return True
else:
return False
Try this solution, it works with all your examples and it's written in a recursive, functional-programming style but without using zip, all, etc. only slices to reduce the size of the list at each step:
def same_structure(a, b):
if a == [] or b == []:
return a == b
elif is_list(a[0]) != is_list(b[0]):
return False
elif not is_list(a[0]):
return same_structure(a[1:], b[1:])
else:
return same_structure(a[0], b[0]) and same_structure(a[1:], b[1:])
You're only doing the first recursive call, since you're returning right away.
If I understood what you want to do, you need to is call same_structure with the sub-element, and check it's return value (not Return it immediately). If the result of any of the calls is false, return false, otherwise, return true.