Mein Spielzeug-Prolog habe ich jetzt um einen cut Kombinator erweitert. Damit ist es zB. möglich, logische Negation so zu definieren:
Code: Alles auswählen
def no(mf:Mf) -> Mf:
return amb(
seq(mf, cut, fail),
unit,
)
Code: Alles auswählen
not(X) :- X, !, fail.
not(_).
Erreicht habe ich das durch Einführung von zwei zusätzlichen Continuations, einer Failure- und einer Escape-Continuation, zusätzlich zur bereits bestehenden Success-Continuation. Es ist also jetzt triple-barrelled. Dadurch wurde es auch möglich, alternative Ausführungspfade zu serialisieren, statt nacheinander in einem for-Loop durchzugehen, also den gesamten Ausführungsplan in einen Stack zu verwandeln. Alternativen werden einfach in die Failure-Continuation eingefädelt. Die Escape-Continuation wird zu Beginn jeder amb()-Auswertung auf die aktuelle Failure-Continuation gesetzt (vereinfachter Code):
Code: Alles auswählen
def choice(mf:Mf, mg:Mf) -> Mf:
def mh(v:Value) -> Ma:
def ma(y:Success, n:Failure, e:Escape) -> Solutions:
def on_fail() -> Solutions:
return mg(v)(y, n, e)
return mf(v)(y, on_fail, e)
return ma
return mh
def amb(mfs:Iterable[Mf]) -> Mf:
def mf(v:Value) -> Ma:
def ma(y:Success, n:Failure, e:Escape) -> Solutions:
return functools.reduce(choice, mfs, fail)(v)(y, n, n) # <- 2 x n, nicht n und e!
return ma
return mf
Code: Alles auswählen
def cut(v:Value) -> Ma:
def ma(y:Success, n:Failure, e:Escape) -> Solutions:
return y(v, e) # <- statt n wird e als Backtrackingpfad gesetzt.
return ma
Wegen der tief geschachtelten Closures habe ich alle lambda-Funktionen in richtige, benannte, Funktionen geändert und ich habe Type Hints verwendet. Das hat mir sehr dabei geholfen, den Überblick zu bewahren und mypy hat mir gesagt, wenn ich diese Closures falsch aufgerufen habe. Ich glaube, dass Type Hints selten nützlich sind und meistens nur nutzlosen optischen Lärm erzeugen, aber in manchen, seltenen Fällen wie hier, sind sie wirklich eine gute Sache.
Jedenfalls ist Yogic jetzt, mit cut, schon viel weniger ein Spielzeug. Hier ein Beispiel, wie man es verwenden kann:
Code: Alles auswählen
@predicate
def child(a, b): # a is das Kind von b:
return amb(
unify([a, b], ['bob', 'archimedes']),
unify([a, b], ['daisy', 'fluffy']),
unify([a, b], ['fluffy', 'fifi']),
unify([a, b], ['athene', 'zeus']),
)
@predicate
def descendant(a, c): # a ist ein Nachfahre von b:
b = var()
return amb(
child(a, c),
seq(child(a, b), descendant(b, c)),
)
x = var()
y = var()
for each in resolve(descendant(x, y)):
print(each[x], each[y])
Code: Alles auswählen
bob archimedes
daisy fluffy
fluffy fifi
athene zeus
daisy fifi
Meine Bitte: Kommentare, Meinungen und Verrisse. Und hat jemand einen Home-Office Job zu vergeben? Oder einen im süd-östlichen Allgäu?