So, I was wondering if anyone had implemented the amb operator in
Clojure, so I decided to give it a try. This is my first Clojure
program bigger then 5 lines, the code still needs ALOT of work, it is
ugly as hell atm, but I think it kind of works. (Yes, I know, it's
very easy to bug it, its just a sketch). Notice that unlike most
continuation based amb operators, the amount of backtracking this
implementation has to do depends on the order of the requires, not the
amb assignments.
If anyone wants to clear or improve the code, go ahead.
I also would like to know how to be able to write @baker instead of
the current (myderef baker), I tried using a proxy and implementing
IDeref, but then I couldn't acess the amb-struct private data. Since
Clojure doesn't have continuations I had to use a try catch block to
do the stack unwinding, and some redundant calculations might be done
because of it.

Here is the code:

(defstruct amb-struct :instance :orig :possible)

(def amb-stack nil)
(defn amb [choices]
  (struct-map amb-struct :instance false :orig choices  :possible
choices))

(defn myderef [a]
  (if (:instance @a)
    (:value @a)
    (do
      (set! amb-stack (cons a amb-stack))
      (let
          [oldpos (:possible @a)]
        (var-set a (assoc @a
                         :value (first oldpos)
                         :instance true
                         :possible (rest oldpos))))
      (:value @a))))

(defn amb-require [condition]
  (if condition
    nil
    (do
      (throw (.Exception "nothing")))))

(defn bindnext []
  (if (empty? amb-stack)
    'nomatchfound
    (let [lastbind (first amb-stack)
          oldpos (:possible @lastbind)]
      (if (empty? oldpos)
        (do
          (var-set lastbind (assoc @lastbind :instance false :possible (:orig
@lastbind)))
          (set! amb-stack (rest amb-stack))
          (recur))
        (do
          (var-set lastbind (assoc @lastbind :value (first oldpos) :possible
(rest oldpos)))
          'recur)))))


(defmacro amb-let [declarations & body]
  `(with-local-vars
        ~declarations
      (binding [amb-stack nil]
        (loop []
          (let [retvalue#
                (try
                 (do ~...@body)
                 (catch Exception ~'except
                   (bindnext)))]
            (cond
              (= retvalue# 'recur) (recur)
              true retvalue#))))))

(defn distinct-seq? [s]
  (cond
    (empty? (rest s)) true
    (= (first s) (second s)) false
    true (and (distinct-seq? (cons (first s) (rest (rest s))))
              (distinct-seq? (rest s)))))

(amb-let [baker (amb [1 2 3 4 5])
          cooper (amb [1 2 3 4 5])
          fletcher (amb [1 2 3 4 5])
          miller (amb [1 2 3 4 5])
          smith (amb [1 2 3 4 5])]
  (amb-require (distinct-seq? (map myderef (list baker cooper fletcher
miller smith))))
  (amb-require (not (= (myderef baker) 5)))
  (amb-require (not (= (myderef cooper) 1)))
  (amb-require (not (= (myderef fletcher) 1)))
  (amb-require (not (= (myderef fletcher) 5)))
  (amb-require (> (myderef miller) (myderef cooper)))
  (amb-require (not (= 1 (. java.lang.Math abs (- (myderef smith)
(myderef fletcher))))))
  (amb-require (not (= 1 (. java.lang.Math abs (- (myderef fletcher)
(myderef cooper))))))
  [['baker (myderef baker)]
   ['cooper (myderef cooper)]
   ['fletcher (myderef fletcher)]
   ['miller (myderef miller)]
   ['smith (myderef smith)]])

Running it returns [[baker 3] [cooper 2] [fletcher 4] [miller 5]
[smith 1]]

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To post to this group, send email to clojure@googlegroups.com
To unsubscribe from this group, send email to 
clojure+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to