Как имитировать "внешнее соединение" в core.logic? - программирование
Подтвердить что ты не робот

Как имитировать "внешнее соединение" в 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]])

Если возможно, я бы предпочел не указывать допустимый вход для всех стран, так как набор элементов с ограничениями относительно невелик, и я хотел бы иметь возможность сделать одно изменение для разрешения/исключения для элемента для данной страны.

Как я могу написать правило, которое дает список элементов/цветов для страны со следующими ограничениями:

  • Элемент должен находиться в списке элементов
  • Страна/элемент не должны находиться в списке "не разрешенных"
  • Либо:
    • В ограниченном списке для этого элемента нет страны
    • Пара стран/товаров находится в ограниченном списке

Есть ли способ сделать это? Я думаю о вещах совершенно не так?

4b9b3361

Ответ 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