Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
tutorials:intermediate:json_prolog [2019/04/24 11:16] – [Accessing data from Prolog] gkazhoyatutorials:intermediate:json_prolog [2022/01/25 16:35] (current) – [Prerequisites] gkazhoya
Line 9: Line 9:
 This tutorial requires CRAM, roslisp_repl and ROS to have already been installed. Go through the previous tutorials and the installation page if you don't have CRAM installed yet. This tutorial requires CRAM, roslisp_repl and ROS to have already been installed. Go through the previous tutorials and the installation page if you don't have CRAM installed yet.
 Make sure you have **Java 8** and **Gradle 5.1** already installed. Otherwise you won't be able to build KnowRob. Other versions could work, but have not been tested yet, so use them at your own risk!  Make sure you have **Java 8** and **Gradle 5.1** already installed. Otherwise you won't be able to build KnowRob. Other versions could work, but have not been tested yet, so use them at your own risk! 
- 
  
 === Installation === === Installation ===
Line 28: Line 27:
  
 === Launch === === Launch ===
-In addition to a **roscore** and the **roslisp_repl** you need to launch **json_prolog** in a Terminal with the following command: +In addition to a **roscore** and the **roslisp_repl** you need to launch **json_prolog** in a Terminal with the following command: 
 +<code bash> 
 +    $ roslaunch rosprolog rosprolog.launch 
 +</code> 
 +or if you're using an older version of KnowRob, then the following command:
 <code bash> <code bash>
     $ roslaunch json_prolog json_prolog.launch      $ roslaunch json_prolog json_prolog.launch 
Line 66: Line 69:
 X being the variable we asked Prolog to fill with a value. Whenever Prolog returns a value for a variable, the variable name gets prefixed with a question mark. "1" is the first value in the list we passed on to the ''member'' predicate of Prolog. The ''member'' predicate is true if the element is a member of the given list, or in this case since it is a variable, it will return the first element of the list. X being the variable we asked Prolog to fill with a value. Whenever Prolog returns a value for a variable, the variable name gets prefixed with a question mark. "1" is the first value in the list we passed on to the ''member'' predicate of Prolog. The ''member'' predicate is true if the element is a member of the given list, or in this case since it is a variable, it will return the first element of the list.
 In a [[json_prolog#Some more information about Prolog in LISP |later section]] we will look in more detail into the bindings we get as a result from Prolog and explain a bit more in depth why we get these results. In a [[json_prolog#Some more information about Prolog in LISP |later section]] we will look in more detail into the bindings we get as a result from Prolog and explain a bit more in depth why we get these results.
 +
 +Why do we get ''?X'' sitting in 3 brackets? The inner bracket connects ''?X'' with it's value ''1''.
 +What does the second bracket do?
 +Let's add another variable to the query -- the variable ''?Y''.
 +<code lisp>
 +CL-USER> (json-prolog:prolog-simple-1 "member(X, [1,2,3]), Y = X.")
 +</code>
 +This binds ''?X'' to member of the ''[1,2,3]'' list and ''?Y'' gets the same value as ''?X''.
 +The result of the query is
 +<code lisp>
 +(((?X . 1) (?Y . 1)))
 +</code>
 +Therefore, the second bracket makes a list of bindings for each variable in the query:
 +<code lisp>
 +((?X . 1) (?Y . 1))
 +</code>
 +is a list of bindings for variable ''?X'' and ''?Y''.
 +
 +The last outer bracket is explained next.
  
 ==== prolog-simple ==== ==== prolog-simple ====
Line 77: Line 99:
 </code>     </code>    
          
-The result is basically still the same, but it looks a little bit more complex on the first glance. What we get is a list which contains two elements. The first one is a list in a list with two elements which we already know from the previous call with ''prolog-simple-1'' and which describe the value Prolog found for '' ?X ''. The second element of the list is  ''#S(CRAM-UTILITIES::LAZY-CONS-ELEM :GENERATOR #<CLOSURE (LAMBDA () :IN JSON-PROLOG::PROLOG-RESULT->BDGS)+The result is basically still the same, but it looks a little bit more complex on the first glance. What we get is a list which contains two elements. The first one is a list in a list with two elements which we already know from the previous call with ''prolog-simple-1'' and which describes the value Prolog found for '' ?X ''. The second element of the list is  ''#S(CRAM-UTILITIES::LAZY-CONS-ELEM :GENERATOR #<CLOSURE (LAMBDA () :IN JSON-PROLOG::PROLOG-RESULT->BDGS)
                 {1005F52FAB}>))'' which is a lazy list element generator.                 {1005F52FAB}>))'' which is a lazy list element generator.
 ==== lazy lists ==== ==== lazy lists ====
Line 95: Line 117:
 We save our list in a variable for convenience purposes. This allows us also to expand the so stored list, and we can see all the possible values Prolog found for the variable ''?X'', which are ''1,2,3''. We save our list in a variable for convenience purposes. This allows us also to expand the so stored list, and we can see all the possible values Prolog found for the variable ''?X'', which are ''1,2,3''.
  
-If we do the same with ''prolog-simple-1'' we can see the difference between ''prolog-simple-1'' and ''prolog-simple'' better:+Now we see why we need the outer bracket: the result of the query is a (lazy) list of all possible bindings for the query variables. Let us add another variable to the query again: 
 <code lisp> <code lisp>
-    CL-USER> (setq *our-list* (json-prolog:prolog-simple-1 "member(X, [1,2,3])")) +CL-USER> (cut:force-ll (json-prolog:prolog-simple "member(X, [1,2,3]), Y = X.")) 
-        (((?X . 1))) +(((?X . 1) (?Y . 1)) ((?X . 2) (?Y . 2)) ((?X . 3) (?Y . 3))) 
-    CL-USER> (cut:force-ll *our-list*+</code> 
-        (((?X . 1)))+We get a list of all possible bindings of the query variables, where the bindings is itself a list with one element for each variable. 
 + 
 +If we evaluate our original ''member'' query with ''prolog-simple-1'' we can see the difference between ''prolog-simple-1'' and ''prolog-simple'' better: 
 +<code lisp> 
 +CL-USER> (cut:force-ll (json-prolog:prolog-simple "member(X, [1,2,3])")) 
 +(((?X . 1)) ((?X . 2)) ((?X . 3))) 
 +CL-USER> (cut:force-ll (json-prolog:prolog-simple-1 "member(X, [1,2,3])")
 +(((?X . 1)))
 </code> </code>
  
Line 112: Line 142:
 Now that we have a lazy list with interesting information, how can we access the elements contained within? Now that we have a lazy list with interesting information, how can we access the elements contained within?
          
-We can use ''(cut:lazy-car *our-list*)'' to access the elements of the list. It is used the same way the normal ''car'' is. Same goes for ''cdr''. So let's get the first element of the lazy list:+We can use ''(cut:lazy-car *our-list*)'' to access the elements of the list. It is used the same way the normal ''car'' is:
 <code lisp> <code lisp>
-    CL-USER> (setq *our-list* +CL-USER> (cut:lazy-car (json-prolog:prolog-simple "member(X, [1,2,3])")
-                   (cut:lazy-car +((?X . 1)) 
-                    (json-prolog:prolog-simple "member(X, [1,2,3])"))) +</code> 
-</code>     +Same goes for ''cdr'':
- +
-This gives us a (normal) list of bindings for the variables in the query. We only have one variable ''?X'', so we get ''((?X . 1))''. If we had more than one variable:+
 <code lisp> <code lisp>
-    CL-USER> (cut:lazy-car (json-prolog:prolog-simple "member(X, [1,2,3]), Y = X."))+CL-USER> (cut:lazy-cdr (json-prolog:prolog-simple "member(X, [1,2,3])")) 
 +(((?X . 2)) 
 + . #S(CRAM-UTILITIES::LAZY-CONS-ELEM 
 +      :GENERATOR #<CLOSURE (LAMBDA () :IN JSON-PROLOG::PROLOG-RESULT->BDGS) 
 +                   {10084ADDCB}>))
 </code> </code>
-we would get '((?X . 1) (?Y . 1))'. 
  
-Now we can use ''(cut:var-value VAR LIST)'' to access the value of a specific item. In our case we asked for possible values of the variable X. Therefore:+When we have one element of the lazy list, we can use ''(cut:var-value VAR LIST)'' to access the value of a specific variable. In our case we asked for possible values of the variable X. Therefore:
 <code lisp> <code lisp>
-    CL-USER> (cut:var-value '?*our-list*+CL-USER> (cut:var-value '?(cut:lazy-car (json-prolog:prolog-simple "member(X, [1,2,3])"))
-         1+1
 </code>      </code>     
-Gives us the result ''1'', since this is the value of ''X''.+Gives us the result ''1'', since this is the value of ''X'' in the first element of the lazy list.
 That way, one can extract the necessary information, adjust it if needed, and pass it on to the plans which need it.  That way, one can extract the necessary information, adjust it if needed, and pass it on to the plans which need it. 
  
Line 144: Line 175:
  
 <code lisp> <code lisp>
- 
     CL-USER> (json-prolog:prolog-simple-1 "register_ros_package(knowrob_common)")     CL-USER> (json-prolog:prolog-simple-1 "register_ros_package(knowrob_common)")
-       (NIL +       (NIL)
-        . #S(CRAM-UTILITIES::LAZY-CONS-ELEM +
-             :GENERATOR #<CLOSURE (LAMBDA (:IN JSON-PROLOG::PROLOG-RESULT->BDGS) +
-                          {1006444B3B}>)) +
-    +
 </code> </code>
 +
 +In JSON Prolog ''(NIL)'' means success, and ''NIL'' means failure. This is explained in detail [[tutorials/intermediate/json_prolog?&#nil_vs_nil|in the next section]].
          
-This allows us to use the **knowrob_common** package, which allows us to access many utility functions and also the most commonly used queries of KnowRob.+This allows us to use the ''knowrob_common'' package, which allows us to access many utility functions and also the most commonly used queries of KnowRob.
  
 With this imported package we could now do the following: With this imported package we could now do the following:
  
 <code lisp> <code lisp>
- +    CL-USER (json-prolog:prolog-simple "lowercase('HELLOWORLD',RESULT)") 
-    CL-USER (json-prolog:prolog-simple "lowercase('HELLOWORLD',Result)") +       (((?RESULT . |''helloworld''|))
-       (((|?Result| . |''helloworld''|))+
         . #S(CRAM-UTILITIES::LAZY-CONS-ELEM         . #S(CRAM-UTILITIES::LAZY-CONS-ELEM
               :GENERATOR #<CLOSURE (LAMBDA () :IN JSON-PROLOG::PROLOG-RESULT->BDGS)               :GENERATOR #<CLOSURE (LAMBDA () :IN JSON-PROLOG::PROLOG-RESULT->BDGS)
Line 166: Line 193:
 </code> </code>
  
-This query converts the first elements upper case letters into lower case, and stores the obtained result in the ''Result'' variable.+This query converts the first elements upper case letters into lower case, and stores the obtained result in the ''RESULT'' variable.
  
-An overview of the currently available packages and their queries can be found [[http://www.knowrob.org/api | here]]. If when loading a package Emacs only returns ''NIL'' then it might be that the package does not exist or is not found on your system. In such cases, you can check the terminal in which json-prolog was launched. If it cannot be found, you will see the following error: +An overview of the currently available packages and their queries can be found [[http://www.knowrob.org/api|here]]. If when loading a package Emacs only returns ''NIL'' then it might be that the package does not exist or is not found on your system. In such cases, you can check the terminal in which json-prolog was launched. If it cannot be found, you will see the following error: 
 <code bash> <code bash>
    [rospack] Error: package 'knowrob_maps_tools' not found    [rospack] Error: package 'knowrob_maps_tools' not found
Line 177: Line 204:
          
 <code lisp> <code lisp>
- 
     Prolog query failed: PrologException: error(existence_error(procedure, '/'(u_load_episodes, 1)), context(':'(system, '/'(call, 1)), _1))     Prolog query failed: PrologException: error(existence_error(procedure, '/'(u_load_episodes, 1)), context(':'(system, '/'(call, 1)), _1))
     [Condition of type SIMPLE-ERROR]     [Condition of type SIMPLE-ERROR]
-     
 </code> </code>
          
-and if you check your terminal, in which the **json_prolog** node is running, there will be a huge java print as well, basically explaining the same thing.+and if you check your terminal, in which the ''json_prolog'' node is running, there will be a huge java print as well, basically explaining the same thing.
      
-     + 
-==== Some more information about Prolog in LISP ====+==== Case-sensitive variable names ==== 
 + 
 +In the former query we got a weird result from Prolog which looked like this:  
 +<code lisp> 
 +|''helloworld''
 +</code> 
 +What do the pipes stand for in this case? 
 + 
 +Lisp is case-insensitive, that means that variable of function names, or more correctly said, any symbol, can be written in any capitalization and will refer to the same symbol: 
 + 
 +<code lisp> 
 +CL-USER> (eq 'hello 'HELLO) 
 +
 +CL-USER> (eq 'hello 'Hello) 
 +
 +</code> 
 + 
 +Prolog, on the other hand, is case-sensitive. 
 +And in some cases, one would like to have a case-sensitive variable in Lisp as well. 
 +This is where the pipes come into play: 
 +<code lisp> 
 +CL-USER> (intern "HELLO"
 +HELLO 
 +:INTERNAL 
 +CL-USER> (intern "Hello"
 +|Hello| 
 +NIL 
 +</code> 
 +The function ''intern'' creates a symbol from a string. 
 +If the string is all capital, we get a normal usual symbol, in the other case we get the pipes. 
 + 
 +The same is with Prolog, if your variable or any other symbol is not all caps, you will get a symbol in Lisp which has pipes around it: 
 + 
 +<code lisp> 
 +CL-USER> (json-prolog:prolog-simple-1 "member(X, [1,2,3])"
 +(((?X . 1))) 
 +CL-USER> (json-prolog:prolog-simple-1 "member(MYVAR, [1,2,3])"
 +(((?MYVAR . 1))) 
 +CL-USER> (json-prolog:prolog-simple-1 "member(MY_VAR, [1,2,3])"
 +(((?MY_VAR . 1))) 
 +CL-USER> (json-prolog:prolog-simple-1 "member(My_var, [1,2,3])"
 +(((|?My_var| . 1))) 
 +</code> 
 + 
 +In that case, to get the value of the variable you should also use pipes: 
 + 
 +<code lisp> 
 +CL-USER> (cut:var-value '|?My_var| 
 +                        (car (json-prolog:prolog-simple-1 "member(My_var, [1,2,3])"))) 
 +
 +</code> 
 + 
 +Please note that in Prolog, all variables have to start with a capital letter and cannot contain dashes: 
 +<code lisp> 
 +CL-USER> (json-prolog:prolog-simple-1 "member(x, [1,2,3])"
 +NIL 
 +CL-USER> (json-prolog:prolog-simple-1 "member(MY-VAR, [1,2,3])"
 +NIL 
 +</code> 
 +==== (NIL) vs NIL ====
          
 If we want to check, if a certain number is member of the list, we must declare the variable accordingly. e.g. If we want to check, if a certain number is member of the list, we must declare the variable accordingly. e.g.