Janus query_once & query

Yesterday I was playing a little bit with Janus and I wanted to ask: query_once I think is rather clear to me how it works, but how does one get access to the data with query? Because this creates a python object and I havent’t understood how to get to the values stored inside…I used a for loop iterating over a variable and printed it (which anyways can’t be, I think, the standard way), but after having used it once, I found that if I tried to loop a second time as result I get an empty list, as if the object had been emptied or something…why did you decide to introduce the functional notation? This is even more obscure to me, but I’ll leave it aside for the moment.
Thanks

using list comprehension is the same:

>>> w = query("member(X, [a, s, d])")
>>> w
<janus_swi.janus.query object at 0x1016f2e90>
>>> [el for el in w]
[{'truth': True, 'X': 'a'}, {'truth': True, 'X': 's'}, {'truth': True, 'X': 'd'}]
>>> [el for el in w]
[]

It works pretty much the same as query_once(). It return a Python iterator. Each element of this iterator is a dict, just like the one returned by query_once(). So, to get all Y for a certain X in p(X,Y), use

    for d in janus.query("p(X,Y)", {'X':x}):
         print(d['Y'])

Or, if you want all Ys in a list

>>> [d['Y'] for d in janus.query("p(X,Y)", {'X':1})]
['a', 'b']

Why not? Note that the number of answer to a Prolog query may be huge or even infinite. Using a loop you can aggregate/filter/… them one by one and if you are satisfied (you don’t want any more), you can use break.

The returned object refers to a non-deterministic Prolog query that you can consume only once. Note that you generally do not want to assign the iterator to something as that will avoid it being finalized. The __del__() method of the iterator terminates the Prolog query. Failure to do so easily leaves Prolog in an inconsistent state. So, use it in list comprehension or as

    for d in janus.query(...):
        <process d>
        <optionally break if you do not want all answers>

It provides an interface that allows doing some simple things easier (for the Python coder) and faster. E.g. to get the Y from p(X,Y) again, we can do

>>> janus.apply_once("user", "p", 1)
"a"

Or

>>> [*janus.apply("user", "p", 1)]
["a", "b"]

It is simpler because it returns only a single value, so we do not need a dict. The price is that you need a predicate that can accept N input arguments as arguments 1…N that receive the Prolog values that result from the default Python → Prolog data conversion and the predicate must produce exactly one output argument that is the last and suitable for conversion to Python data. In general that means you’ll often have to define a Prolog predicate that massages input and output to suit the needs at the Python side. Given that though, calling it from Python is easier and the overhead is about 5 times smaller (+/- 300K calls/sec for query_once() and 1.5M calls/sec for apply_once(), doing something really trivial).

1 Like

Thanks for the explanation. It helped. I think I’m understanding the relational notation better now. The functional remains still somewhat mysterious (I fear I still don’t get exactly the inputs the outputs, what goes where, …), but it’s in any case a step forward… of course I understand it’s not practical to have answers as dicts… I’ll experiment a little bit tomorrow. Maybe things get better even in this respect

Anyways I’m not surprised that of the two, I seem to have understood “naturally” so to say the most inefficient one… :sweat_smile:

rereading what you wrote I think I understood it better: the functional notation is a subset, so to say, of the relational one, it doesn’t allow you to do everything you can do with the second one, rather it allows you to do some simple things in a more efficient way? Did I get it right? Thanks

1 Like