Как имитировать "внешнее соединение" в core.logic?
Я только начал играть с core.logic, и для работы над этим я пытаюсь реализовать что-то простое, что похоже на проблему, с которой я сейчас работаю профессионально. Тем не менее, одна часть проблемы меня озадачила...
В качестве упрощения моего примера, если у меня есть каталог предметов, а некоторые из них доступны только в некоторых странах, а некоторые из них недоступны в определенных странах. Я хотел бы иметь возможность указать список элементов и исключения, что-то вроде:
(defrel items Name Color)
(defrel restricted-to Country Name)
(defrel not-allowed-in Country Name)
(facts items [['Purse 'Blue]
['Car 'Red]
['Banana 'Yellow]])
(facts restricted-to [['US 'Car]])
(facts not-allowed-in [['UK 'Banana]
['France 'Purse]])
Если возможно, я бы предпочел не указывать допустимый вход для всех стран, так как набор элементов с ограничениями относительно невелик, и я хотел бы иметь возможность сделать одно изменение для разрешения/исключения для элемента для данной страны.
Как я могу написать правило, которое дает список элементов/цветов для страны со следующими ограничениями:
- Элемент должен находиться в списке элементов
- Страна/элемент не должны находиться в списке "не разрешенных"
- Либо:
- В ограниченном списке для этого элемента нет страны
- Пара стран/товаров находится в ограниченном списке
Есть ли способ сделать это? Я думаю о вещах совершенно не так?
Ответы
Ответ 1
Обычно, когда вы начинаете отрицать цели в логическом программировании, вам нужно достичь нереляционных операций (вырезать в Prolog, conda в core.logic).
Это решение следует вызывать только с аргументами с землей.
(defn get-items-colors-for-country [country]
(run* [q]
(fresh [item-name item-color not-country]
(== q [item-name item-color])
(items item-name item-color)
(!= country not-country)
(conda
[(restricted-to country item-name)
(conda
[(not-allowed-in country item-name)
fail]
[succeed])]
[(restricted-to not-country item-name)
fail]
;; No entry in restricted-to for item-name
[(not-allowed-in country item-name)
fail]
[succeed]))))
(get-items-colors-for-country 'US)
;=> ([Purse Blue] [Banana Yellow] [Car Red])
(get-items-colors-for-country 'UK)
;=> ([Purse Blue])
(get-items-colors-for-country 'France)
;=> ([Banana Yellow])
(get-items-colors-for-country 'Australia)
;=> ([Purse Blue] [Banana Yellow])
Полное решение
Ответ 2
Conda может комплексифицировать код, используя nafc, вы можете более легко переупорядочить цели, если хотите.
Это все еще нерелятивно!:)
(ns somenamespace
(:refer-clojure :exclude [==])
(:use [clojure.core.logic][clojure.core.logic.pldb]))
(db-rel items Name Color)
(db-rel restricted-to Country Name)
(db-rel not-allowed-in Country Name)
(def stackoverflow-db
(db [items 'Purse 'Blue]
[items 'Car 'Red]
[items 'Banana 'Yellow]
[restricted-to 'US 'Car]
[not-allowed-in 'UK 'Banana]
[not-allowed-in 'France 'Purse]))
(defn get-items-colors-for-country [country]
(with-db stackoverflow-db
(run* [it co]
(items it co)
(nafc not-allowed-in country it)
(conde
[(restricted-to country it)]
[(nafc #(fresh [not-c] (restricted-to not-c %)) it)]))))
(get-items-colors-for-country 'US)
;=> ([Purse Blue] [Banana Yellow] [Car Red])
(get-items-colors-for-country 'UK)
;=> ([Purse Blue])
(get-items-colors-for-country 'France)
;=> ([Banana Yellow])
(get-items-colors-for-country 'Australia)
;=> ([Purse Blue] [Banana Yellow])
Дополнительные примеры: https://gist.github.com/ahoy-jon/cd0f025276234de464d5