def append(Xs: Union[LinkedList, Var], Ys: Union[LinkedList, Var], Zs: Union[LinkedList, Var]): """ append([], Ys, Zs). append([X|Xs], Ys, [X|Zs]) :- append(Xs, Ys, Zs). Note that this could just have well have been written: append([Z|Xs], Ys, [Z|Zs]) :- append(Xs, Ys, Zs). or append([W|Xs], Ys, [W|Zs]) :- append(Xs, Ys, Zs). We don't have to implicitly favor the Xs. No matter what the variable is called, this is really unifying Xs_head and Zs_head. append/3 converts between: Xs + Ys <--> Zs It does this by moving elements between the Xs and the Zs. If we are moving from Zs to Xs + Ys, there will be multiple answers depending on how much of Zs is shifted before we let Ys be the rest. The function consists of two "clauses" (see prolog definition above), which are tried in sequence. Clause 1 This is the step in which we set Xs to [] and set Ys to Zs. No recursive call. At this point, earlier recursion calls will have shifted elements between Xs and Zs. But those are not visible at this level. Clause 2. The recursive step. Move an element between Xs and Zs and call append recursively with Xs_Tail and Zs_Tail. Since an empty list cannot unify with a LinkedList that has a head and a tail, if either Xs or Zs is empty, unify_pairs will fail. The actual code is quite short. It's very similar to like the prolog code. """ # Create the existential variables at the start. (XZ_Head, Xs_Tail, Zs_Tail) = n_Vars(3) for _ in forany([ # Clause 1. lambda: unify_pairs([(Xs, emptyLinkedList), (Ys, Zs)]), # Clause 2. lambda: forall([ lambda: unify_pairs([(Xs, LinkedList((XZ_Head, Xs_Tail))), (Zs, LinkedList((XZ_Head, Zs_Tail)))]), lambda: append(Xs_Tail, Ys, Zs_Tail) ]) ]): yield
def complete_column(carry_out: int, Carry_Out_Dig: PyValue, sum_dig: int, Sum_Dig: PyValue, digits_in: List[int], Leading_Digits): """ If Sum_Dig (the variable representing the digit in the sum for this column) is not yet instantiated, instantiate it to sum_dig (if that digit is available). If Sum_Dig is already instantiated, ensure it is consistent with the sum_dig. Instantiate Carry_Out_Dig to carry_out. """ # Is Sum_Dig uninstantiated? If so, instantiate it to sum_digit if possible. # Then instantiate Carry_Out_Dig, and return (yield) digits_in with sum_digit removed. if not Sum_Dig.is_instantiated(): if sum_dig not in digits_in: # sum_dig is not available in digits_in. Fail, i.e., return instead of yield. return # sum_dig is available in digits_in. Give it to Sum_Dig as long as this does not give # 0 to one of the leading digits. if sum_dig != 0 or all(Sum_Dig is not LD for LD in Leading_Digits): for _ in unify_pairs([(Carry_Out_Dig, carry_out), (Sum_Dig, sum_dig)]): # Remove sum_digit from digits_in i = digits_in.index(sum_dig) yield digits_in[:i] + digits_in[i + 1:] # If Sum_Dig is instantiated, is it equal to sum_digit? # If so, instantiate Carry_Out_Dig and return the current digits_in. elif sum_dig == Sum_Dig.get_py_value(): for _ in unify(Carry_Out_Dig, carry_out): yield digits_in
def append(Xs: Union[PySequence, Var], Ys: Union[PySequence, Var], Zs: Union[PySequence, Var]): """ append([], Ys, Zs). append([X|Xs], Ys, [X|Zs]) :- append(Xs, Ys, Zs). See discussion in linked_list version. This version assumes we are working with Python lists or tuples, i.e., no uninstantiated tails. """ if isinstance(Zs, Var) and (isinstance(Xs, Var) or isinstance(Ys, Var)): # Can't have Xs or Ys Var if Zs is Var. return if isinstance(Zs, Var): ListType = type(Xs) # Make Zs a list of Var's of length len(Xs) + len(Ys) # After this unification, Zs will still be a Var, but # by the time we reach this point again, Zs will refer # to its trail end, which is not a Var. for _ in unify(Zs, ListType(n_Vars(len(Xs) + len(Ys)))): yield from append(Xs, Ys, Zs) return # We now know that: Zs is not a Var -- although it may be a sequence of Vars. # Divide up its length among Xs and Ys ListType = type(Zs) len_Zs = len(Zs) for i in range(len_Zs + 1): # If Xs or Ys are already instantiated to some fixed length Sequence, unify will fail when given the wrong length. for _ in unify_pairs([(Xs, ListType(n_Vars(i))), (Ys, ListType(n_Vars(len_Zs - i)))]): # Although the lengths of Xs and Ys vary with i, # Xs, Ys, and Zs are all of fixed lengths in which len(Xs) + len(Ys) = len(Zs). # Concatenate Xs and Ys and then unify the concatenation with Zs. XYs = [ *Xs.unification_chain_end().args, *Ys.unification_chain_end().args ] yield from unify_sequences(XYs, Zs.args)
def is_even_2_decorated(n: int, Res: Var) -> Bool_Yield_Wrapper: for i in range(n): for _ in unify_pairs( [(PyValue(i % 2 == 0), PyValue(True)), (PyValue(i), Res) ]): yield
def is_even_2(n: int, Res: Var) -> Generator[None, None, None]: for i in range(n): for _ in unify_pairs([ (PyValue(i % 2 == 0), PyValue(True)), (PyValue(i), Res) ]): yield
E_Sub = [Var(), PyValue(2)] linkedlistE_Sub = LinkedList(E_Sub) print(f'\nE_Sub: {linkedlistE_Sub}\n?- is_contiguous_in(E_Sub, {A_List})') for _ in A_List.has_contiguous_sublist(E_Sub): print(f'E_Sub: {linkedlistE_Sub}') X = PyValue('abc') Z1 = Var() Y1 = LinkedList([X, Z1]) Z2 = Var() Z3 = Var() Y2 = LinkedList([Z2, Z3]) print(f'\n1. Y1: {Y1}, Y2: {Y2}') for _ in unify_pairs([(Y1, Y2), (Z2, Z3)]): print(f'2. Y1: {Y1}, Y2: {Y2}') print(f'3. Y1: {Y1}, Y2: {Y2}') print('\nAgain, switching the order of the unifications') print(f'4. Y1: {Y1}, Y2: {Y2}') for _ in unify_pairs([(Y1, Y2), (Z2, Z3)]): print(f'5. Y1: {Y1}, Y2: {Y2}') print(f'6. Y1: {Y1}, Y2: {Y2}') print('End test') """ Expected output emptyLinkedList: [] LinkedList([1, 2, 3]): [1, 2, 3]