3
\$\begingroup\$

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.

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 bindings

The 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?

asked Jan 8, 2013 at 16:48
\$\endgroup\$

1 Answer 1

4
\$\begingroup\$
  1. Your code would be helped by splitting parts of it out into functions.
  2. Your continue statement does nothing
  3. Rather then having a special case for the first time through, initialize bindings to [{}] 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 bindings
answered Jan 8, 2013 at 20:02
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.