0
votes

I'm trying to write some clojure which builds up a data-structure that will be valid clojure code as well. (note I'm not building a macro here: just a function that happens to return clojure code).

Here's my working bit:

(defn create-clause [  property operator value  ]
  (list (symbol operator) (symbol property) (symbol value))
)

(create-clause "b" "<" "5")

So this creates a 'clause' like this:

(< b 5)

And this works.

What I want to do is end up with something like this:

(and (= AccountType "current") (< Balance 0))

I can chain up a couple of clauses manually like this:

(list 'and (create-clause "a" "=" "current") (create-clause "b" "<" "0"))

Which results in:

(and (= a "current") (< b 0))

But I want a function that takes my 3-string arguments "property" "operator" "value" , creates the clause and results in a combined 'and' list of all the clauses which can be evaluated (assume the symbols are bound in a 'let' of course...)

EDIT: Got a bit closer but still no cigar....

Slightly refactored the func - so it now takes a single argument of a list , rather than 3 separate arguments:

(defn create-clause [  [ property operator value ]  ]
  (list (symbol operator) (symbol property) (symbol value))
)

(create-clause [ "b" "<" "5" ] )

Now using a loop/recur - and I can nearly get what I need:

(list 'and (loop [ input [ "a" "<" "10" , "b" "<" "5" ] output [] ]
  (if (= (count input) 0) output
  (recur (drop 3 input) (conj output (create-clause(take 3 input)))))))

The result of the above is:

(and [(< a 10) (< b 5)]) ; wrong - don't want that internal vector wrapper...

EDIT #2:

So I'm thinking I have accumulate the result somewhere in my loop - so I might was well stick to using the vector as above (but dropping the (list 'and...) ) - but then I should be able to apply something to the result of it which will have the effect of turning this something like this structure:

[ (= 1 1) (= 1 1) ]

Into this structure:

(and (= 1 1) (= 1 1))

But now I'm stuck again.....

EDIT #3:

Ok , I got there - I have ended up with an unholy mess - but at least it works and I can start refactoring now !

(conj (seq (loop [ input [ "AccountType" "=" "current" , "Balance" "<" 5 ] output [] ]
  (if (= (count input) 0) output
  (recur (drop 3 input) (conj output (create-clause(take 3 input))))))) 'and)
(and (= AccountType "current") (< Balance 5))
1

1 Answers

1
votes

partition will chunk up a sequence into a certain size:

(def s (partition 3 (range 6)))
; s => ((0 1 2) (3 4 5))

A combination of map, partial and apply will let you call your method:

(defn reorder [a b c] (list b c a))
(def reordered (map (partial apply reorder) s))
; reordered => ((1 2 0) (4 5 3))

And top it all off with an and:

(conj reordered 'and)
; => (and (1 2 0) (4 5 3))