views:

170

answers:

2

Using PyClips, I'm trying to build rules in Clips that dynamically retrieve data from the Python interpreter. To do this, I register an external function as outlined in the manual.

The code below is a toy example of the problem. I'm doing this because I have an application with a large corpus of data, in the form of a SQL database, which I want to reason with using Clips. However, I don't want to waste time converting all this data into Clips assertions, if I can simply "plug" Clips directly into Python's namespace.

However, when I try to create the rule, I get an error. What am I doing wrong?

import clips

#user = True

#def py_getvar(k):
#    return globals().get(k)
def py_getvar(k):
    return True if globals.get(k) else clips.Symbol('FALSE')

clips.RegisterPythonFunction(py_getvar)

print clips.Eval("(python-call py_getvar user)") # Outputs "nil"

# If globals().get('user') is not None: assert something
clips.BuildRule("user-rule", "(neq (python-call py_getvar user) nil)", "(assert (user-present))", "the user rule")
#clips.BuildRule("user-rule", "(python-call py_getvar user)", "(assert (user-present))", "the user rule")

clips.Run()
clips.PrintFacts()
+1  A: 

Your problem has something to do with the (neq (python-call py_getvar user) 'None'). Apparently clips doesn't like the nested statement. It appears that trying to wrap a function call in an equality statement does bad things. However you'll never assert the value anyway as your function returns either Nil or the value. Instead what you'll want to do is this:

def py_getvar(k):
    return clips.Symbol('TRUE') if globals.get(k) else clips.Symbol('FALSE')

then just change "(neq (python-call py_getvar user) 'None')" to "(python-call py_getvar user)"

And that should work. Haven't used pyclips before messing with it just now, but that should do what you want.

HTH!

>>> import clips
>>> def py_getvar(k):
...     return clips.Symbol('TRUE') if globals.get(k) else clips.Symbol('FALSE')

...
>>> clips.RegisterPythonFunction(py_getvar)
>>> clips.BuildRule("user-rule", "(python-call py_getvar user)", "(assert (user-
present))", "the user rule")
<Rule 'user-rule': defrule object at 0x00A691D0>
>>> clips.Run()
0
>>> clips.PrintFacts()
>>>
Wayne Werner
I'm sorry, but your solution does not work. I get the same error. Note, the goal is to use the result from the Python function in arbitrary Clips expressions, so removing the (neq ...) would not have been acceptable.
Chris S
`(neq...)` returns either the symbol TRUE or FALSE - though the result of negating the comparison of Nil (as returned by get_var) to the string "None" will always be not false, meaning or TRUE, regardless of what globals contains. However, if you want the symbol TRUE or FALSE based on the input to a function, just return the value from that function.
Wayne Werner
Did you even run the code? Your solution returns an error.
Chris S
Not on my installation... see edit
Wayne Werner
+1  A: 

I received some help on the PyClips support group. The solution is to ensure your Python function returns a clips.Symbol object and use (test ...) to evaluate functions in the LHS of rules. The use of Reset() also appears to be necessary to activate certain rules.

import clips
clips.Reset()

user = True

def py_getvar(k):
    return (clips.Symbol('TRUE') if globals().get(k) else clips.Symbol('FALSE'))

clips.RegisterPythonFunction(py_getvar)

# if globals().get('user') is not None: assert something
clips.BuildRule("user-rule", "(test (eq (python-call py_getvar user) TRUE))",
                '(assert (user-present))',
                "the user rule")

clips.Run()
clips.PrintFacts()
Chris S