patternpythonMinor
Querying a data structure that contains various triples
Viewed 0 times
triplesqueryingthatstructurecontainsdatavarious
Problem
I wrote code that queries a data structure that contains various triples - 3-item tuples in the form subject-predicate-object. I wrote this code based on an exercise from Programming the Semantic Web textbook.
The function invocation looks like:
The function
The function
How can I simplify this code? How can I make it more functional-style, without nested
def query(clauses):
bindings = None
for clause in clauses:
bpos = {}
qc = []
for pos, x in enumerate(clause):
if x.startswith('?'):
qc.append(None)
bpos[x] = pos
else:
qc.append(x)
rows = list(triples(tuple(qc)))
if bindings == None:
# 1st pass
bindings = [{var: row[pos] for var, pos in bpos.items()}
for row in rows]
else:
# >2 pass, eliminate wrong bindings
for binding in bindings:
for row in rows:
for var, pos in bpos.items():
if var in binding:
if binding[var] != row[pos]:
bindings.remove(binding)
continue
else:
binding[var] = row[pos]
return bindingsThe function invocation looks like:
bg.query([('?person','lives','Chiapas'),
('?person','advocates','Zapatism')])The function
triples inside it accepts 3-tuples and returns list of 3-tuples. It can be found here.The function
query loops over each clause, tracks variables (strings that start with '?'), replaces them with None, invokes triples, receives rows, tries to fit values to existing bindings.How can I simplify this code? How can I make it more functional-style, without nested
for-loops, continue keyword and so on?Solution
- Your code would be helped by splitting parts of it out into functions.
- Your
continuestatement does nothing
- Rather then having a special case for the first time through, initialize
bindingsto[{}]for the same effect.
My version:
def clause_to_query(clause):
return tuple(None if term.startswith('?') else term for term in clause)
def compatible_bindings(clause, triples):
for triple in triples:
yield { clause_term : fact_term
for clause_term, fact_term in zip(clause, row) if clause_term.startswith('?')
}
def merge_bindings(left, right):
shared_keys = set(left.keys()).intersection(right.keys)
if all(left[key] == right[key] for key in shared_keys):
bindings = left.copy()
bindings.update(right)
return bindings
else:
return None # indicates that a binding cannot be merged
def merge_binding_lists(bindings_left, bindings_right):
new_bindings = []
for left, right in itertools.product(bindings_left, bindings_right):
merged_binding = merge_bindings(left, right)
if merged_binding is not None:
new_bindings.append( merged_binding )
return new_bindings
def query(clauses):
bindings = [{}]
for clause in clauses:
query = clause_to_query(clause)
new_bindings = compatible_bindings(clause, triples(query))
bindings = merge_binding_lists(bindings, new_bindings)
return bindingsCode Snippets
def clause_to_query(clause):
return tuple(None if term.startswith('?') else term for term in clause)
def compatible_bindings(clause, triples):
for triple in triples:
yield { clause_term : fact_term
for clause_term, fact_term in zip(clause, row) if clause_term.startswith('?')
}
def merge_bindings(left, right):
shared_keys = set(left.keys()).intersection(right.keys)
if all(left[key] == right[key] for key in shared_keys):
bindings = left.copy()
bindings.update(right)
return bindings
else:
return None # indicates that a binding cannot be merged
def merge_binding_lists(bindings_left, bindings_right):
new_bindings = []
for left, right in itertools.product(bindings_left, bindings_right):
merged_binding = merge_bindings(left, right)
if merged_binding is not None:
new_bindings.append( merged_binding )
return new_bindings
def query(clauses):
bindings = [{}]
for clause in clauses:
query = clause_to_query(clause)
new_bindings = compatible_bindings(clause, triples(query))
bindings = merge_binding_lists(bindings, new_bindings)
return bindingsContext
StackExchange Code Review Q#20278, answer score: 4
Revisions (0)
No revisions yet.