-1
def resolveSentences(s1, s2):
"""
    given two sentences s1 and s2 return the resolved sentence
"""
clauses = []
for p1 in s1:
    for p2 in s2:
        if p1.name == p2.name and p1.sign != p2.sign:
            s1 = remove(p1,s1)
            s2 = remove(p2, s2)
            s = None
            if s1 and s2:
                s = list(set(s1).union(s2))
            elif s1 and not s2:
                s = list(set(s1))
            elif s2 and not s1:
                s = list(set(s2))
            if s:
                for pred in s:
                    clauses.append(pred)
if len(clauses)> 0:
    return clauses
else:
    return None

I call the function using:

if __name__ == "__main__":
    p1 = convertToPredicate("A(x)")
    p2 = convertToPredicate("B(x)")
    p3 = convertToPredicate("~A(x)")
    p4 = convertToPredicate("~B(x)")
    p5 = convertToPredicate("C(x)")
    s1 = [p1,p2,p5]
    # A(x)| B(x) | C(x)
    s2 = [p3,p4]
    # ~A(x)| ~B(x)
    trial = resolveSentences(s1,s2)
    for t in trial:
        print(t.sign, t.name, t.arguments, sep="\t")

My expected answer is: C(x) My current answer is: B(x)| ~B(x)| C(x)

Question: Why doesn't it remove B(x)? My observation: The first for loop skips the second predicate in the function resolveSentences(). I am not able to figure out why though. Any help would be appreciated.

The remove function is as follows:

def equals( p1, p2):
    """
        returns True is predicate p1 is equal to p2
    """
    if p1.name == p2.name:
        if p1.sign == p2.sign:
            return True
    return False

def remove( predicate_to_remove, sentence):
    """
        removes all instances of predicates from sentence and returns a list of predicates
    """
    for predicate in sentence:
        if equals(predicate_to_remove, predicate):
            sentence.remove(predicate)
    return sentence        

Predicate is a class that has attributes: name, sign, constants, variables, arguments

the class defn for Predicate is:

class Predicate:
"""
    determining the contents of a predicate. 
    sign: 1 if negative, else positive
"""
name = None
sign = None
constants = None
variables = None
arguments = None

def __init__(self):
    self.arguments = []
    self.name = ""
    self.sign = 0
    self.constants = []
    self.variables = []

function convertToPredicate is:

def convertToPredicate(query):
"""
    converts the raw input to an object of class predicate
    (str) -> (Predicate)
"""
std_predicate = Predicate()

#determining sign of predicate
std_predicate.sign = 1 if query[0] == "~" else 0
query = query.replace("~","")

#determining name of predicate
std_predicate.name = query[:query.find("(")]

#determining arg/var/const of predicate
std_predicate.arguments = query[query.find("(")+1:query.find(")")].replace(" ", "").split(",")
for arg in std_predicate.arguments:
    if arg[0].isupper():
        std_predicate.constants.append(arg)
    elif arg[0].islower():
        std_predicate.variables.append(arg)
return std_predicate

1 Answers1

2

The documentation says this is expected:

Note: There is a subtlety when the sequence is being modified by the loop (this can only occur for mutable sequences, i.e. lists). An internal counter is used to keep track of which item is used next, and this is incremented on each iteration. When this counter has reached the length of the sequence the loop terminates. This means that if the suite deletes the current (or a previous) item from the sequence, the next item will be skipped (since it gets the index of the current item which has already been treated). Likewise, if the suite inserts an item in the sequence before the current item, the current item will be treated again the next time through the loop.

One way to avoid this is to drive the for loops using copies of the lists.
Instead of this:

for p1 in s1:
    for p2 in s2:

Use this:

base_s1 = s1[:]
base_s2 = s2[:]
for p1 in base_s1:
    for p2 in base_s2:

Since the loops use copies of s1 and s2, changes to those lists in the loop body won't affect the values that p1 and p2 take on.

cco
  • 5,873
  • 1
  • 16
  • 21