1

I try to implement a C interface using a Prolog script based on GNU Prolog. My problem is to get single elements of a nested Prolog list.

Actually my C code looks like

...
int func;
PlTerm arg[10];
PlTerm *sol_gb;
PlBool res;
int nmb; 
char *strHead;
char *strTail;
PlLong nummero;
PlTerm pl_nummero;

Pl_Start_Prolog(argc, argv);


Pl_Query_Begin(PL_TRUE);

arg[0] = Pl_Mk_String(strRName);
arg[1] = Pl_Mk_Variable();
arg[2] = Pl_Mk_Variable();
arg[3] = Pl_Mk_String("true");

res = Pl_Query_Call(func, 4, arg);

sol_gb = Pl_Rd_List(arg[2]);
nmb = Pl_List_Length(sol_gb[0]);

strHead = Pl_Write_To_String(sol_gb[0]);      
printf("strHead = %s\n",strHead);
strTail = Pl_Write_To_String(sol_gb[1]);      
printf("strTail = %s\n",strTail);
...

The Prolog list returned in arg[2] looks like

[ [ Spezial Bolognese, 
    [2, ,Zwiebeln,300,gramm,Hackfleisch,10, ,Tomaten,
    100,ml,Sahne,500,gramm,Spaghetti] 
  ],
  [ Spaghetti Bolognese,
    [2, ,Zwiebeln gehackt,300,gramm,Hackfleisch,10, ,Fleischtomaten,
     100,ml,Sahne,500,gramm,Spaghetti]
  ]
]

The output of the conversion into a String is

strHead = [Spezial Bolognese,[2, ,Zwiebeln gehackt,300,gramm,Hackfleisch,
          10, ,Fleischtomaten,100,ml,Sahne,500,gramm,Spaghetti]]

strTail = [[Spaghetti Bolognese,[2, ,Zwiebeln gehackt,300,gramm,Hackfleisch,
          10, ,Fleischtomaten,100,ml,Sahne,500,gramm,Spaghetti]]]

So I assume, I am "nearly there" but as I have to re-activate my C knowledge I do not get the solution how to enter in the next level of the list to get finally each element as string ("Spezial Bolognese", next step: "2", "Zwiebeln" etc.).

How can I step through the Prolog list in C?

I would be very happy about every hint, thank you again!

kiw
  • 155
  • 6

2 Answers2

2

To get the content of a list from the C-code you can use 2 kind of functions.

First possibility (simple since the list is seen as a flat object but requires more memory and needs a proper list, i.e. does not work for lists not terminated by [])

int Pl_Rd_Proper_List_Check(PlTerm the_prolog_list, PlTerm *the_array_receiving_arguments);

this functions receives an array (it is up to you to ensure it is large enough), stores each element of the list in the array and returns the total number of elements. Example:

PlTerm list = ...some Prolog list...
int nElem = Pl_List_Length(list);
PlTerm *elem = (PlTerm *) calloc(nElem, sizeof(PlTerm));
Pl_Rd_Proper_List_Check(list, elem);
int i;
for(i = 0; i < nElem; i++) { 
   // here there is an argument in elem[i], let's print it
   Pl_Write(elem[i]);
}

Second possibility (more general but see a list as a chained list, each cell contains the head and a tail (a list))

PlTerm *Pl_Rd_List(PlTerm the_prolog_list);

This function returns an array of 2 elements corresponding to the head and the tail of the received list. This function should be called on each element of the list; either you know the number of elements or you test the end of the list (e.g. waiting for the end of list atom []). Here is a code doing this (it is expected to be inside the above loop since we know the second argument of the list is a nested list.

PlTerm list = ... some Prolog list...;
while(!Pl_Un_Atom(Pl_Atom_Nil(), list)) {
   PlTerm *lst_arg = Pl_Rd_List(list); // [0] = head element, [1] = tail list
   // here there is an argument in lst_arg[0], let's print it
   Pl_Write(lst_arg[0]);
   list = lst_arg[1];
}

In your example, the first list looks like:

[ 'Spezial Bolognese', 
    [2,' ','Zwiebeln',
     300,'gramm','Hackfleisch',
     10,' ','Tomaten',
     100,'ml','Sahne',
     500,'gramm','Spaghetti'] 
]

so the second element is a nested list. The following code uses the first method for the above list (which has 2 elements), and the second method for the nested list:

nElem = Pl_List_Length(sol_gb[0]);
PlTerm *elem = (PlTerm *) calloc(nElem, sizeof(PlTerm));
Pl_Rd_Proper_List_Check(sol_gb[0], elem);
int i;
for(i = 0; i < nmb; i++) { 
   if (i != 1) { 
      Pl_Write(elem[i]);
      printf("\n");
   } else {               // we know it is a list
      printf("(");
      PlTerm list = elem[i];
      while(!Pl_Un_Atom(Pl_Atom_Nil(), list)) {
          PlTerm *lst_arg = Pl_Rd_List(list); // [0] = head element, [1] = tail list
          printf(" ");
          Pl_Write(lst_arg[0]);
          list = lst_arg[1];
      }
      printf(" )\n");
   }
}

Here should be the output

Spezial Bolognese
( 2   Zwiebeln 300 gramm Hackfleisch 10   Tomaten 100 ml Sahne 500 gramm Spaghetti )
didou
  • 692
  • 3
  • 7
  • Many, many thanks! :) I will test it and then give feedback. --- It works!! Uff, I am really grateful for your help! – kiw Sep 25 '14 at 07:33
0

The sample code you give of the lists of lists sounds like a textbook example of a very bad choice for knowledge representation. I strongly suggest you change it to a more declarative representation. Something like:

% pizza(Name, Steps)
pizza('Spezial Bolognese', ...).
...

where Steps could be a list of step(...) terms. This would potentially make processing the information easier and more efficient. For example, in the Prolog side, you could then use the standard arg/3 predicate to access a specific element in a step. With lists, you have no choice other than traversing them every time you want an element other than the list head.

Paulo Moura
  • 18,373
  • 3
  • 23
  • 33
  • Thanks for the advice. But even for a simple Prolog list, do you know how to get a single element from the list within the C interface? – kiw Sep 24 '14 at 09:08