Adventskalender 21. Dezember 2023 - Logische Programmierung

Smalltalk
Benutzeravatar
heisenberg
Beiträge: 3567
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: Adventskalender 21. Dezember 2023 - Logische Programmierung

Beitrag von heisenberg » 22.12.2023 16:35:17

Ich habe mir diese Webseite als Prolog Grundlagenkurs etwas angeschaut. Das scheint mir im Vergleich zu ein paar anderen das Beste zu sein, was ich gefunden habe:

https://user.phil.hhu.de/~petersen/WiSe ... rolog.html
Jede Rohheit hat ihren Ursprung in einer Schwäche.

chrbr
Beiträge: 551
Registriert: 29.10.2022 15:53:26

Re: Adventskalender 21. Dezember 2023 - Logische Programmierung

Beitrag von chrbr » 22.12.2023 18:04:02

Hallo @Heisenberg,
der Link ist wirklich gut. Das erste Kapitel hat ja schon Dinge erklärt, die mir unklar waren und Du auch schon erklärt hast. Mit deklarativen Programmiersprachen hatte ich bisher nichts zu tun. Ich freue mich auch, durch diesen Adventskalender einen Anlass zu haben, mich damit zu beschäftigen. Wir müssen ja nicht schon morgen den Guru Status erreichen. Übermorgen reicht vollkommen aus :-).
Viele Grüße, Christoph

Benutzeravatar
heisenberg
Beiträge: 3567
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: Adventskalender 21. Dezember 2023 - Logische Programmierung

Beitrag von heisenberg » 22.12.2023 18:26:53

Ich habe auch nochmal im SWI-Prolog-Forum eine Frage gestellt. Aber mit der Antwort kann ich (noch) nix anfangen...

https://swi-prolog.discourse.group/t/cr ... nts/7101/1
Jede Rohheit hat ihren Ursprung in einer Schwäche.

Benutzeravatar
TRex
Moderator
Beiträge: 8086
Registriert: 23.11.2006 12:23:54
Wohnort: KA

Re: Adventskalender 21. Dezember 2023 - Logische Programmierung

Beitrag von TRex » 22.12.2023 18:36:59

So fühlt es sich an, wenn einer aus nem Windows-Forum hierherkommt und ne Frage zu seinem Pi stellt :mrgreen: :lol:
Jesus saves. Buddha does incremental backups.
Windows ist doof, Linux funktioniert nichtDon't break debian!Wie man widerspricht

Benutzeravatar
heisenberg
Beiträge: 3567
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: Adventskalender 21. Dezember 2023 - Logische Programmierung

Beitrag von heisenberg » 22.12.2023 21:19:39

Inzwischen hat jemand im Prolog-Forum einen Fix für die Unique-Klausel gepostet:

Code: Alles auswählen

unique([]).
unique([_]).
unique([H|T]) :-  T\=[],
    \+ contains(T, H),
    unique(T).
Das funktioniert jetzt mit der Testfunktion auch so wie es soll. Erklärung im verlinkten Forenthread.
Jede Rohheit hat ihren Ursprung in einer Schwäche.

chrbr
Beiträge: 551
Registriert: 29.10.2022 15:53:26

Re: Adventskalender 21. Dezember 2023 - Logische Programmierung

Beitrag von chrbr » 23.12.2023 12:10:36

@Heisenberg: Vielen Dank für Deine Frage im Forum. Es ist wohl wirklich angebracht, den Debugger auszuprobieren. Normalerweise ist das aber nicht mein Ding. Bisher habe ich zum Probieren oder zur Fehlersuche in anderen Programmiersprachen Ausgaben auf dem Terminal ausgegeben.

Eine klitzekleine Kleinigkeit habe ich auch. In "rule4" kann man statt

Code: Alles auswählen

PA = 768358;
PB = 768358;
PD = 768358
auch gleich

Code: Alles auswählen

PC \= 768358
schreiben. Das "\=" wird im unique Fix benutzt, aber auch in dem von Dir empfohlenen Skript vorgestellt.

Benutzeravatar
heisenberg
Beiträge: 3567
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: Adventskalender 21. Dezember 2023 - Logische Programmierung

Beitrag von heisenberg » 23.12.2023 16:16:23

Hallo zusammen,

vielleicht gibt's ja nach Weihnachten noch ein paar hilfreiche Tips vom Content-Creator hier.

Ich habe hier auch nochmal eine einfache Einführung zu Prolog gesehen und mal reingelesen. Die finde ich ganz gelungen:

https://www.swisseduc.ch/informatik/pro ... folien.pdf

Ansonsten stelle ich hier noch mal meinen MYSQL-Ansatz vor, der natürlich bei Weitem nicht so logisch elegant ist, wie Prolog:

1. Grundlegende Datentabellen

Es gibt zunächst mal 3 Grundtabellen: city, population und station

Code: Alles auswählen

MariaDB [raetsel]> select * from city;
+-------------+
| name        |
+-------------+
| Krasnojarsk |
| Omsk        |
| Tjumen      |
| Novosibirsk |
+-------------+
4 rows in set (0,001 sec)

MariaDB [raetsel]> select * from station;
+------+
| name |
+------+
| A    |
| B    |
| C    |
| D    |
+------+
4 rows in set (0,001 sec)

MariaDB [raetsel]> select * from population;
+---------+
| count   |
+---------+
| 768358  |
| 1090811 |
| 1172070 |
| 1612833 |
+---------+
4 rows in set (0,001 sec)
2. Einfache Regelumsetzungen

Die einfachen Regeln kann man in einer Verknüpfungstabelle der obigen 3 Tabellen umsetzen.

Regel #1

Die Regel #1 "Krasnojarsk" hat nicht die höchste Bevölkerungszahl. Damit man auf der Basis weiterarbeiten kann, speichere ich das gleich als SQL-View ab:

Code: Alles auswählen

CREATE VIEW city_population AS 
    SELECT name,count 
    FROM city, population
    WHERE NOT (name="Krasnojarsk" and count = 1612833);
Regel #3

Regel 3 ist auch einfach. Hier verknüpfe ich Station und Stadt und schmeisse alle Kombinationen raus, in denen Omsk nicht Station B ist.

Code: Alles auswählen

CREATE VIEW city_station AS 
    SELECT station.name AS station_name, city.name AS city_name 
    FROM station 
    LEFT JOIN city ON
        NOT (
                   station.name = "A" and city.name="Omsk" 
                or station.name = "C" and city.name="Omsk" 
                or station.name = "D" and city.name="Omsk"
                ) ;

Regel #2 und Regel #4

Regeln #2 und #4 sind auch noch einfach und deswegen reicht auch hier eine Verknüpfungstabelle zwischen station und population.

Code: Alles auswählen

CREATE VIEW station_population AS 
    SELECT station.name, population.count 
    FROM station 
    LEFT JOIN population ON NOT (
            name="A" AND count = 1090811 
        OR  name="B" AND count = 1090811 
        OR  name="C" AND count = 1090811 
        OR  name="C" AND count < 1000000
        );
Regel #2: Alle Datensätze, in denen die Station anders als D ist und die Bewohnerzahl 1090811 hat aussortieren
Regel #4: alle Datensätze, bei denen Station C weniger als 1000000 Einwohner hat aussortieren

Komplexe Regel #5 - Teil 1

Jetzt wird es schwieriger. Regel 5 benötigt die Verknüpfung aller 3 Tabellen. Dazu baue ich auf den bereits vorhandenen Verknüpfungstabellen, die die vorhandenen Regeln abbilden eine zusammengesetzte Tabelle. Dazu benötige ich grundsätzlich eine Verknüpfung der gemeinsamen Felder (Schlüsselfelder) der Tabellen:

D. h. wenn ich city_population und city_station verknüpfe, muss ich das mit dem gemeinsamen Schlüssel city_name tun. Das gleiche gilt für die Tabelle city_station und station_population, wobei hier der gemeinsame Schlüssel station_name ist. Zum Dritten noch das gleiche mit station_population und city_population mit dem Schüssel population.

Damit ich einzelne Datensätze aussortieren kann muss ich deswegen wieder mit einem negierten JOIN arbeiten; denn würde ich mit einem WHERE arbeiten, würde ich nur die Menge erhalten, die ich ja nicht will.

Code: Alles auswählen

CREATE VIEW csp AS 
    SELECT city_station.station_name, city_station.city_name, station_population.count 
    FROM city_population, city_station 
    INNER JOIN station_population ON 
            city_station.station_name = station_population.name 
        AND NOT ( city_name = "Tjumen" AND station_name = "D") 
        AND NOT ( station_name = "A" AND count = 1172070 ) 
    WHERE       city_population.name            = city_station.city_name 
            AND city_population.count           = station_population.count ;
Zur Erklärung der übrigen Regeln: Die logischen Folgen aus Regel 5, dass eine Station mit der Bevölkerungszahl 1172070 der Station in der Stadt Tjumen folgt, ist, dass Tjumen nicht die letzte Station sein kann und die Stadt mit der Bevölkerungszahl 1172070 nicht die erste sein kann. Diese beiden Elemente werden mit dem negierten INNER JOIN aussortiert.

Damit ist jetzt in der VIEW csp eine Liste von Möglichkeiten, welche Wegpunkte in welcher Stadt mit welcher Bevölkerungszahl noch in Frage kommt. Das sind gar nicht mehr so viele.

Zwei SQL-Funktionen

Jetzt brauchen wir noch 2 SQL-Funktionen, die abbilden, dass jede Stadt nur einmal Wegpunkt sein darf. Ich nenne das gleich wie in der Prolog-Variante "UNIQUE". Da MySQL aber typisierte Variablen hat, braucht es dafür 2 Funktionen, eine für Strings und eine für Zahlen:

Code: Alles auswählen

DELIMITER $$
CREATE FUNCTION unique_str(IN a VARCHAR(30), IN b VARCHAR(30), IN c VARCHAR(30), IN d VARCHAR(30)) RETURNS INT  
BEGIN 
    IF (a!=b AND a!=c AND a!=d AND b!=c AND b!=d AND c!=d) THEN 
        RETURN 1;  
    ELSE 
        RETURN 0; 
    END IF; 
END;
$$

DELIMITER $$
CREATE FUNCTION unique_int(IN a INT, IN b INT, IN c INT, IN d INT) RETURNS INT  
BEGIN 
    IF (a!=b AND a!=c AND a!=d AND b!=c AND b!=d AND c!=d) THEN 
        RETURN 1;  
    ELSE 
        RETURN 0; 
    END IF; 
END;
$$
Finale: Regel #5 - Teil 2

Jetzt kommt das grosse Finale. Hier wird die Tabelle der einzelnen Wegpunkte (Station, Stadt, Bevölkerungszahl, ...) in den Weg tranponiert, also von dem Zustand ...

Code: Alles auswählen

A, Krasnojarsk, 1172070
A, Tjumen, 1090811
...
D, Novosivirsk, 1612833
... in diesen ...

Code: Alles auswählen

Krasnojarsk, 1172070, Omsk, 1234567, Tjumen, 1090811, Novosivirsk, 1612833
...
Tjumen, 1234567, Omsk ,1172070, Tjumen, 1090811, Novosivirsk, 1612833
... wobei dann am Schluss nur noch ein Datensatz übrig bleiben sollte: Die Lösung.

Dazu spanne ich den Select über die gleiche Tabelle mit mehren Aliasen, wähle für die jeweilige Station den entsprechenden Stationsnamen aus und filtere das ganze durch die beiden Uniquefunktionen, damit sowohl Bevölkerungszahl als auch Stadtname nur jeweils einmal vorkommen:

Code: Alles auswählen

SELECT  csp_a.city_name, csp_a.count,
        csp_b.city_name, csp_b.count, 
        csp_c.city_name, csp_c.count, 
        csp_d.city_name, csp_d.count 
FROM csp AS csp_a 
LEFT JOIN csp AS csp_b ON csp_b.station_name = "B" 
LEFT JOIN csp AS csp_c ON csp_c.station_name = "C" 
LEFT JOIN csp AS csp_d ON csp_d.station_name = "D"
WHERE       csp_a.station_name = "A" 
        AND unique_str(csp_a.city_name,csp_b.city_name,csp_c.city_name,csp_d.city_name) 
        AND unique_int(csp_a.count, csp_b.count,csp_c.count,csp_d.count);
Jetzt muss noch der 2. Teil der 5. Regel erfüllt werden. Die Stadt, die Tjumen folgt, hat 1172070 Einwohner. Hier gibt es drei mögliche Konstellationen, die ich selektiere:

Das wäre dann die Bedingung dazu:

Code: Alles auswählen

    csp_a.city_name = "Tjumen" AND csp_b.count=1172070 
OR  csp_b.city_name = "Tjumen" AND csp_c.count=1172070 
OR  csp_c.city_name = "Tjumen" AND csp_d.count=1172070
Als komplette Abfrage ergibt sich dann:

Code: Alles auswählen

SELECT  csp_a.city_name, csp_a.count,
        csp_b.city_name, csp_b.count, 
        csp_c.city_name, csp_c.count, 
        csp_d.city_name, csp_d.count 
FROM csp AS csp_a 
LEFT JOIN csp AS csp_b ON csp_b.station_name = "B" 
LEFT JOIN csp AS csp_c ON csp_c.station_name = "C" 
LEFT JOIN csp AS csp_d ON csp_d.station_name = "D"
WHERE       csp_a.station_name = "A" 
        AND unique_str(csp_a.city_name,csp_b.city_name,csp_c.city_name,csp_d.city_name) 
        AND unique_int(csp_a.count, csp_b.count,csp_c.count,csp_d.count)
        and (   
                csp_a.city_name = "Tjumen" AND csp_b.count=1172070 
            OR  csp_b.city_name = "Tjumen" AND csp_c.count=1172070 
            OR  csp_c.city_name = "Tjumen" AND csp_d.count=1172070
            );
Dieser Select gibt jetzt die Lösung aus.

Als Verbesserungsmöglichkeit könnte man die Station noch numerisch setzen und dann hat man statt 3 Einzelabfragen bzgl. Regel #5-Teil2 nur noch eine einzelne komplexe Abfrage, was dann mehr eine klare logisch strukturierte Lösung wäre.

Hier ist noch der Datenbankdump der MariaDB dazu: NoPaste-Eintrag42056

Ich glaube im Vergleich sieht man, dass es bestimmt sehr lohnenswert ist, zu verstehen, wie das mit Prolog geht, weil das viel weniger Code ist.
Jede Rohheit hat ihren Ursprung in einer Schwäche.

Benutzeravatar
paedubucher
Beiträge: 856
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: Adventskalender 21. Dezember 2023 - Logische Programmierung

Beitrag von paedubucher » 23.12.2023 16:43:55

Meillo hat geschrieben: ↑ zum Beitrag ↑
22.12.2023 16:32:26
TRex ist traumatisiert und paedubucher scheint sich entspannt zurueckzulehnen ... Wir stuempern mit unserem Halbwissen rum, dabei ist es bei einer so indirekten/high-level Sprache wie Prolog besonders wichtig, zu *wissen* was man tut und Trial'n'Error kaum hilfreich.
Ich war gestern noch sehr beschäftigt und heute war ich vorübergehend ausser Kraft gesetzt, aber ich werde mich noch einmal tiefer in die Materie einarbeiten und kann dann hoffentlich noch etwas dazu beitragen.

Dazu etwas Hintergrund: Ich habe Prolog etwa vor 5 Jahren im Studium für 3-4 Wochen behandelt und hatte damals seine Funktionsweise recht gut verstanden. Für dieses Türchen wollte ich mich wieder in die Materie einarbeiten und habe es gerade soweit gebracht, dass das Logical eine Lösung ausspuckt, und ich den Text hierzu schrieb. Erst am Abend vor der Veröffentlichung des Beitrags habe ich anhand einer geografischen Karte versucht meine Lösung du verifizieren, bin dann aber erschrocken, dass etwas nicht stimmt. Und da ich auf die Schnelle nicht auf die Lösung kam, habe ich dieses Problem als Übung zum Türchen formuliert.

So tappe ich auch noch im Dämmerlicht, krame aber jetzt einmal meine alten Notizen zur Vorlesung hervor.

Nachtrag: Das Rätselbuch gibt die Lösungen an, so können wir ein paar Sachen schon einmal verifizieren:
  • Tjumen (768.358): A ist falsch, PA ist korrekt
  • Omsk (1.172.070): B ist korrekt (da gegeben), PB ist falsch
  • Novosibirsk (1.612.833): C ist falsch, PC ist falsch
  • Krasnojarsk (1.090.811): D ist falsch, PD ist falsch
Was hingegen stimmt: Novosibirsk hat eine Bevölkerungszahl von 1.612.833.

Nach-Nachtrag: In der swipl-Konsole kann man mit einem Punkt abbrechen und mit einem Semikolon weitere Lösungen anzeigen lassen. Offenbar ist die erste Lösung nicht die einzige.
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

chrbr
Beiträge: 551
Registriert: 29.10.2022 15:53:26

Re: Adventskalender 21. Dezember 2023 - Logische Programmierung

Beitrag von chrbr » 23.12.2023 17:39:04

Meine unelegante Lösung in Python ist dort: NoPaste-Eintrag42057. Die Ergebnisse decken sich mit Einwohnerzahlen gemäß Wikipedia. Aber das nur als Referenz. Mit Prolog bin ich noch nicht so weit.

Benutzeravatar
paedubucher
Beiträge: 856
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: Adventskalender 21. Dezember 2023 - Logische Programmierung

Beitrag von paedubucher » 23.12.2023 17:43:43

Der Kater vom Vorabend und der Nebel, der sich nach fünf Jahre Prolog-Abstinenz vor meinen Augen gebildet hat, lichten sich. Ich habe euch und mich selbst mit dem Prädikat namens has_population/2 wohl etwas an der Nase herumgeführt. Dieses sagt bloss aus, dass das erste Argument eine Stadt und das zweite eine Bevölkrungszahl sein muss. Eine Zuordnung der beiden Variablen findet aber nicht statt.

So kann has_population/2 gebraucht werden, um die ersten paar Regeln etwas kompakter zu formulieren. Statt:

Code: Alles auswählen

city(A),
population(PA),
schreibt man:

Code: Alles auswählen

has_population(A, PA),
Das ist eine kleine Vereinfachung. Die Konsequenz daraus ist aber, dass wir die Zuordnungen von Städten zu Bevölkerungen per Regeln ausdrücken müssen, etwa so:

Code: Alles auswählen

A = X, PA = PX;
B = X, PB = PX;
C = X, PC = PX;
D = X, PD = PX
Also überall, wo wir einen Oder-Block mit runden Klammern mit has_population/2-Verwendungen hatten, muss nun der Block umformuliert werden. Die Variablen X und PX im obigen Beispiel matchen auf city(X) und population(PX). Damit sollte sich das Rätsel wohl lösen lassen!
Zuletzt geändert von paedubucher am 23.12.2023 17:56:37, insgesamt 3-mal geändert.
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

Benutzeravatar
paedubucher
Beiträge: 856
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: Adventskalender 21. Dezember 2023 - Logische Programmierung

Beitrag von paedubucher » 23.12.2023 17:45:01

chrbr hat geschrieben: ↑ zum Beitrag ↑
23.12.2023 17:39:04
Meine unelegante Lösung in Python ist dort: NoPaste-Eintrag42057. Die Ergebnisse decken sich mit Einwohnerzahlen gemäß Wikipedia. Aber das nur als Referenz. Mit Prolog bin ich noch nicht so weit.
Das ist in ungefähr das, was Prolog im Hinergrund macht :THX:

Bei Prolog sieht es bloss eleganter aus :wink:
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

Benutzeravatar
heisenberg
Beiträge: 3567
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: Adventskalender 21. Dezember 2023 - Logische Programmierung

Beitrag von heisenberg » 23.12.2023 19:16:22

paedubucher hat geschrieben: ↑ zum Beitrag ↑
23.12.2023 17:43:43
So kann has_population/2 gebraucht werden, um die ersten paar Regeln etwas kompakter zu formulieren. Statt:

Code: Alles auswählen

city(A),
population(PA),
schreibt man:

Code: Alles auswählen

has_population(A, PA),
Das ist eine kleine Vereinfachung.
Das verstehe ich.
Die Konsequenz daraus ist aber, dass wir die Zuordnungen von Städten zu Bevölkerungen per Regeln ausdrücken müssen, etwa so:

Code: Alles auswählen

A = X, PA = PX;
B = X, PB = PX;
C = X, PC = PX;
D = X, PD = PX
Also überall, wo wir einen Oder-Block mit runden Klammern mit has_population/2-Verwendungen hatten, muss nun der Block umformuliert werden. Die Variablen X und PX im obigen Beispiel matchen auf city(X) und population(PX). Damit sollte sich das Rätsel wohl lösen lassen!
Das verstehe ich überhaupt nicht. Kannst Du das nochmal anders und mit konkreten Code-Beispielen des vorliegenden Codes erläutern?
Jede Rohheit hat ihren Ursprung in einer Schwäche.

chrbr
Beiträge: 551
Registriert: 29.10.2022 15:53:26

Re: Adventskalender 21. Dezember 2023 - Logische Programmierung

Beitrag von chrbr » 23.12.2023 19:21:57

Ich habe jetzt eine korrekte Lösung in Prolog, bei der ich allerdings has_population und comes_right_after_in zu Fuß mit "Oder" Verknüpfungen erledigt habe. Das sind etwas längere verschachtelte Klammerausdrücke. Die Erklärung bzw der Umbau von has_population ist mir noch nicht klar. Das schaue ich mir noch an. Das temporäre "Machwerk" ist wie das Python Skript 3 Monate verfügbar. Die Prolog Datei ist dort: NoPaste-Eintrag42058

Benutzeravatar
paedubucher
Beiträge: 856
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: Adventskalender 21. Dezember 2023 - Logische Programmierung

Beitrag von paedubucher » 23.12.2023 19:38:17

heisenberg hat geschrieben: ↑ zum Beitrag ↑
23.12.2023 19:16:22
paedubucher hat geschrieben: ↑ zum Beitrag ↑
23.12.2023 17:43:43
Die Konsequenz daraus ist aber, dass wir die Zuordnungen von Städten zu Bevölkerungen per Regeln ausdrücken müssen, etwa so:

Code: Alles auswählen

A = X, PA = PX;
B = X, PB = PX;
C = X, PC = PX;
D = X, PD = PX
Also überall, wo wir einen Oder-Block mit runden Klammern mit has_population/2-Verwendungen hatten, muss nun der Block umformuliert werden. Die Variablen X und PX im obigen Beispiel matchen auf city(X) und population(PX). Damit sollte sich das Rätsel wohl lösen lassen!
Das verstehe ich überhaupt nicht. Kannst Du das nochmal anders und mit konkreten Code-Beispielen des vorliegenden Codes erläutern?
Folgendes bedeutet:

Code: Alles auswählen

has_population(A, PA)
dass A eine Stadt und PA eine Bevölkerungszahl ist. Es bedeutet nicht, dass PA die Bevölkerungszahl von Stadt A ist.

Wenn jetzt X eine gesuchte Stadt mit der Bevölkerung PX ist (siehe Ende der stations/8-Regel), dann muss man A und PA noch auf X und PX matchen, oder B und PB auf X und PX usw. Dieser Und-Zusammenhang hat noch gefehlt.

Edit: Ich löse mal die erste Regel. Die fünfte Regel sollte sich dann mit comes_right_after_in/3 analog dazu lösen lassen.

Code: Alles auswählen

    % K wie "Krasnojansk"
    population(PK),
    PK \= 1612833,
    (
        PA = PK, A = krasnojarsk;                                                                         
        PB = PK, B = krasnojarsk;                                                                         
        PC = PK, C = krasnojarsk;                                                                         
        PD = PK, D = krasnojarsk                                                                          
    ),
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

chrbr
Beiträge: 551
Registriert: 29.10.2022 15:53:26

Re: Adventskalender 21. Dezember 2023 - Logische Programmierung

Beitrag von chrbr » 23.12.2023 20:06:59

Ist der Code Block für Regel 1 dann äquivalent zu dem Block unten? Das würde ja bedeuten, dass einige Klammern überflüssig sind.

Code: Alles auswählen

    % rule 1
    (
        (
            A = krasnnojarsk,
            PA \= 1612833
        );
        (
            B = krasnnojarsk,
            PB \= 1612833
        );
        (
            C = krasnnojarsk,
            PC \= 1612833
        );
        (
            D = krasnnojarsk,
            PD \= 1612833
        )

Benutzeravatar
paedubucher
Beiträge: 856
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: Adventskalender 21. Dezember 2023 - Logische Programmierung

Beitrag von paedubucher » 23.12.2023 22:38:21

chrbr hat geschrieben: ↑ zum Beitrag ↑
23.12.2023 20:06:59
Ist der Code Block für Regel 1 dann äquivalent zu dem Block unten? Das würde ja bedeuten, dass einige Klammern überflüssig sind.

Code: Alles auswählen

    % rule 1
    (
        (
            A = krasnnojarsk,
            PA \= 1612833
        );
        (
            B = krasnnojarsk,
            PB \= 1612833
        );
        (
            C = krasnnojarsk,
            PC \= 1612833
        );
        (
            D = krasnnojarsk,
            PD \= 1612833
        )
Ja, da Und stärker bindet als Oder.
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

Benutzeravatar
paedubucher
Beiträge: 856
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: Adventskalender 21. Dezember 2023 - Logische Programmierung

Beitrag von paedubucher » 23.12.2023 22:40:47

Hier ist nun meine funktionierende Lösung: NoPaste-Eintrag42060

Ein paar Anmerkungen: mit maplist/2 kann man ein Prädikat auf eine Liste von Variablen anwenden:

Code: Alles auswählen

    maplist(city, [A, B, C, D]),
    maplist(population, [PA, PB, PC, PD]),
Die erste Regel:

Code: Alles auswählen

    population(PK),
    PK \= 1612833,
    (
        PA = PK, A = krasnojarsk;
        PB = PK, B = krasnojarsk;
        PC = PK, C = krasnojarsk;
        PD = PK, D = krasnojarsk
    ),
Und die fünfte Regel:

Code: Alles auswählen

    comes_right_after_in(X, tjumen, [A, B, C, D]),
    (
        PA = 1172070, A = X;
        PB = 1172070, B = X;
        PC = 1172070, C = X;
        PD = 1172070, D = X
    ).
Komischerweise wird die gleiche Lösung mehrmals gefunden, d.h. wohl auf verschiedenen Pfaden im Regelbaum. Mithilfe von distinct/1 kann man aber mehrdeutige Lösungen ignorieren:

Code: Alles auswählen

$ swipl transsib.pl
?- distinct(stations(A, PA, B, PB, C, PC, D, PD)).
A = tjumen,
PA = 768358,
B = omsk,
PB = 1172070,
C = novosibirsk,
PC = 1612833,
D = krasnojarsk,
PD = 1090811 ;
false.
Nach der erste Lösung habe ich ";" eingetippt, um eine weitere anzeigen zu lassen. Die Antwort darauf lautet "false", da es keine weiteren Lösungen mehr gibt.
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

chrbr
Beiträge: 551
Registriert: 29.10.2022 15:53:26

Re: Adventskalender 21. Dezember 2023 - Logische Programmierung

Beitrag von chrbr » 23.12.2023 22:58:10

paedubucher hat geschrieben: ↑ zum Beitrag ↑
23.12.2023 22:40:47
Komischerweise wird die gleiche Lösung mehrmals gefunden, d.h. wohl auf verschiedenen Pfaden im Regelbaum. Mithilfe von distinct/1 kann man aber mehrdeutige Lösungen ignorieren:
Da sollte der Fix in unique helfen, den Heisenberg gepostet hat. Ich habe is_set() statt unique verwendet. Damit wird nur eine Lösung gefunden. Das comes_right_after_in(X, tjumen, [A, B, C, D]) muss ich mir auch genauer anschauen und verstehen.

Es war wirklich gut, dass das Beispiel nicht auf Anhieb funktioniert hat. Dann hätte ich es vielleicht damit abgetan, dass es wie nicht anders zu erwarten auch mit irgendeiner anderen Sprache funktioniert. So hat es den Buddler geweckt :THX: .

Benutzeravatar
paedubucher
Beiträge: 856
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: Adventskalender 21. Dezember 2023 - Logische Programmierung

Beitrag von paedubucher » 24.12.2023 09:23:22

chrbr hat geschrieben: ↑ zum Beitrag ↑
23.12.2023 22:58:10
paedubucher hat geschrieben: ↑ zum Beitrag ↑
23.12.2023 22:40:47
Komischerweise wird die gleiche Lösung mehrmals gefunden, d.h. wohl auf verschiedenen Pfaden im Regelbaum. Mithilfe von distinct/1 kann man aber mehrdeutige Lösungen ignorieren:
Da sollte der Fix in unique helfen, den Heisenberg gepostet hat. Ich habe is_set() statt unique verwendet. Damit wird nur eine Lösung gefunden. Das comes_right_after_in(X, tjumen, [A, B, C, D]) muss ich mir auch genauer anschauen und verstehen.

Es war wirklich gut, dass das Beispiel nicht auf Anhieb funktioniert hat. Dann hätte ich es vielleicht damit abgetan, dass es wie nicht anders zu erwarten auch mit irgendeiner anderen Sprache funktioniert. So hat es den Buddler geweckt :THX: .
Das is_set/1 kannte ich gar nicht (mehr?), Danke für den Hinweis!
heisenberg hat geschrieben: ↑ zum Beitrag ↑
22.12.2023 21:19:39
Inzwischen hat jemand im Prolog-Forum einen Fix für die Unique-Klausel gepostet:

Code: Alles auswählen

unique([]).
unique([_]).
unique([H|T]) :-  T\=[],
    \+ contains(T, H),
    unique(T).
Das funktioniert jetzt mit der Testfunktion auch so wie es soll. Erklärung im verlinkten Forenthread.
Ja, es lag wirklich an dieser Klausel, dass mehrere Lösungen gefunden worden sind. Aber die Lösung ist noch einfacher:

Code: Alles auswählen

unique([]).
unique([H|T]) :-
    not(contains(T, H)),
    unique(T).
Die zweite Klausel kann einfach eggelassen werden. Wenn der Tail leer ist, wird das schon im nächsten Aufruf bemerkt.
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

Antworten