How can I store a function, like < or >= as a value for evaluating it later, in CLIPS? - rule-engine

I'm new into CLIPS, and I'm forced to work strictly in heuristic (non procedural) paradigm. I'm trying to store a comparison function as a value in a similar way as I do in Python or in LISP, so I can modify the effect of a rule, as follows:
(assert (comp >=))
(defrule assert-greatest "Prints the unsorted item with the greatest rating."
(comp ?c)
?u <- (unsorted ?name)
?r <- (cinema (name ?name) (rate ?rate))
(forall (and (unsorted ?n)(cinema (name ?n) (rate ?r)))
(test ((eval ?c) ?r ?rate))
)
=>
(retract ?u)
(modify ?rec (name ?name) (rating ?rating))
)
I'm not sure to understand the meaning of 'eval' in CLIPS, and probably I'm misusing it, but there might be something like (eval f) in LISP.

Eval expects a string:
CLIPS (6.31 6/12/19)
CLIPS> (eval "3")
3
CLIPS> (eval "(>= 3 4)")
FALSE
CLIPS> (eval "(>= 4 3)")
TRUE
CLIPS>
In your code, you would need to use the str-cat function to build a string to pass to eval:
(eval (str-cat "(" ?v " " ?r " " ?rate ")"))
The funcall function is better for what you're trying to do:
CLIPS> (funcall >= 4 3)
TRUE
CLIPS> (funcall >= 3 4)
FALSE
CLIPS>
In your code replace the eval call with this:
(funcall ?v ?r ?rate)

Related

Simplify symbolic expressions

I am new in Lisp and i need some help.
I need to simplify next expressions:
from (+ (+ A B) C) to (+ A B C)
and from (- (- A B) C) to (- A B C).
If you could help me with one of them I'll understand how i need to do this to the next one.
Thanks a lot.
Assuming you have an input that matches this pattern, (+ e1 ... en), you want to recursively simplify all e1 to en, which gives you s1, ..., sn, and then extract all the si that start with a + to move their arguments one level up, to the simplified expression you are building.
An expression e matches the above pattern if (and (consp e) (eq '+ (car e))).
Then, all the ei are just given by the list that is (cdr e).
Consider the (+) case, how could you simplify it?
To apply a function f to a list of values, call (mapcar #'f list).
To split a list into two lists, based on a predicate p, you might use a loop:
(let ((sat nil) (unsat nil))
(dolist (x list (values sat unsat))
(if (funcall predicate x)
(push x sat)
(push x unsat))))
There is a purely functional way to write this, can you figure it out?
Here is a trivial simplifier written in Racket, with an implementation of a rather mindless simplifier for +. Note that this is not intended as anything serious: it's just what I typed in when I was thinking about this question.
This uses Racket's pattern matching, probably in a naïve way, to do some of the work.
(define/match (simplify expression)
;; simplifier driver
(((cons op args))
;; An operator with some arguments
;; Note that this assumes that the arguments to operators are always
;; expressions to simplify, so the recursive level can be here
(simplify-op op (map simplify args)))
((expr)
;; anything else
expr))
(define op-table (make-hash))
(define-syntax-rule (define-op-simplifier (op args) form ...)
;; Define a simplifier for op with arguments args
(hash-set! op-table 'op (λ (args) form ...)))
(define (simplify-op op args)
;; Note the slightly arcane fallback: you need to wrap it in a thunk
;; so hash-ref does not try to call it.
((hash-ref op-table op (thunk (λ (args) (cons op args)))) args))
(define-op-simplifier (+ exprs)
;; Simplify (+ ...) by flattening + in its arguments
(let loop ([ftail exprs]
[results '()])
(if (null? ftail)
`(+ ,#(reverse results))
(loop (rest ftail)
(match (first ftail)
[(cons '+ addends)
(append (reverse addends) results)]
[expr (cons expr results)])))))
It is possible to be more aggressive than this. For instance we can coalesce runs of literal numbers, so we can simplify (+ 1 2 3 a 4) to
(+ 6 a 4) (note it is not safe in general to further simplify this to (+ 10 a) unless all arithmetic is exact). Here is a function which does this coalescing for for + and *:
(define (coalesce-literal-numbers f elts)
;; coalesce runs of literal numbers for an operator f.
;; This relies on the fact that (f) returns a good identity for f
;; (so in particular it returns an exact number). Thisis true for Racket
;; and CL and I think any Lisp worth its salt.
;;
;; Note that it's important here that (eqv? 1 1.0) is false.
;;;
(define id (f))
(let loop ([tail elts]
[accum id]
[results '()])
(cond [(null? tail)
(if (not (eqv? accum id))
(reverse (cons accum results))
(reverse results))]
[(number? (first tail))
(loop (rest tail)
(f accum (first tail))
results)]
[(eqv? accum id)
(loop (rest tail)
accum
(cons (first tail) results))]
[else
(loop (rest tail)
id
(list* (first tail) accum results))])))
And here is a modified simplifier for + which uses this. As well as coalescing it notices that (+ x) can be simplified to x.
(define-op-simplifier (+ exprs)
;; Simplify (+ ...) by flattening + in its arguments
(let loop ([ftail exprs]
[results '()])
(if (null? ftail)
(let ([coalesced (coalesce-literal-numbers + (reverse results))])
(match coalesced
[(list something)
something]
[exprs
`(+ ,#exprs)]))
(loop (rest ftail)
(match (first ftail)
[(cons '+ addends)
(append (reverse addends) results)]
[expr (cons expr results)])))))
Here is an example of using this enhanced simplifier:
> (simplify 'a)
'a
> (simplify 1)
1
> (simplify '(+ 1 a))
'(+ 1 a)
> (simplify '(+ a (+ b c)))
'(+ a b c)
> (simplify '(+ 1 (+ 3 c) 4))
'(+ 4 c 4)
> (simplify '(+ 1 2 3))
6
For yet more value you can notice that the simplifier for * is really the same, and change things to this:
(define (simplify-arith-op op fn exprs)
(let loop ([ftail exprs]
[results '()])
(if (null? ftail)
(let ([coalesced (coalesce-literal-numbers fn (reverse results))])
(match coalesced
[(list something)
something]
['()
(fn)]
[exprs
`(,op ,#exprs)]))
(loop (rest ftail)
(match (first ftail)
[(cons the-op addends)
#:when (eqv? the-op op)
(append (reverse addends) results)]
[expr (cons expr results)])))))
(define-op-simplifier (+ exprs)
(simplify-arith-op '+ + exprs))
(define-op-simplifier (* exprs)
(simplify-arith-op '* * exprs))
And now
(simplify '(+ a (* 1 2 (+ 4 5)) (* 3 4) 6 (* b)))
'(+ a 36 b)
Which is reasonably neat.
You can go further than this, For instance when coalescing numbers for an operator you can simply elide sequences of the identity for that operator: (* 1 1 a 1 1 b) can be simplified to (* a b), not (* 1 a 1 b). It may seem silly to do that: who would ever write such an expression, but they can quite easily occur when simplifying complicated expressions.
There is a gist of an elaborated version of this code. It may still be buggy.

error in dr Racket problem. unbound identifier in mymeber

trying to do a dr racket problem to tell if a number is apart of list. getting errors
#lang racket
(mymember (x, l))
if l=?null
then "false"
if x==car(l)
then "true"
mymember(x,l)
mymember 2' (1,3,4,5,6)
Racket uses prefix notation. This makes commas unnecessary. First, some syntax:
How to define a function?
( define ( name variable1 variable2 ) body )
Where name is the name of the function, and the variables are the parameters. Which are followed by the body expression.
Example:
; Number -> Number
; converts from fahrenheit to celsius.
(define (f2c f)
(* 5/9 (- f 32)))
How to call a function?
( name expression1 expression2 )
name is the name of the function and expression1 and expression2 are its arguments.
Example:
(sqr 3)
;; == 9
Similarly, to check if two values are equal: (equal? x y)
How to use the if expressions?
( if question-expression then-answer-expression else-answer-expression )
If the value of the question-expression is not false, the if evaluates the then-answer-expression, otherwise it evaluates the else-answer-expression.
Example:
;; Number -> Number
;; reciprocate all non-zero x, otherwise return 0.
(define (inverse-of-x x)
(if (= x 0) 0 (/ 1 x)))
... and so on. Read the Racket Guide for the essentials on syntax, semantics and datatypes in the language.
Fixing all the syntax still leads to one error: an infinite loop. That is because the recursive call doesn't call cdr on the list. So the recursive call is made on the same list (not a shorter list) forever. Wrapping a cdr and fixing the syntax leads to a correct function.
#lang racket
; [X] [List-of X] -> "true" U "false"
; is x an element of l?
(define (mymember x l)
(if (equal? l null)
"false"
(if (equal? x (car l))
"true"
(mymember x (cdr l)))))
(mymember 2 '()) ; = false
(mymember 2 '(1 3 4 5 6)) ; = false
(mymember 2 '(1 3 2 5 6)) ; = true

eval macro Unbound variable (CHICKEN Scheme)

I'm trying to eval a list with variables inside a macro function that defines a variable in a lambda but the eval in it can't
(define-syntax MYVAR
(syntax-rules ()
[(_ varname value body ...) ((lambda (varname) body ...) value)]))
(define mylist '(list P Q))
(print mylist)
(MYVAR P 1
(MYVAR Q 2
(print P Q) ;;everything prints fine in here
(print (eval mylist))))
<eval> ((lambda2127 (P) (MYVAR Q 2 (print P Q) (print (eval mylist)))) 1)
<eval> ((lambda2129 (Q) (print P Q) (print (eval mylist))) 2)
<eval> (print P Q)
<eval> (print (eval mylist))
<eval> (eval mylist)
<syntax> (list P Q)
<eval> (list P Q) <--
=> Error: unbound variable: P
I assume eval tries to evaluate before my macro compiles but not sure,
is there a way to reuse a list and evaluate it inside a macro?
I have tried to use define-for-syntax but the same error arises
I'd recommend that you try:
(MYVAR P 1
(MYVAR Q 2
((print (eval 'mylist)))))
After this your output should be
(list P Q)
Why did this work?
In Scheme, code is nested using parentheses.
So, for example
(print (eval (list 1 2 3)))
Scheme will try and find the definition of 1 and pass 2 and 3 as arguments.
Whereas
(print (eval '(list 1 2 3)))
Will print
(list 1 2 3)
In short, the single quote is like an escape character.

Comparison returns expected value call function directory, but it is not so at process on list

I am creating a simple elisp tester.
However, I am getting the wrong behavior (which I can not understand) as seen below.
I think that testers should return t test cases (:eq 'a 'a) and (:eq (return-symbol) 'a) naturally as my tester also precedes the following code. Actually it is not so.
The following code has been lengthened beyond necessity, but for the most part it is checking the obvious behavior.
I think that my tester should also return those expected return values.
Are there any good ideas?
Even explaining the reason for this behavior would be a help to improve my tester. I would appreciate it if you give me something.
;; return-symbol is always return 'a
(defun return-symbol ()
'a)
;; => return-symbol
;; operation check for return-symbol
(return-symbol)
;; => a
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; compare 'a and 'a by eq
(eq 'a 'a)
;; => t
;; compare 'a and 'a by equal
(equal 'a 'a)
;; => t
;; compare (return-symbol) and 'a by eq
(eq (return-symbol) 'a)
;; => t
;; compare (return-symbol) and (return-symbol) by eq
(eq (return-symbol) (return-symbol))
;; => t
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; comparison by funcalled eq
(funcall 'eq 'a 'a)
;; => t
(funcall 'eq (return-symbol) 'a)
;; => t
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; funcall with interned symbol
(funcall (intern "eq") 'a 'a)
;; => t
(funcall (intern "eq") (return-symbol) 'a)
;; => t
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; define universal comparison function
(defun multi-comp (key a b)
"KEY is funcname symbol such as :FUNCNAME"
(let ((funcname (replace-regexp-in-string "^:+" "" (symbol-name key))))
(funcall (intern funcname) a b)))
;; => multi-comp
;; operation check for multi-comp
(multi-comp :eq 'a 'a)
;; => t
(multi-comp :eq (return-symbol) 'a)
;; => t
(multi-comp :equal (return-symbol) 'a)
;; => t
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Define function to apply sequentially
(defun run-test (tests)
"TESTS is list such as (([str]TESTNAME ([sym]:FUNC [sexp]A [sexp]B))
([str]TESTNAME ([sym]:FUNC [sexp]A [sexp]B))...)"
(dolist (x tests)
(let* ((testname (car x))
(values (cadr x))
(key (nth 0 values))
(a (nth 1 values))
(b (nth 2 values)))
(if (multi-comp key a b)
(princ (format "%s is passed\n" testname))
(princ (format "%s is failed\n" testname))))))
;; => run-test
;; operation check of run-test
(run-test '(("eq1" (:eq 'a 'a))
("eq2" (:eq (return-symbol) (return-symbol)))
("equal1" (:equal 'a 'a))
("equal2" (:equal (return-symbol) 'a))
("equal3" (:equal (return-symbol) (return-symbol)))))
;; =>
;; eq1 is failed ; <= ??
;; eq2 is failed ; <= ??
;; equal1 is passed
;; equal2 is failed ; <= ??
;; equal3 is passed
;; nil
Your argument to run-test is evaluated once, so "eq1" sees 'a which is (quote a) (a list of length 2) which is, of course, fails under eq.
Similarly, (return-symbol) is not evaluated and "eq1" sees the lists of length 1 which are not identical under eq.
You would have discovered that by simply adding print to multi-comp.
Your code would probably work is you replace (multi-comp key a b) with (multi-comp key (eval a) (eval b)).
Please note that the fact that you need eval is a very strong indicator that you are doing something horribly wrong.
PS. I urge you to use ERT instead of rolling your own.

Append two functions recursively with Racket?

I'm using racket language, but i'm having some trouble getting some expected results for a recursive function. My goal is to input an integer n and output the element n times, as a list.
' exclude the ' and the text, #lang racket
; take n (integer) e (scheme) build new list
; 2 `() -> () ()
; 3 `a -> a a a
; 4 `(a) -> (a) (a) (a) (a)
(define (list n e)
(if (= n 0) e
(append e(list (- n 1) e)) ))
; (list 0 '())
; prints '()
; (list 2 '())
; should print '() '()
Your problem would appear to be that append isn't doing what you're expecting it to - it unwraps and discards top-level empty lists. (e.g. (append '(1) '() '(2) '() '(3)) ;; => '(1 2 3)).
So, swapping cons in for append will result in (what I believe to be) the expected output.
(define (my-list n empty-list)
(if (= n 0)
empty-list
(cons
empty-list
(my-list (- n 1) empty-list))))
(my-list 2 '()) ;; => '(() ())
You should also reconsider clobbering Racket's built-in list function.
This answer has a useful breakdown of what append is doing internally and why it's undesirable in this scenario.