This commit is contained in:
Tibo De Peuter 2025-05-08 18:58:33 +02:00
parent 7daae860fc
commit 1feb3893c5
Signed by: tdpeuter
GPG key ID: 38297DE43F75FFE2
4 changed files with 45 additions and 27 deletions

View file

@ -48,16 +48,17 @@ abstract class Clause(var head: Head, var body: Body) : Term, Resolvent {
onSuccess = { bodySubs -> onSuccess = { bodySubs ->
// If the body can be proven, yield the (combined) substitutions // If the body can be proven, yield the (combined) substitutions
val goalResult = goalToHeadSubs.mapValues { applySubstitution(it.value, bodySubs) } val goalResult = goalToHeadSubs.mapValues { applySubstitution(it.value, bodySubs) }
yield(Result.success(goalResult + headResult)) val bodyResult = bodySubs.filterKeys { occurs(it as Variable, simplifiedGoal) }
yield(Result.success(goalResult + headResult + bodyResult))
}, },
onFailure = { error -> onFailure = { error ->
if (error is AppliedCut) { if (error is AppliedCut) {
// Find single solution and return immediately // Find single solution and return immediately
if (error.subs != null) { if (error.subs != null) {
val goalResult = goalToHeadSubs.mapValues { val bodySubs = error.subs
applySubstitution(it.value, error.subs) val goalResult = goalToHeadSubs.mapValues { applySubstitution(it.value, bodySubs) }
} val bodyResult = bodySubs.filterKeys { occurs(it as Variable, simplifiedGoal) }
yield(Result.failure(AppliedCut(goalResult + headResult))) yield(Result.failure(AppliedCut(goalResult + headResult + bodyResult)))
} else { } else {
yield(Result.failure(AppliedCut())) yield(Result.failure(AppliedCut()))
} }

View file

@ -35,10 +35,11 @@ class FunctorOp(private val term: Term, private val functorName: Term, private v
"Arguments are not sufficiently instantiated" "Arguments are not sufficiently instantiated"
} }
val t = applySubstitution(term, subs)
val name = applySubstitution(functorName, subs) as Atom val name = applySubstitution(functorName, subs) as Atom
val arity = applySubstitution(functorArity, subs) as Integer val arity = applySubstitution(functorArity, subs) as Integer
val result = Structure(name, List(arity.value) { AnonymousVariable.create() }) val result = Structure(name, List(arity.value) { AnonymousVariable.create() })
sequenceOf(Result.success(mapOf(term to result))) sequenceOf(Result.success(mapOf(t to result)))
} }
else -> throw IllegalStateException() else -> throw IllegalStateException()

View file

@ -2,14 +2,15 @@ package prolog.builtins
import prolog.Answers import prolog.Answers
import prolog.Substitutions import prolog.Substitutions
import prolog.ast.Database.Program
import prolog.ast.logic.LogicOperand import prolog.ast.logic.LogicOperand
import prolog.ast.logic.LogicOperator import prolog.ast.logic.LogicOperator
import prolog.ast.terms.Atom import prolog.ast.terms.Atom
import prolog.ast.terms.Body import prolog.ast.terms.Body
import prolog.ast.terms.Goal import prolog.ast.terms.Goal
import prolog.ast.terms.Structure
import prolog.flags.AppliedCut import prolog.flags.AppliedCut
import prolog.logic.applySubstitution import prolog.logic.applySubstitution
import prolog.logic.numbervars
/** /**
* Always fail. * Always fail.
@ -53,8 +54,8 @@ class Conjunction(val left: LogicOperand, private val right: LogicOperand) :
left.fold( left.fold(
// If it succeeds, satisfy the right part with the updated substitutions and return all results // If it succeeds, satisfy the right part with the updated substitutions and return all results
onSuccess = { leftSubs -> onSuccess = { leftSubs ->
right.satisfy(subs + leftSubs).forEach { right.satisfy(subs + leftSubs).forEach { right ->
it.fold( right.fold(
// If the right part succeeds, yield the result with the left substitutions // If the right part succeeds, yield the result with the left substitutions
onSuccess = { rightSubs -> onSuccess = { rightSubs ->
yield(Result.success(leftSubs + rightSubs)) yield(Result.success(leftSubs + rightSubs))

View file

@ -12,10 +12,7 @@ import prolog.ast.lists.List.Cons
import prolog.ast.lists.List.Empty import prolog.ast.lists.List.Empty
import prolog.ast.logic.Fact import prolog.ast.logic.Fact
import prolog.ast.logic.LogicOperator import prolog.ast.logic.LogicOperator
import prolog.ast.terms.Atom import prolog.ast.terms.*
import prolog.ast.terms.Structure
import prolog.ast.terms.Term
import prolog.ast.terms.Variable
// Apply substitutions to a term // Apply substitutions to a term
fun applySubstitution(term: Term, subs: Substitutions): Term = when { fun applySubstitution(term: Term, subs: Substitutions): Term = when {
@ -62,7 +59,10 @@ fun unifyLazy(term1: Term, term2: Term, subs: Substitutions): Answers = sequence
val t2 = applySubstitution(term2, subs) val t2 = applySubstitution(term2, subs)
when { when {
equivalent(t1, t2, subs) -> yield(Result.success(emptyMap())) equivalent(t1, t2, subs) -> {
yield(Result.success(emptyMap()))
}
variable(t1, subs) -> { variable(t1, subs) -> {
val variable = t1 as Variable val variable = t1 as Variable
if (!occurs(variable, t2, subs)) { if (!occurs(variable, t2, subs)) {
@ -85,19 +85,7 @@ fun unifyLazy(term1: Term, term2: Term, subs: Substitutions): Answers = sequence
val args1 = structure1.arguments val args1 = structure1.arguments
val args2 = structure2.arguments val args2 = structure2.arguments
if (args1.size == args2.size) { if (args1.size == args2.size) {
// Keep track of the substitutions so far yieldAll(unifyArgs(args1, args2, subs))
val results = args1.zip(args2).map { (arg1, arg2) ->
unifyLazy(arg1, arg2, subs)
}
// Combine the results of all unifications
val combinedResults = results.reduce { acc, result ->
acc.flatMap { a ->
result.map { b ->
if (a.isSuccess && b.isSuccess) a.getOrThrow() + b.getOrThrow() else emptyMap()
}
}.map { Result.success(it) }
}
yieldAll(combinedResults)
} }
} }
} }
@ -131,6 +119,32 @@ fun unifyLazy(term1: Term, term2: Term, subs: Substitutions): Answers = sequence
} }
} }
private fun unifyArgs(
args1: kotlin.collections.List<Argument>,
args2: kotlin.collections.List<Argument>,
subs: Substitutions
): Answers = sequence {
// Using the current subs, unify the first argument of each list
val arg1 = applySubstitution(args1[0], subs)
val arg2 = applySubstitution(args2[0], subs)
unifyLazy(arg1, arg2, subs).forEach { headResult ->
// Use the resulting substitutions to unify the rest of the arguments
headResult.map { headSubs ->
val rest1 = args1.drop(1).map { applySubstitution(it, headSubs) }
val rest2 = args2.drop(1).map { applySubstitution(it, headSubs) }
if (rest1.isEmpty() && rest2.isEmpty()) {
yield(Result.success(headSubs))
} else if (rest1.isNotEmpty() && rest2.isNotEmpty()) {
unifyArgs(rest1, rest2, subs).forEach { tailResult ->
tailResult.map { tailSubs ->
yield(Result.success(headSubs + tailSubs))
}
}
}
}
}
}
fun unify(term1: Term, term2: Term): Answer { fun unify(term1: Term, term2: Term): Answer {
val substitutions = unifyLazy(term1, term2, emptyMap()).iterator() val substitutions = unifyLazy(term1, term2, emptyMap()).iterator()
return if (substitutions.hasNext()) { return if (substitutions.hasNext()) {
@ -151,6 +165,7 @@ fun equivalent(term1: Term, term2: Term, subs: Substitutions): Boolean {
equivalent(arg1, arg2, subs) equivalent(arg1, arg2, subs)
} }
} }
term1 is Integer && term2 is Integer -> compare(term1, term2, subs) == 0 term1 is Integer && term2 is Integer -> compare(term1, term2, subs) == 0
term1 is Number && term2 is Number -> compare(term1, term2, subs) == 0 term1 is Number && term2 is Number -> compare(term1, term2, subs) == 0
term1 is Variable && term2 is Variable -> term1 == term2 term1 is Variable && term2 is Variable -> term1 == term2