Cleanup 1

This commit is contained in:
Tibo De Peuter 2025-05-09 14:43:20 +02:00
parent 026218ddbd
commit 2089e20da5
Signed by: tdpeuter
GPG key ID: 38297DE43F75FFE2
4 changed files with 88 additions and 97 deletions

View file

@ -18,7 +18,7 @@ import prolog.logic.numbervars
*/ */
object Fail : Atom("fail"), Body { object Fail : Atom("fail"), Body {
override fun satisfy(subs: Substitutions): Answers = emptySequence() override fun satisfy(subs: Substitutions): Answers = emptySequence()
override fun applySubstitution(subs: Substitutions): Fail = Fail override fun applySubstitution(subs: Substitutions): Fail = this
} }
/** /**
@ -31,7 +31,7 @@ typealias False = Fail
*/ */
object True : Atom("true"), Body { object True : Atom("true"), Body {
override fun satisfy(subs: Substitutions): Answers = sequenceOf(Result.success(emptyMap())) override fun satisfy(subs: Substitutions): Answers = sequenceOf(Result.success(emptyMap()))
override fun applySubstitution(subs: Substitutions): True = True override fun applySubstitution(subs: Substitutions): True = this
} }
// TODO Repeat/0 // TODO Repeat/0
@ -41,7 +41,7 @@ class Cut() : Atom("!") {
return sequenceOf(Result.failure(AppliedCut(emptyMap()))) return sequenceOf(Result.failure(AppliedCut(emptyMap())))
} }
override fun applySubstitution(subs: Substitutions): Cut = Cut() override fun applySubstitution(subs: Substitutions): Cut = this
} }
/** /**
@ -50,83 +50,63 @@ class Cut() : Atom("!") {
open class Conjunction(val left: LogicOperand, private val right: LogicOperand) : open class Conjunction(val left: LogicOperand, private val right: LogicOperand) :
LogicOperator(Atom(","), left, right) { LogicOperator(Atom(","), left, right) {
override fun satisfy(subs: Substitutions): Answers = sequence { override fun satisfy(subs: Substitutions): Answers = sequence {
fun satisfyRight(leftSubs: Substitutions): Answers = sequence {
right.satisfy(subs + leftSubs).forEach { right ->
right.fold(
// If the right part succeeds, yield the result with the left substitutions
onSuccess = { rightSubs ->
yield(Result.success(leftSubs + rightSubs))
},
onFailure = { exception ->
// If the right part fails, check if it's a cut
if (exception is AppliedCut) {
// If it's a cut, yield the result with the left substitutions
val newSubs = if (exception.subs != null) leftSubs + exception.subs else null
yield(Result.failure(AppliedCut(newSubs)))
return@sequence
}
// If it's not a cut, yield the failure
yield(Result.failure(exception))
}
)
}
}
fun findNextCutSolution(appliedCut: AppliedCut): Answers = sequence {
val leftSubs = appliedCut.subs
right.satisfy(subs + (appliedCut.subs!!)).firstOrNull()?.map { rightSubs ->
// If the right part succeeds, yield the result with the left substitutions
yield(Result.success(leftSubs + rightSubs))
return@sequence
} ?: yield(Result.failure(AppliedCut()))
}
// Satisfy the left part first, which either succeeds or fails // Satisfy the left part first, which either succeeds or fails
left.satisfy(subs).forEach { left -> left.satisfy(subs).forEach { left ->
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 -> yieldAll(satisfyRight(leftSubs)) },
right.satisfy(subs + leftSubs).forEach { right ->
right.fold(
// If the right part succeeds, yield the result with the left substitutions
onSuccess = { rightSubs ->
yield(Result.success(leftSubs + rightSubs))
},
// If the right part fails, check if it's a cut
onFailure = { exception ->
if (exception is AppliedCut) {
if (exception.subs != null) {
// If it's a cut, yield the result with the left substitutions
yield(Result.failure(AppliedCut(leftSubs + exception.subs)))
} else {
yield(Result.failure(AppliedCut()))
}
return@sequence
}
// If it's not a cut, yield the failure
yield(Result.failure(exception))
}
)
}
},
// If it fails, check these conditions: // If it fails, check these conditions:
onFailure = { exception -> onFailure = { exception ->
// 1. If the left part is a cut, satisfy the right part ONCE, and stop searching for more solutions when (exception) {
if (exception is AppliedCut) { // 1. If the left part is a cut, satisfy the right part ONCE, and stop searching for more solutions
right.satisfy(subs + (exception.subs!!)).firstOrNull()?.fold( is AppliedCut -> yieldAll(findNextCutSolution(exception))
onSuccess = {
// If the right part succeeds, yield the result with the left substitutions
yield(Result.success(exception.subs + it))
return@sequence
},
onFailure = {
// If the right part fails, yield the failure
yield(Result.failure(it))
}
) ?: yield(Result.failure(AppliedCut()))
} else if (exception is AppliedShift) {
if (exception.cont == null) {
// Goal should be our right operand
yield(Result.failure(AppliedShift(exception.subs, exception.ball, right as Goal)))
} else {
val leftSubs = exception.subs
right.satisfy(subs + leftSubs).forEach { right ->
right.fold(
// If the right part succeeds, yield the result with the left substitutions
onSuccess = { rightSubs ->
yield(Result.success(leftSubs + rightSubs))
},
// If the right part fails, check if it's a cut
onFailure = { exception ->
if (exception is AppliedCut) {
if (exception.subs != null) {
// If it's a cut, yield the result with the left substitutions
yield(Result.failure(AppliedCut(leftSubs + exception.subs)))
} else {
yield(Result.failure(AppliedCut()))
}
return@sequence
}
// If it's not a cut, yield the failure // 2. If the left part is a shift, we need to check if the right part can be satisfied
yield(Result.failure(exception)) is AppliedShift -> {
} if (exception.cont == null) {
) // Pass the right of our disjunction as the continuation
val newShift = AppliedShift(exception.subs, exception.ball, right as Goal)
yield(Result.failure(newShift))
} else {
// Satisfy the right part with the updated substitutions
yieldAll(satisfyRight(exception.subs))
} }
} }
} else {
// 2. Any other failure should be returned as is // 3. Any other failure should be returned as is
yield(Result.failure(exception)) else -> yield(Result.failure(exception))
} }
} }
) )

View file

@ -4,52 +4,54 @@ import prolog.Answers
import prolog.Substitutions import prolog.Substitutions
import prolog.ast.arithmetic.Integer import prolog.ast.arithmetic.Integer
import prolog.ast.terms.Atom import prolog.ast.terms.Atom
import prolog.ast.terms.CompoundTerm
import prolog.ast.terms.Goal import prolog.ast.terms.Goal
import prolog.ast.terms.Structure import prolog.ast.terms.Structure
import prolog.ast.terms.Term import prolog.ast.terms.Term
import prolog.flags.AppliedShift import prolog.flags.AppliedShift
import prolog.logic.applySubstitution
import prolog.logic.unifyLazy import prolog.logic.unifyLazy
class Reset(private val goal: Goal, private val term1: Term, private val cont: Term) : /**
Structure(Atom("reset"), listOf(goal, term1, cont)) { * These classes provide support for delimited continuations in Prolog.
* More specifically, they implement the reset/3 and shift/1 operators.
*
* See also [SWI-Prolog 4.9 Delimited Continuations](https://www.swi-prolog.org/pldoc/man?section=delcont)
*/
/**
* Call [Goal]. If Goal calls [Shift], and the arguments of Shift can be unified with Ball, Shift causes Reset to
* return, unifying Cont with a Goal that represents the continuation after shift.
*/
class Reset(private val goal: Goal, private val ball: Term, private val cont: Term) :
Structure(Atom("reset"), listOf(goal, ball, cont)) {
override fun satisfy(subs: Substitutions): Answers = sequence { override fun satisfy(subs: Substitutions): Answers = sequence {
goal.satisfy(subs).forEach { goalResult -> goal.satisfy(subs).forEach { goalResult ->
goalResult.fold( goalResult.fold(
// If Goal succeeds, then reset/3 also succeeds and binds Cont and Term1 to 0. // If Goal succeeds, then reset/3 also succeeds and binds Cont and Term1 to 0.
onSuccess = { goalSubs -> onSuccess = { goalSubs ->
// Unify Cont with 0
unifyLazy(cont, Integer(0), goalSubs).forEach { contResult -> unifyLazy(cont, Integer(0), goalSubs).forEach { contResult ->
contResult.map { contSubs -> contResult.map { contSubs ->
yield(Result.success(goalSubs + contSubs)) yield(Result.success(goalSubs + contSubs))
// // Unify Term1 with 0
// unifyLazy(term1, Integer(0), goalSubs + contSubs).forEach { termResult ->
// termResult.map { termSubs ->
// yield(Result.success(goalSubs + termSubs + contSubs))
// }
// }
} }
} }
// TODO
// val conjunction = Conjunction(Unify(term1, Integer(0)), Unify(cont, Integer(0)))
// yieldAll(conjunction.satisfy(goalSubs))
}, },
onFailure = { failure -> onFailure = { failure ->
// If at some point Goal calls shift(Term2), then its further execution is suspended. // If at some point Goal calls shift(Term2), then its further execution is suspended.
// Reset/3 succeeds immediately, binding Term1 to Term2 and Cont to the remainder of Goal.
// If Goal fails, then reset also fails.
// yield(Result.failure(failure))
// Unify Term1 with the ball
if (failure is AppliedShift) { if (failure is AppliedShift) {
require(failure.cont != null) { "Shift must have a continuation" } require(failure.cont != null) { "Shift must have a continuation" }
val newSubs: Substitutions = mapOf( // Reset/3 succeeds immediately, binding Term1 to Term2 and Cont to the remainder of Goal.
term1 to failure.ball, val shiftSubs = failure.subs
cont to failure.cont val appliedBall = applySubstitution(failure.ball, shiftSubs)
) val appliedCont = applySubstitution(failure.cont, shiftSubs)
yield(Result.success(failure.subs + newSubs)) Conjunction(Unify(ball, appliedBall), Unify(appliedCont, cont))
.satisfy(shiftSubs)
.forEach { conResult ->
conResult.map { conSubs ->
yield(Result.success(shiftSubs + conSubs))
}
}
} else { } else {
// If Goal fails, then reset also fails.
yield(Result.failure(failure)) yield(Result.failure(failure))
} }
} }
@ -62,5 +64,12 @@ class Reset(private val goal: Goal, private val term1: Term, private val cont: T
* Variables that have been bound during the procedure called by reset/3 stay bound after a shift/1: * Variables that have been bound during the procedure called by reset/3 stay bound after a shift/1:
*/ */
class Shift(private val ball: Term) : Structure(Atom("shift"), listOf(ball)) { class Shift(private val ball: Term) : Structure(Atom("shift"), listOf(ball)) {
override fun satisfy(subs: Substitutions): Answers = sequenceOf(Result.failure(AppliedShift(subs, ball))) override fun satisfy(subs: Substitutions): Answers = sequence {
val shift = AppliedShift(
subs = subs,
ball = ball,
cont = null
)
yield(Result.failure(shift))
}
} }

View file

@ -7,7 +7,6 @@ import prolog.ast.lists.List.Cons
import prolog.ast.lists.List.Empty import prolog.ast.lists.List.Empty
import prolog.ast.terms.Atom import prolog.ast.terms.Atom
import prolog.ast.terms.Operator import prolog.ast.terms.Operator
import prolog.ast.terms.Structure
import prolog.ast.terms.Term import prolog.ast.terms.Term
import prolog.logic.applySubstitution import prolog.logic.applySubstitution

3
src/tool/mapEach.kt Normal file
View file

@ -0,0 +1,3 @@
package tool
fun <T, R> Sequence<T>.mapEach(transform: (T) -> R): Sequence<R> = map(transform)