Как смоделировать «внешнее соединение» в core.logic? - PullRequest
16 голосов
/ 03 января 2012

Я только начал играть с 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]])

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

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

  • Элемент должен быть в списке элементов
  • Страна / элемент не должны быть в недопустимом'list
  • Либо:
    • Нет страны в списке запрещенных для этого элемента
    • Пара стран / предметов находится в списке запрещенных

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

Ответы [ 2 ]

14 голосов
/ 04 января 2012

Обычно, когда вы начинаете отрицание целей в логическом программировании, вам нужно достичь нереляционных операций (вырезать в 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 голосов
/ 20 января 2014

Конда может усложнить код, используя 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

...