2012-01-02 14 views
16

Acabo de empezar a jugar con core.logic, y para trabajar en ello, intento implementar algo simple que sea similar a un problema en el que actualmente estoy trabajando profesionalmente. Sin embargo, una parte del problema me ha dejado perplejo ...Cómo simular una 'unión externa' en core.logic?

Como una simplificación de mi ejemplo, si tengo un catálogo de artículos, y algunos de ellos solo están disponibles en ciertos países, y algunos no están disponibles en determinados países. Me gustaría ser capaz de especificar la lista de artículos, así como las excepciones, algo así como:

(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]]) 

Si es posible, prefiero no especificar permitido en todos los países, como el conjunto de elementos, con restricciones es relativamente pequeño, y me gustaría poder hacer un único cambio para permitir/excluir un artículo para un país determinado.

¿Cómo puedo escribir una regla que da la lista de los artículos/colores para un país, con las siguientes limitaciones:

  • El artículo debe estar en la lista de elementos
  • El país/El artículo debe que no esté en la lista 'en no permitido'
  • O bien:
    • no hay ningún país en el restringida a la lista de ese elemento
    • el par de países/elemento está en la restricted- a la lista

¿Hay alguna manera de hacer esto? ¿Estoy pensando en cosas completamente equivocadas?

Respuesta

14

Normalmente, cuando comienza a negar objetivos en la programación lógica, necesita alcanzar operaciones no relacionales (corte en Prolog, conda en core.logic).

Esta solución solo debe invocarse con argumentos de tierra.

(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]) 

Full solution

+0

Por los argumentos de tierra ', supongo que te refieres un valor, en lugar de las variables de consulta? Disculpas, mi último roce con la programación lógica fue un curso de prólogo de pregrado hace casi 25 años ... –

+1

No puede ser una variable desatada o unground. Un valor es molido si no contiene variables lógicas independientes (por ejemplo, [1 2 0._] no está en la tierra). Esto se vuelve relevante si la función es un objetivo y está pasando variables de consulta como argumentos. En este sentido, 'items-colors-for-country' requiere que su primer argumento sea molido. El objetivo es más flexible y composable que mi respuesta inicial. Por ejemplo, podemos consultar los colores de los monederos disponibles en EE. UU. https://gist.github.com/1557417 – Ambrose

+0

¡Gracias por la explicación! –

2

Conda puede complexifies el código, usando CFAN, puede reordenar los objetivos más fácilmente si lo desea. ¡Esto todavía no es relacional! :)

(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]) 

Para más ejemplos: https://gist.github.com/ahoy-jon/cd0f025276234de464d5

Cuestiones relacionadas