1 / 14

Introduction to Core.Logic Programming in Clojure

Introduction to Core.Logic Programming in Clojure. An engine for programming Relations and Constraints. Many combinatorial problems can be solved experimentally using combination of functional and logic programming to encode relations and constraints between entities.

xander
Download Presentation

Introduction to Core.Logic Programming in Clojure

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Introduction to Core.LogicProgramming in Clojure An engine for programming Relationsand Constraints

  2. Many combinatorial problems can be solved experimentally using combination of functional and logic programming to encode relations and constraints between entities. • core.logic is a Clojure implementation of miniKanrena simple logic programming library

  3. Environment Get core.logic by including it in your Leiningenproject.clj: :dependencies [... [org.clojure/core.logic "0.8.7"] ...] Then lein deps and you're set. The examplesbelowassumehaving a namespacesetuplike: (nslogic-examples.ancenstors (:refer-clojure :exclude[==]) (:use[clojure.core.logic]))

  4. Introducing query-variables A core.logic program is written: (run* [query-variable] &logic-expressions) (run* [q] (membero q [1 2 3]) (membero q [2 3 4])) • (2 3) Fresh is like let in clojure: (fresh [a b c] &logic-expressions)

  5. Unification The most fundamental operator is ==, or unify: (== lvar1 lvar2) Unify serves to constrain each lvar to have the same set of possible values. It is a goal that succeeds if lvar1 can be made equal to lvar2, otherwise it fails. In the simplest case you can use Clojure literals for unification with lvars. For example the expression: (run* [q] (== q 1)) This expression (== q 1) succeeds if q can be made to have the value of 1. As run* introduces q in the fresh state, this is possible. Hence the unify goal succeeds, hence run* returns the list of successful values of q, that is (1).

  6. Unificaiton of two lvars Here we introduce an lvar a with fresh and constrain it to being either 1 or 2 or 3. Then we constrain q to being either 3 or 4 or 5. Finally we unify a and q leaving both with the value of their intersection: (3). (run* [q] (fresh [a] (membero a [1 2 3]) (membero q [3 4 5]) (== a q))) => (3)

  7. Conde Unsurprisingly conde behaves a lot like cond, and is used for the logical disjunction (or) on constraint goals. (run* [q] (conde [(== q 1)] [(== q 2)])) =>(1 2) The difference here is that each unification is in a different clause of conde, and each can succeeds independently, producing a value for q.

  8. Conso, Resto, Membero Conso is very similar to cons, but with the 'resulting' seq passed as an argument: (conso x r s) This is a function that succeeds only if s is a list with head x and rest r. We can use either lvars or literals for any of x r s. So we can asked run* to find list q with head 1 and rest (2 3): (run* [q] (conso 1 [2 3] q)) => ((1 2 3)) Or find a list that is a rest of (1 2 3): (run* [q] (conso 1 q [1 2 3])) => ((2 3))

  9. resto is complement of conso (run* [q] (resto [1 2 3 4] q)) =>((2 3 4)) (membero x l) membero is a function that constrains whatever logic variables are present such that x is an element of l. (run* [q] (membero q [1 2 3])) => (1 2 3)

  10. Using Facts and Relations (defrel father Father Child) (defrel mother Mother Child) (factsfather [['Vito 'Michael] ['Vito 'Sonny] ['Vito 'Fredo] ['Michael 'Anthony] ['Michael 'Mary] ['Sonny 'Vicent] ['Sonny 'Francesca] ['Sonny 'Kathryn] ['Sonny 'Frank] ['Sonny 'Santino]]) (factsmother [['Carmela 'Michael] ['Carmela 'Sonny] ['Carmela 'Fredo] ['Kay 'Mary] ['Kay 'Anthony] ['Sandra 'Francesca] ['Sandra 'Kathryn] ['Sandra 'Frank] ['Sandra 'Santino]]) From this basic setup we can ask some basic questions: (run* [q] (father 'Vito q)) = > (Sonny Michael Fredo) (run* [q] (father q 'Michael)) = > (Vito)

  11. Using conde Lets define as a parent function: (defneparent [p child] (conde ((father p child)) ((mother p child)))) (run* [q] (parent q 'Michael)) = > (Vito Carmela)

  12. (defnegrandparent [gparent child] (fresh [p] (parent gparent p) (parent p child))) (run* [q] (grandparent q 'Anthony)) = > (Vito Carmela) (run* [q] (grandparent 'Vito q)) = > (Francesca Frank Vicent Mary Kathryn Santino Anthony)

  13. A query to find intersecting relations Find grandparent relation related to one parent Michael (run* [q]   (fresh [gparent p child]     (== p 'Michael)     (parent p child)     (grandparent gparent child)     (== q {:grandparent gparent :parent p :child child}))) = > ({:grandparent Vito, :parent Michael, :child Mary}   {:grandparent Vito, :parent Michael, :child Anthony} {:grandparent Carmela, :parent Michael, :child Mary} {:grandparent Carmela, :parent Michael, :child Anthony}) Note that no child or grandparent was specified. We only specified the parent Michael. Note usage of the map as the value of q. Since there are several answers and several pieces of information for each answer, the map provides clarity.

  14. The Reasoned Schemer • Exploring further

More Related