Concise way for wildcard match among objects? - python

I have a Match class which needs to implement a wildcard match method. This method will check all the attributes of the class with another object of the same class. If both are same or either is a * then it is a match. This logic is true for all the attributes in a class.
Please refer to the implementation of the wildcard_match method below.
The problem is if I add more attributes to the class, or if the number of attributes is large, I need to manually keep on adding to the method. So I need a concise, DRY way of implementing the method.
Any help is appreciated.
class Match:
def __init__(self):
self.src = "h%s" % random.randint(1, SRC)
self.dst = "h%s" % random.randint(1, DST)
self.proto = random.choice(L4_PROTO)
self.src_port = str(random.randint(2000, 5000))
self.dst_port = random.choice(L4_PORTS)
def __members(self):
return (self.src, self.dst, self.proto, self.src_port, self.dst_port)
def __hash__(self):
return hash(self.__members())
def __eq__(self, other):
""" Exact match check """
if isinstance(other, self.__class__):
return self.__members() == other.__members()
else:
return False
def wildcard_match(self, other):
""" Check whether the two matches are a wildcard match """
if isinstance(other, self.__class__):
if self.src != "*" and other.src != "*" and self.src != other.src:
return False
if self.dst != "*" and other.dst != "*" and self.dst != other.dst:
return False
if self.proto != "*" and other.proto != "*" and self.proto != other.proto:
return False
if self.src_port != "*" and other.src_port != "*" and self.src_port != other.src_port:
return False
if self.dst_port != "*" and other.dst_port != "*" and self.dst_port != other.dst_port:
return False
return True
else:
return False

You can use your classes __dict__ which contains all attributes you defined:
def wildcard_match(self, other):
""" Check whether the two matches are a wildcard match """
if isinstance(other, self.__class__):
for attr_name in self.__dict__:
self_attr = self.__getattr__(attr_name)
other_attr = other.__getattr__(attr_name)
if self_attr != "*" and other_attr != "*" and self_attr != other_attr:
return False
return True
else:
return False

Related

CS50's Introduction to Artificial Intelligence with Python - Knowledge

I'm studyng Harvard's Introduction to Artificial Intelligence with Python course. I'm enjoying a lot. However I downloaded logic file to use Boolean algebra and Knowledge, that simple operations (OR,AND,NOT...) Before I show my doubt I will share the Knowledge class from harvard source code, I hope there isn't issues on it:
link to this class:
Harvard class
logic.py
import itertools
class Sentence():
def evaluate(self, model):
"""Evaluates the logical sentence."""
raise Exception("nothing to evaluate")
def formula(self):
"""Returns string formula representing logical sentence."""
return ""
def symbols(self):
"""Returns a set of all symbols in the logical sentence."""
return set()
#classmethod
def validate(cls, sentence):
if not isinstance(sentence, Sentence):
raise TypeError("must be a logical sentence")
#classmethod
def parenthesize(cls, s):
"""Parenthesizes an expression if not already parenthesized."""
def balanced(s):
"""Checks if a string has balanced parentheses."""
count = 0
for c in s:
if c == "(":
count += 1
elif c == ")":
if count <= 0:
return False
count -= 1
return count == 0
if not len(s) or s.isalpha() or (
s[0] == "(" and s[-1] == ")" and balanced(s[1:-1])
):
return s
else:
return f"({s})"
class Symbol(Sentence):
def __init__(self, name):
self.name = name
def __eq__(self, other):
return isinstance(other, Symbol) and self.name == other.name
def __hash__(self):
return hash(("symbol", self.name))
def __repr__(self):
return self.name
def evaluate(self, model):
try:
return bool(model[self.name])
except KeyError:
raise Exception(f"variable {self.name} not in model")
def formula(self):
return self.name
def symbols(self):
return {self.name}
class Not(Sentence):
def __init__(self, operand):
Sentence.validate(operand)
self.operand = operand
def __eq__(self, other):
return isinstance(other, Not) and self.operand == other.operand
def __hash__(self):
return hash(("not", hash(self.operand)))
def __repr__(self):
return f"Not({self.operand})"
def evaluate(self, model):
return not self.operand.evaluate(model)
def formula(self):
return "¬" + Sentence.parenthesize(self.operand.formula())
def symbols(self):
return self.operand.symbols()
class And(Sentence):
def __init__(self, *conjuncts):
for conjunct in conjuncts:
Sentence.validate(conjunct)
self.conjuncts = list(conjuncts)
def __eq__(self, other):
return isinstance(other, And) and self.conjuncts == other.conjuncts
def __hash__(self):
return hash(
("and", tuple(hash(conjunct) for conjunct in self.conjuncts))
)
def __repr__(self):
conjunctions = ", ".join(
[str(conjunct) for conjunct in self.conjuncts]
)
return f"And({conjunctions})"
def add(self, conjunct):
Sentence.validate(conjunct)
self.conjuncts.append(conjunct)
def evaluate(self, model):
return all(conjunct.evaluate(model) for conjunct in self.conjuncts)
def formula(self):
if len(self.conjuncts) == 1:
return self.conjuncts[0].formula()
return " ∧ ".join([Sentence.parenthesize(conjunct.formula())
for conjunct in self.conjuncts])
def symbols(self):
return set.union(*[conjunct.symbols() for conjunct in self.conjuncts])
class Or(Sentence):
def __init__(self, *disjuncts):
for disjunct in disjuncts:
Sentence.validate(disjunct)
self.disjuncts = list(disjuncts)
def __eq__(self, other):
return isinstance(other, Or) and self.disjuncts == other.disjuncts
def __hash__(self):
return hash(
("or", tuple(hash(disjunct) for disjunct in self.disjuncts))
)
def __repr__(self):
disjuncts = ", ".join([str(disjunct) for disjunct in self.disjuncts])
return f"Or({disjuncts})"
def evaluate(self, model):
return any(disjunct.evaluate(model) for disjunct in self.disjuncts)
def formula(self):
if len(self.disjuncts) == 1:
return self.disjuncts[0].formula()
return " ∨ ".join([Sentence.parenthesize(disjunct.formula())
for disjunct in self.disjuncts])
def symbols(self):
return set.union(*[disjunct.symbols() for disjunct in self.disjuncts])
class Implication(Sentence):
def __init__(self, antecedent, consequent):
Sentence.validate(antecedent)
Sentence.validate(consequent)
self.antecedent = antecedent
self.consequent = consequent
def __eq__(self, other):
return (isinstance(other, Implication)
and self.antecedent == other.antecedent
and self.consequent == other.consequent)
def __hash__(self):
return hash(("implies", hash(self.antecedent), hash(self.consequent)))
def __repr__(self):
return f"Implication({self.antecedent}, {self.consequent})"
def evaluate(self, model):
return ((not self.antecedent.evaluate(model))
or self.consequent.evaluate(model))
def formula(self):
antecedent = Sentence.parenthesize(self.antecedent.formula())
consequent = Sentence.parenthesize(self.consequent.formula())
return f"{antecedent} => {consequent}"
def symbols(self):
return set.union(self.antecedent.symbols(), self.consequent.symbols())
class Biconditional(Sentence):
def __init__(self, left, right):
Sentence.validate(left)
Sentence.validate(right)
self.left = left
self.right = right
def __eq__(self, other):
return (isinstance(other, Biconditional)
and self.left == other.left
and self.right == other.right)
def __hash__(self):
return hash(("biconditional", hash(self.left), hash(self.right)))
def __repr__(self):
return f"Biconditional({self.left}, {self.right})"
def evaluate(self, model):
return ((self.left.evaluate(model)
and self.right.evaluate(model))
or (not self.left.evaluate(model)
and not self.right.evaluate(model)))
def formula(self):
left = Sentence.parenthesize(str(self.left))
right = Sentence.parenthesize(str(self.right))
return f"{left} <=> {right}"
def symbols(self):
return set.union(self.left.symbols(), self.right.symbols())
def model_check(knowledge, query):
"""Checks if knowledge base entails query."""
def check_all(knowledge, query, symbols, model):
"""Checks if knowledge base entails query, given a particular model."""
# If model has an assignment for each symbol
if not symbols:
# If knowledge base is true in model, then query must also be true
if knowledge.evaluate(model):
return query.evaluate(model)
return True
else:
# Choose one of the remaining unused symbols
remaining = symbols.copy()
p = remaining.pop()
# Create a model where the symbol is true
model_true = model.copy()
model_true[p] = True
# Create a model where the symbol is false
model_false = model.copy()
model_false[p] = False
# Ensure entailment holds in both models
return (check_all(knowledge, query, remaining, model_true) and
check_all(knowledge, query, remaining, model_false))
# Get all symbols in both knowledge and query
symbols = set.union(knowledge.symbols(), query.symbols())
# Check that knowledge entails query
return check_all(knowledge, query, symbols, dict())
I know it's too much code, but my doubt is very simple, I tested basic Knowledge Boolean algebra operations such as NOT, AND, and OR. The problem is only at OR fucntion, it always should return TRUE if at least one is true. But it's returning false.
from logic import *
a = Symbol("a")
b = Symbol("b")
# OR
# Error here
orSentence = Or(a, b)
valueOrSentence = model_check(orSentence, a)
print(orSentence.formula() + f" ({valueOrSentence})")
valueOrSentence = model_check(orSentence, Not(a))
print(orSentence.formula() + f" ({valueOrSentence})")
print('---/---/---/')
It should return "true" when check the model, but instead of it it's returning "false"
I prefer to belive there is no error on Harvard logic.py file, what should I do to fix this "OR" logic?
For one specific case of model your knowledge entails but query doesn't, hence it is returning False. There is nothing wrong with it.
When model = {'a': False, 'b': True} then orSentence.evaluate(model) would return True but a.evaluate(model) would return False making the overall result of model_check as False.
If you use andSentence = And(a, b) and then run model_check(andSentence, a), it would return True because for every value of model either andSentence (knowledge) and a (query) both are True or both are False.

Abstract regular expressions

I am working on processing queries in russian language (translating them into SQL code to be executed). I tokenize the query, do morphological analysis, from lemmas I get links to database objects. So now I want to use patterns like regular expressions to get things like conditions, ORDER BY expressions and so on. But the thing is, regex can only be used with list of characters (strings).
Is there a library/solution for Python (preferably) which works like regular expressions but for any kinds of objects (list of tokens with grammatical properties or database objects), not just strings?
So, as an example, I want to write patterns which would look something this:
[db-column]((','|'and')[db-column])*
this pattern would match a list of database objects like this: columnA, columnB and columnC.
If someone is interested in the topic, there is a GitHub repository with my work, I wrote the following code for Abstract regular expressions:
"""
AbstractRegularExpressions.py
Abstract Regular Expressions.
They are like regular expressions, but can work with any kinds of objects,
not just characters as in regex.
This code is based on Mark-Jason Dominus's article «How Regexes Work»,
you can find it here: https://perl.plover.com/Regex/article.html
"""
# NFA - Nondeterministic Finite Automata (machine).
# Used for defining custom operators.
# http://code.activestate.com/recipes/384122/
class Infix:
def __init__(self, function):
self.function = function
def __ror__(self, other):
return Infix(lambda x, self=self, other=other: self.function(other, x))
def __or__(self, other):
return self.function(other)
def __rlshift__(self, other):
return Infix(lambda x, self=self, other=other: self.function(other, x))
def __rshift__(self, other):
return self.function(other)
def __call__(self, value1, value2):
return self.function(value1, value2)
# Alternatives («|» in regex): a|b|c (in regex) <==> a |OR| b |OR| c (here).
class Cases:
def __init__(self, cases):
self.cases = cases
def add(self, case):
self.cases.append(case)
return self
# Operator for joining cases, used like this: a |OR| b |OR| c.
OR = Infix(lambda x, y: x.add(y) if isinstance(x, Cases) else Cases([x, y]))
# Primitives are used to test one token for some predicate.
# In regex there's only one primitive — whether a token is equal to some character or not.
# Examples of primitives: «is token a table column?», «is token's type — string» etc.
class Primitive:
def __init__(self, name, predicate):
self.predicate = predicate
self.name = name
def test(self, *args):
return self.predicate(*args)
def __str__(self):
return self.name
# Transitions are the arrows in NFAs,
# one transition leads from one state to another if the pattern's condition is met for a token.
# If there is no pattern (pattern = None), then you can always go to the next state (epsilon-transition).
class Transition:
def __init__(self, pattern, nextState=None):
self.pattern = pattern
self.nextState = nextState
stateID = 0 # Only used for pretty-printing Patterns (machines).
# State is just a bunch of transitions leading to other states or the final state (None).
class State:
def __init__(self, transitions):
global stateID
stateID += 1
self.ID = stateID
self.transitions = transitions
def __str__(self):
return str(self.ID)
# Recursively creates an NFA
# (makes simple NFAs from primitives and then connects them with connectMachines).
def makeMachine(pattern):
# Subpatterns are treated the same as primitives.
if (isinstance(pattern, Pattern) or isinstance(pattern, Primitive)):
return State({ Transition(pattern) })
# Make a machine out of each member of a list and then connect them with connectMachines.
elif (isinstance(pattern, list)):
if (len(pattern) == 0): return None
accMachine = makeMachine(pattern[-1])
for i in range(1, len(pattern)):
accMachine = connectMachines(makeMachine(pattern[-1 - i]), accMachine)
return accMachine
# Quantifiers (+, ?, *).
elif (isinstance(pattern, tuple)):
(p, quantifier) = pattern
machine = makeMachine(p)
if (quantifier == '+' or quantifier == '*'):
endTransitions = []
for state in statesIterator(machine, set()):
for transition in state.transitions:
if transition.nextState == None:
endTransitions.append((state, transition))
for (state, transition) in endTransitions:
transition.nextState = machine
state.transitions.add(Transition(transition.pattern, None))
if (quantifier == '?' or quantifier == '*'):
if (len([m for m in machine.transitions if m.pattern == None and m.nextState == None]) == 0):
machine.transitions.add(Transition(None))
return machine
# For cases (a |OR| b |OR| c).
elif (isinstance(pattern, Cases)):
return combineMachines([makeMachine(p) for p in pattern.cases])
# Goes through each state of a machine.
def statesIterator(state, passedStates = set()):
if (state != None): yield state
else: return
passedStates.add(state)
for transition in state.transitions:
if (not transition.nextState in passedStates):
for s in statesIterator(transition.nextState, passedStates):
yield s
# Connects two machines into one (replaces finish states of machineA with start states of machineB).
def connectMachines(machineA, machineB):
endTransitions = []
for state in statesIterator(machineA, set()):
for transition in state.transitions:
if transition.nextState == None:
endTransitions.append(transition)
for transition in endTransitions:
transition.nextState = machineB
return machineA
# Connects cases of machines (machineA |OR| machineB |OR| ... |OR| machineN) into one machine.
def combineMachines(machines):
return State({ t for machine in machines for t in machine.transitions })
# Pattern, as in regular expressions.
# Example: p = Pattern('name', [a, b, c, (d, '+') |OR| e])
# which gives you a regex «abc(d+|e)»,
# where a, b, c, d and e are another patterns or primitives.
class Pattern:
def __init__(self, name, pattern):
global stateID
stateID = 0
self.name = name
self.machine = makeMachine(pattern)
def __str__(self):
return self.name
# Structure stores tokens of a found pattern.
class Structure:
def __init__(self, name, elements=None):
self.name = name
self.elements = elements if elements != None else []
def __str__(self):
return f' --- {self.name} --- {[str(el) for el in self.elements]}'
# Class for storing a token and pattern which found this token.
class PatternToken:
def __init__(self, pattern, token):
self.pattern = pattern
self.token = token
def __str__(self):
return f'{self.pattern}: {self.token.text}'
# Used for storing linked matched tokens.
class CurrentState:
def __init__(self, token, transition=None, previousState=None, patternsStack=[]):
self.transition = transition
self.token = token
self.previousState = previousState
self.patternsStack = patternsStack
def __str__(self):
t = self.token.text if self.token != None else ''
return f'== [{self.transition.pattern.name} ({len(self.patternsStack)}): {t}]\n{self.previousState}'
# Connects linked CurrentStates into one Structure.
def connect(self, name):
state = self
structuresStack = []
result = []
indexes = []
while True:
token = state.token
transition = state.transition
# Pattern
if (isinstance(transition.pattern, Pattern)):
if (len(structuresStack) == 0 or structuresStack[-1].name != transition.pattern.name):
structuresStack.append(Structure(transition.pattern.name))
else:
structure = structuresStack.pop()
structure.elements = structure.elements[::-1] # reverse the elements.
if (len(structuresStack) == 0):
result.append(structure)
else:
structuresStack[-1].elements.append(structure)
# Primitive
elif (isinstance(transition.pattern, Primitive)):
indexes.append(token.index)
if (len(structuresStack) == 0):
result.append(PatternToken(transition.pattern, token))
else:
structuresStack[-1].elements.append(PatternToken(transition.pattern, token))
state = state.previousState
if (state == None): break
return ((min(indexes), max(indexes)), Structure(name, result[::-1]))
# Used for running a pattern on a list of tokens.
class Automata:
def __init__(self, pattern):
self.pattern = pattern
self.finalStates = set()
self.currentStates = set()
def feedToken(self, token):
currentStates = set(self.currentStates)
self.currentStates = set()
for state in currentStates:
for transition in state.transition.nextState.transitions:
self.processTransition(transition, token, state)
for transition in self.pattern.machine.transitions:
self.processTransition(transition, token)
def __str__(self):
return "\n\n".join([str(state) for state in self.finalStates])
def processTransition(self, transition, token, previousState=None):
# Epsilon
if (transition.pattern == None):
if (transition.nextState != None):
for t in transition.nextState.transitions:
self.processTransition(t, token, previousState)
elif (len(previousState.patternsStack) == 0):
self.finalStates.add(previousState)
else:
newState = previousState
while True:
patternsStack = list(newState.patternsStack)
patternState = patternsStack.pop()
newState = CurrentState(None, patternState.transition, newState, patternsStack)
if (newState.transition.nextState != None):
for t in newState.transition.nextState.transitions:
self.processTransition(t, token, newState)
break
if (len(newState.patternsStack) == 0):
self.finalStates.add(newState)
break
# Primitive
elif (isinstance(transition.pattern, Primitive)):
if (transition.pattern.test(token)):
patternsStack = previousState.patternsStack if previousState != None else []
newState = CurrentState(token, transition, previousState, list(patternsStack))
if (newState.transition.nextState != None):
self.currentStates.add(newState)
elif (len(newState.patternsStack) == 0):
self.finalStates.add(newState)
else:
while True:
patternsStack = list(newState.patternsStack)
patternState = patternsStack.pop()
newState = CurrentState(None, patternState.transition, newState, patternsStack)
if (newState.transition.nextState != None):
self.currentStates.add(newState)
break
if (len(newState.patternsStack) == 0):
self.finalStates.add(newState)
break
# Pattern
elif (isinstance(transition.pattern, Pattern)):
patternsStack = previousState.patternsStack if previousState != None else []
newState = CurrentState(None, transition, previousState, list(patternsStack))
newState.patternsStack.append(newState)
for t in transition.pattern.machine.transitions:
self.processTransition(t, token, newState)
# Pretty-print a machine.
def printMachine(machine):
padding = 0
for state in statesIterator(machine, set()):
for t in state.transitions:
print(padding*' ' + f'{state}: --{t.pattern}->{t.nextState}')
padding += 1
# Pretty-print a pattern.
def printPattern(pattern):
print(f'-=-=-=-=-= {pattern.name} =-=-=-=-=-')
printMachine(pattern.machine)
Which allows you to write templates like these:
# Selecting
table = Primitive('table', lambda token: token.type == 'table')
column = Primitive('column', lambda token: token.type == 'column')
columnExpr = Pattern('columnExpr', [(operator, '*'), column])
columnLiteralExpr = Pattern('columnExpr', [(operator, '*'), column |OR| literal])
listOfTables = Pattern('listOfTables', [table])
listOfColumns = Pattern('listOfColumns', [columnExpr, ([connector, columnExpr], '*'), (table, '?')])
selectExpr = Pattern('selectExpr', [listOfColumns |OR| listOfTables])

Combination Lock Program

Me and my partner have been working on this for a few hours and can't figure this out. The directions are vague in some areas and our professor did not do a good job of breaking it down to help us. Here is a link to the directions. I believe they are not very clear but please correct me if I am wrong and just overthinking it https://imgur.com/a/huHnwos
I believe that our biggest problems are the unlock(combination) and set_new_combination(new_combination) methods. I can figure out the str() method as that one isn't very hard to do. We've tried the things our professor has told us to try but they have been unsuccessful.
class Lock:
def __init__(self, combination = 0):
self.combination = combination
self.locked = False
def lock(self):
self.locked = True
def unlock(self, combination):
if combination == True or combination == 0:
self.locked = False
def set_new_combination(self, new_combination):
if self.locked == False:
self.combination = new_combination
def is_locked(self):
if self.locked == True or self.combination == True:
return True
else:
return False
def __eq__(self, other):
if other is not None and type(other) == type(self):
if self.combination == other.new_combination:
return False
def __str__(self):
return self.combination, ',', self.locked
The expected result should be a working basic combination lock.
Here's my implementation based on the inctructions provided, with comments where it deviates from your code.
class Lock:
def __init__(self, combination = 0): # No change here
self.combination = combination
self.locked = False
def lock(self):
# Although a test of self.locked is redundant, the instructions state
# "...if invoked a second time this, method should do nothing."
if not self.locked:
self.locked = True
def unlock(self, combination):
# You were not testing the stored combination against the one passed to the method.
# it does not matter if the stored combination is zero or a different number,
# you still need to check for equality.
# You also need a test as with lock() to satisfy the "if invoked a second time this,
# method should do nothing" requirement.
if self.locked and self.combination == combination:
self.locked = False
def set_new_combination(self, new_combination):
# You can simply the `if` condition, there's no need to test against False
if not self.locked:
self.combination = new_combination
def is_locked(self):
# I don't know why you are testing the self.combination value, you
# only need to return the state of the lock
return self.locked
def __eq__(self, other):
# You have the correct guard conditions but were returning False when
# the combinations matched. You can simply return the comparison result.
if other is not None and type(other) == type(self):
return self.combination == other.new_combination
def __str__(self):
# For some reason the output format specified for this appears to put it in a list
# (the square brackets) but as it's only for display we'll "fake" the list.
# The `if` statement prints the word 'locked' or 'unlocked' depending on the
# `self.locked` state.
return '[{}, {}]'.format(self.combination, 'locked' if self.locked else 'unlocked')
There are couple of problems with your code. First, if statement in your unlock method will be executed only if combination == 0 or combination == 1, which has nothing to do with lock's combination (self.combination). In your is_locked method you should only return self.locked, no need for if. __eq__ method can also be simplified. And __str__ method should actually return String.
class Lock:
def __init__(self, combination = 0):
self.combination = combination
self.locked = False
def lock(self):
self.locked = True
def unlock(self, combination):
if self.combination == combination:
self.locked = False
def set_new_combination(self, new_combination):
if not self.locked:
self.combination = new_combination
def is_locked(self):
return self.locked
def __eq__(self, other):
return isinstance(other, Lock) and self.combination == other.combination
def __str__(self):
return f'{self.combination}, { "locked" if self.locked else "unlocked"}'
Your unlockmethod is trying to compare a boolean to a number (the combination). Change it to look like this:
def unlock(self, combination):
if combination == self.combination:
self.locked = False
You also did this in your is_locked method, so that should be changed too:
def is_locked(self):
return self.locked
(Any time you find yourself writing something along the lines of if x return True else return False you can almost always replace this with return x if the conditional is simple).
set_new_combination works fine; I don't know what issue you saw with it.
Finally, your __str__ method should actually return a string:
def __str__(self):
return '[' + str(self.combination) + ', ' + 'locked' if self.locked else 'unlocked' + ']'

Leetcode Python 208. Implement Trie (Prefix Tree)

Can someone say what is wrong with my code, it is passing all the test cases except the last one when I downloaded the specific test case both the expected and actual output seems same, the question is https://leetcode.com/problems/implement-trie-prefix-tree/description/
Edit 1:
Here is the code:
class Trie:
def __init__(self):
"""
Initialize your data structure here.
"""
self.data = None
self.children = {}
self.isWord = False
def insert(self, word):
"""
Inserts a word into the trie.
:type word: str
:rtype: void
"""
if len(word) == 0:
return
if word[0] not in self.children:
self.children[word[0]] = Trie()
self.insertHelper(word[1:], self.children[word[0]])
else:
self.insertHelper(word[1:], self.children[word[0]])
if len(word) == 1:
self.isWord = True
def insertHelper(self, word, trie):
if len(word) == 0:
return
if word[0] not in trie.children:
trie.children[word[0]] = Trie()
trie.insertHelper(word[1:], trie.children[word[0]])
else:
trie.insertHelper(word[1:], trie.children[word[0]])
if len(word) == 1:
trie.isWord = True
def search(self, word):
"""
Returns if the word is in the trie.
:type word: str
:rtype: bool
"""
if len(word) == 1 and word[0] in self.children and self.isWord:
return True
elif len(word) == 0:
return False
if word[0] in self.children:
return self.searchHelper(word[1:], self.children[word[0]])
else:
return False
def searchHelper(self, word, trie):
if len(word) == 1 and word[0] in trie.children and trie.isWord:
return True
elif len(word) == 0:
return False
if word[0] in trie.children:
return self.searchHelper(word[1:], trie.children[word[0]])
else:
return False
def startsWith(self, prefix):
"""
Returns if there is any word in the trie that starts with the given prefix.
:type prefix: str
:rtype: bool
"""
if len(prefix) == 0:
return False
if prefix[0] in self.children:
return self.startsWithHelper(prefix[1:], self.children[prefix[0]])
else:
return False
def startsWithHelper(self, prefix, trie):
if len(prefix) == 0:
return True
if prefix[0] in trie.children:
return trie.startsWithHelper(prefix[1:], trie.children[prefix[0]])
else:
return False
Thanks in advance.
One quirk I noticed is passing an empty prefix into startsWith(). If this method is modeled on the Python str method startswith(), then we expect True:
>>> "apple".startswith("")
True
>>>
But your Trie returns False in this situation:
>>> t = Trie()
>>> t.insert("apple")
>>> t.startsWith("")
False
>>>
Below is my rework of your code that I did primarily to understand it but I also found you had redundancies, particularly your Helper functions. This code fixes the quirk mentioned above and is Python 3 specific:
class Trie:
def __init__(self):
self.children = {}
self.isWord = False
def insert(self, word):
"""
Inserts a word into the trie.
:type word: str (or list internally upon recursion)
:rtype: None
"""
if not word:
return
head, *tail = word
if head not in self.children:
self.children[head] = Trie()
trie = self.children[head]
if tail:
trie.insert(tail)
else:
self.isWord = True
def search(self, word):
"""
Returns True if the word is in the trie.
:type word: str (or list internally upon recursion)
:rtype: bool
"""
if not word:
return False
head, *tail = word
if head in self.children:
if not tail and self.isWord:
return True
return self.children[head].search(word[1:])
return False
def startsWith(self, prefix):
"""
Returns if there is any word in the trie that starts with the given prefix.
:type prefix: str (or list internally upon recursion)
:rtype: bool
"""
if not prefix:
return True
head, *tail = prefix
if head in self.children:
return self.children[head].startsWith(tail)
return False
Here's another solution using the 'defaultdictionary' from the collections module to utilize recursion in the 'insert' function too.
Credit: https://leetcode.com/problems/implement-trie-prefix-tree/discuss/631957/python-elegant-solution-no-nested-dictionaries
class Trie:
def __init__(self):
"""
Initialize your data structure here.
"""
self.nodes = collections.defaultdict(Trie)
self.is_word = False
def insert(self, word: str) -> None:
"""
Inserts a word into the trie.
"""
if not word:
self.is_word = True
else:
self.nodes[word[0]].insert(word[1:])
def search(self, word: str) -> bool:
"""
Returns if the word is in the trie.
"""
if not word:
return self.is_word
if word[0] in self.nodes:
return self.nodes[word[0]].search(word[1:])
return False
def startsWith(self, prefix: str) -> bool:
"""
Returns if there is any word in the trie that starts with the given prefix.
"""
if not prefix:
return True
if prefix[0] in self.nodes:
return self.nodes[prefix[0]].startsWith(prefix[1:])
return False
Your Trie object will be instantiated and called as such:
obj = Trie()
obj.insert(word)
param_2 = obj.search(word)
param_3 = obj.startsWith(prefix)
class TrieNode:
def __init__(self):
# each key is a TrieNode
self.keys = {}
self.end = False
class Trie:
def __init__(self):
self.root = TrieNode()
# node=this.root gives error "this" is not defined
def insert(self, word: str, node=None) -> None:
if node == None:
node = self.root
# insertion is a recursive operation
if len(word) == 0:
node.end = True
return
elif word[0] not in node.keys:
node.keys[word[0]] = TrieNode()
self.insert(word[1:], node.keys[word[0]])
# that means key exists
else:
self.insert(word[1:], node.keys[word[0]])
def search(self, word: str, node=None) -> bool:
if node == None:
node = self.root
# node.end=True means we have inserted the word before
if len(word) == 0 and node.end == True:
return True
# if we inserted apple and then search for app we get false becase we never inserted app so a-p-p last_p.end is not True
# But startsWith(app) would return True
elif len(word) == 0:
return False
elif word[0] not in node.keys:
return False
else:
# we have to return becasue api expects us to return bool
return self.search(word[1:], node.keys[word[0]])
def startsWith(self, prefix: str, node=None) -> bool:
if node == None:
node = self.root
if len(prefix) == 0:
return True
elif prefix[0] not in node.keys:
return False
else:
return self.startsWith(prefix[1:], node.keys[prefix[0]])

Recursion and return statements in python

I am trying to implement a 2-3 tree but I am having trouble with the find method.
This method given an int as parameter should return the node that contains the int.
The problem is that sometimes it works, sometimes it does't and I don't know why.
I have added a test print. For a particular int that I know for sure that is part of the tree, the code executes the print statement, meaning that it has found the node, but does not return this node. Instead it return False which is at the end of the code.
Can you help me solving this ?
def find(self,data,node=0): #return problem ???
if node==0:
a=self.root
else:
a=node
if a.data2==None:
if data==a.data: ### here is the problem
print("qwertyuiop") ### it does not execute the return statement
return a
elif data < a.data:
if a.left!=None:
return self.find(data,a.left)
elif data > a.data:
if a.right!=None:
return self.find(data,a.right)
else:
if a.data2!=None:
if (data==a.data or data==a.data2):
return a
elif data<a.data:
if a.left!=None:
return self.find(data,a.left)
elif (data>a.data and data<a.data2):
if a.middle!=None:
return self.find(data,a.middle)
elif data>a.data2:
if a.right!=None:
return self.find(data,a.right)
print("Not Found") ### function executes this print
return False
self.root is the root of the tree and is an object of the following class
class Node:
def __init__(self, data=None, left=None, right=None):
self.data = data
self.data2 = None
self.data3 = None
self.left = left
self.right = right
self.middle = None
self.middle2 = None
Binary Search Tree:
class Nodeee:
def __init__(self, data=None, left=None, right=None):
self.data = data
self.left = left
self.right = right
class BST:
def __init__(self, root=None):
self.c=[]
self.total=0
self.root = None
def parent(self,data,node=5):
def search(nodee,cc,data):
if data==cc.data:
return nodee
else:
if data<cc.data:
nodee=cc
return search(nodee,cc.left,data)
elif data>cc.data:
nodee=cc
return search(nodee,cc.right,data)
print("Parent Error")
return False
if node==self.root:
print("Root has no parent")
else:
a=self.root
c=self.root
return search(a,c,data)
def lookup(self,data,node=0):
if node==0:
a=self.root
else:
a=node
if data < a.data:
if a.left==None:
return a
else:
return self.lookup(data,a.left)
elif data > a.data:
if a.right==None:
return a
else:
return self.lookup(data,a.right)
def find(self,data,node=0):
if node==0:
a=self.root
else:
a=node
if data==a.data:
print("WTF")
return a
elif data < a.data:
if a.left!=None:
return self.find(data,a.left)
elif data > a.data:
if a.right!=None:
return self.find(data,a.right)
print("Not Found")
return False
def find2(self,data,node=0):
if node==0:
a=self.root
else:
a=node
if data==a.data:
return True
elif data < a.data:
return self.find2(data,a.left)
elif data > a.data:
return self.find2(data,a.right)
return False
def is_empty(self):
if self.root==None:
return True
def is_leaf(self,n):
if (n.left==None and n.right==None):
return True
return False
def delete(self):
self.root=None
def insert(self, data):
if self.root==None:
self.root=Nodeee(data)
self.total+=1
return True
else:
b=self.lookup(data)
if data < b.data:
b.left=Nodeee(data)
self.total+=1
return True
elif data > b.data:
b.right=Nodeee(data)
self.total+=1
return True
print("Insert Error !")
return False
def inorder_swap(self,data):
a=self.find(data)
b=a.right
while self.is_leaf(b)!=True:
if b.left!=None:
b=b.left
elif b.left==None:
b=b.right
temp=a.data
a.data=b.data
b.data=temp
def remove(self,data):
a=self.find(data)
if self.is_leaf(a)==True:
b=self.parent(data)
if b.left==a:
b.left=None
elif b.right==a:
b.right=None
elif self.is_leaf(a)==False:
if a.left==None:
b=self.parent(data)
if b.left==a:
b.left=b.left.right
elif b.right==a:
b.right=b.right.right
elif a.right==None:
b=self.parent(data)
if b.left==a:
b.left=b.left.left
elif b.right==a:
b.right=b.right.left
elif (a.left!=None and a.right!=None):
self.inorder_swap(data)
self.remove(data)
def inorder(self,node):
if node!=None:
self.inorder(node.left)
self.c.append(node.data)
self.inorder(node.right)
def inorder_print(self):
self.c=[]
self.inorder(self.root)
print("\nStart")
for x in range(len(self.c)):
print(self.c[x], end=",")
print("\nFinish\n")
a=BST()
print(a.insert(234)==True)
print(a.insert(13)==True)
print(a.insert(65)==True)
print(a.insert(658)==True)
print(a.insert(324)==True)
print(a.insert(86)==True)
print(a.insert(5)==True)
print(a.insert(76)==True)
print(a.insert(144)==True)
print(a.insert(546)==True)
print(a.insert(2344)==True)
print(a.insert(1213)==True)
print(a.insert(6345)==True)
print(a.insert(653348)==True)
print(a.insert(35324)==True)
print(a.insert(8463)==True)
print(a.insert(5555)==True)
print(a.insert(76539)==True)
print(a.insert(14499)==True)
print(a.insert(59999946)==True)
a.inorder_print()
a.remove(35324)
a.remove(1213)
a.remove(2344)
a.remove(144)
a.remove(5555)
a.remove(6345)
a.remove(59999946)
a.remove(76)
print(a.root.data)
a.inorder_print()
def inorder_swap(self,data):
a=self.find(data)
b=a.right
while self.is_leaf(b)!=True:
if b.left!=None:
b=b.left
elif b.left==None:
b=b.right
temp=a.data
a.data=b.data
b.data=temp
a here is the node containing the passed data. This method does nothing else than swapping a's data with some leaf's data (first one while finds), thereby distorting the tree order. A follow-up find on the same data therefore fails and returns False. Since your code has no error checks, this results in an AttributeError.
You probably want to move nodes around in inorder_swap. However you only assign to the local name b. If you want to change nodes, then you need to use b.left = or b.right =.
It might be that there are more problems, that I don't see right now.
Also your code has several style problems, some of them:
You have four functions doing the same: find, find2, lookup and search in parent.
Most of the naming is not informative or even confusing.
Lines like if a.right==None: should be written as if not a.right: (or maybe if a.right is None:).
Check the return value of functions and don't just assume they return a valid node if they might not (i.e. find might return False instead of a node). Or alternatively use exception handling.
If you have an if ... elif ... elif block you don't have to check the last possibility if it is sure to be true, for example:
if b.left!=None:
# something
elif b.left==None:
# something else
should be
if b.left:
# something
else:
# something else

Categories

Resources