Stephen Cagle
Stephen Cagle

Reputation: 14524

How do I de-structure a map in core.logic?

I believe I am having trouble destructuring a map in core.logic. I have the following code:

... used clojure.core.logic 
... required clojure.core.logic.arithmetic as logic.arithmetic. 

(def hand ({:rank 9, :suit :hearts} 
           {:rank 13, :suit :clubs} 
           {:rank 6, :suit :spades} 
           {:rank 8, :suit :hearts} 
           {:rank 12, :suit :clubs}))

(run* [q]
  (fresh [v w x y z]  ;;cards
    (== q [v w x y z])
    (membero v hand)
    (membero w hand)
    (membero x hand)
    (membero y hand)
    (membero z hand)
    (fresh [a b c d e]  ;;ranks
      (== {:rank a} v)
      (== {:rank b} w)
      (== {:rank c} x)
      (== {:rank d} y)
      (== {:rank e} z)
      (logic.arithmetic/>= a b)
      (logic.arithmetic/>= b c)
      (logic.arithmetic/>= c d)
      (logic.arithmetic/>= d e))
    (distincto q)))

It returns the empty list (), indicating that it found no matches. I believe it is a problem in the (== {:rank a} v) portion of the code. I am attempting to simply return q, where q is a vector of the maps in :rank descending order.

Upvotes: 3

Views: 825

Answers (3)

dnolen
dnolen

Reputation: 18556

A much more concise solution can now be written using the latest core.logic release 0.8.3:

(ns cards
  (:refer-clojure :exclude [==])
  (:use [clojure.core.logic])
  (:require [clojure.core.logic.fd :as fd]))

(def hand
  [{:rank 9, :suit :hearts} 
   {:rank 13, :suit :clubs} 
   {:rank 6, :suit :spades} 
   {:rank 8, :suit :hearts} 
   {:rank 12, :suit :clubs}])

(defn ranko [card rank]
  (featurec card {:rank rank}))

(run* [v w x y z :as q]
  (permuteo hand q)
  (fresh [a b c d e]
    (ranko v a) (ranko w b) (ranko x c)
    (fd/>= a b) (fd/>= b c)
    (ranko y d) (ranko z e)
    (fd/>= c d) (fd/>= d e)))

Upvotes: 7

Norman Richards
Norman Richards

Reputation: 1626

If you don't need to refer to the logic variable, you don't actually need to name it:

(== {:rank a :suit (lvar)} v)

I find myself using (lvar) more and more. It usually makes the code clearer than a throwaway unused variable, but I do wish there were a better way to express this.

Upvotes: 0

Stephen Cagle
Stephen Cagle

Reputation: 14524

Evidently you have to do exact matches on maps. This means that you could create garbage variables to capture the values of anything you are not interested in. Seems weird, but ok.

(run* [q]
  (fresh [v w x y z]  ;;cards
    (== q [v w x y z])
    (membero v hand)
    (membero w hand)
    (membero x hand)
    (membero y hand)
    (membero z hand)
    (fresh [a b c d e, f g h i j]  ;;ranks, garbage
      (== {:rank a :suit f } v)
      (== {:rank b :suit g } w)
      (== {:rank c :suit h } x)
      (== {:rank d :suit i } y)
      (== {:rank e :suit j } z)
      (logic.arithmetic/>= a b)
      (logic.arithmetic/>= b c)
      (logic.arithmetic/>= c d)
      (logic.arithmetic/>= d e))
      (distincto q)))

Finally, here is a slightly more succinct, faster and more garbled version of the above.

(run* [q]
  (fresh [v w x y z]  ;;cards
    (permuteo hand q)
    (== q [v w x y z])
    (fresh [a b c d e, f g h i j]  ;;ranks, garbage
      (== {:rank a :suit f } v)
      (== {:rank b :suit g } w)
      (logic.arithmetic/>= a b)
      (== {:rank c :suit h } x)
      (logic.arithmetic/>= b c)
      (== {:rank d :suit i } y)
      (logic.arithmetic/>= c d)
      (== {:rank e :suit j } z)
      (logic.arithmetic/>= d e))))

Upvotes: 0

Related Questions