Newsgroup: it.comp.lang.c++
Titolo: Boost token

Ho scaricato boost perché mi interessava usare il tokenizer.
Purtroppo ci sto capendo ben poco.
Avrei bisogno di leggere un file di testo TBox.txt che contiene 
definizioni del tipo:
;CONDITIONER-ACTUATOR concept
(createConcept CONDITIONER-ACTUATOR
	(pand ACTUATOR
	(all controlling CONDITIONER)
	(all imposing CONDITIONER-REF-DATA)
	(atLeast 1 controlling)
	;(atLeast 1 imposing)
	)
)
Il puntevirgola è il carattere che indica i commenti, sulla riga dopo 
viene definito il concetto CONDITIONER-ACTUATOR.
Quello che dovrei fare è creare per ogni concetto un nodo di una rete 
bayesiana (usando le librerie contenute nelle dilib). Ogni nodo contiene 
una tabella che indica le probabilità condizionate del nodo rispetto ai 
nodi genitori. Le probabilità le aggiungerei in una riga di commento, 
qualcosa come:
;p(CONDITIONER-ACTUATOR|ACTUATOR)=0.5
Quello che vorrei fare è scorrere il testo, ogni volta che trovo 
createConcept prendo la parola dopo e la aggiungo come nodo all'elenco 
dei nodi, aggiungo un arco che va dalla seconda parola in parentesi al 
nodo corrente, imposto come probabilità il valore dopo l'uguale.
Potrei usare boost per fare tutto ciò?

 )
Non so se ti può essere utile saperlo, ma questo è LISP.
E.


Non mi stupisce, in realtà sono comandi di NeoClassic, un sw per gestire 
ontologie, che viene distribuito in C++ e in Lisp.
Un sw simile, PowerLoom, viene distribuito in C++ e Lisp ma viene 
sviluppato in STELLA, Strongly Typed, Lisp-like LAnguag.
Credo che il Lisp sia molto usato per quel tipo di applicazioni ma io 
devo usare per forza C++ perchè è il linguaggio usato in laboratorio e 
che è stato usato per il resto del progetto (si tratta di domotica)
Quello che devo fare è leggere il file di testo e passare i nomi dei 
concetti a dei metodi (che importo dal librerie dlib) che mi 
costruiscono il grafo.

 costruiscono il grafo.
Sì, certo la domanda era chiara. Io non ti so aiutare perché non conosco 
la libreria che vuoi utilizzare. Io farei direttamente il parser a mano, 
  non è molto difficile... (ma è meglio se riesci ad utilizzare la 
libreria).
E.

 Potrei usare boost per fare tutto ciò?
Se ti bastano le espressioni regolari: Boost.Regex. 
Altrimenti, se vuoi costruirti un parser LL: Boost.Spirit.
Max


Perdona la mia ignoranza, cos'è un parser LL?


WIYFT: 
http://en.wikipedia.org/wiki/LL_parser
Ciao,
Nicola

 costruiscono il grafo.
Si, ma perchè non embeddi un interprete lisp e fai fare a lui il
lavoraccio?
-- 
-riko


Non è esattamente Lisp, si tratta di questo:
http://www.bell-labs.com/project/classic/neo.html
La prima versione, Classic, era scritta e distribuita in Lisp.
Quella più recente, NeoClassic è scritta e distribuita in C++, ma 
logicamente usa gli stessi comandi di Classic.
Dici che i comandi sono proprio LISP?
Hai fatto caso a "pand" anzichè "and"?
Il codice C++ della AT&T usa and, la sostituzione l'hanno fatta nel 
laboratorio dove sto facendo la tesi, per (mi hanno detto) problemi di 
compatibilità cn l'and dei compilatori C++ attuali.
Ammesso che un interprete Lisp possa digerire i comandi di (Neo)Classic, 
mi permetterebbe di richiamare i metodi delle librerie C++?

cr610 ha scritto:

Per questo e altri problemi, secondo me è molto più facile riscriversi 
il parser e l'interprete, piuttosto che includere un interprete lisp.
E.

 il parser e l'interprete, piuttosto che includere un interprete lisp.
Dipende probabilmente da cosa deve gestire. Scrivere un parser per un
linguaggio lispish è catalogabile come banalità. Scrivere un interprete
dipende in buona sostanza da "quanto lisp" deve riconoscere. Tipicamente
scrivere un interpete completo è tutto fuorchè una bazzecola.
Se invece incidentalmente non sta gestendo sorgenti lisp (e manco roba
turing completa), non ha da gestire definizioni di macro, funzioni,
cazzi e mazzi, e deve solo buttare a memoria dei fatti, si, allora
probabilmente fa prima come dici tu.
-- 
-riko

 probabilmente fa prima come dici tu.
Sì, infatti. Il primo messaggio dell'OP fa pensare che sia questo il caso.
E.

 Se invece incidentalmente non sta gestendo sorgenti lisp (e manco roba
No, non ho dei sorgenti LISP. Come ho già scritto, è un sw per gestire 
ontologie che in origine era stato sviluppato in Lisp. Ecco perché i 
comandi sembrano Lisp.



Allora, torniamo al mio primo messaggio.
Con un testo del tipo:
;CONDITIONER-ACTUATOR concept
(createConcept CONDITIONER-ACTUATOR
     (pand ACTUATOR
     (all controlling CONDITIONER)
     (all imposing CONDITIONER-REF-DATA)
     (atLeast 1 controlling)
     ;(atLeast 1 imposing)
     )
)
Vorei scorrere il file di testo, prendere le stringhe che seguono 
"createConcept" ed usarle come nodi.
Quindi all'interno di enum  nodes dovrebbero esserci tali stringhe.
Il loro numero dovrebbe essere passato a bn.set_number_of_nodes()
bn.add_edge(A, D);
significa che c'è un arco che va da A a D, nell'esempio sopra 
CONDITIONER-ACTUATOR viene definito come una specializzazione di 
ACTUATOR, quindi ci dovrà essere un arco da ACTUATOR a CONDITIONER-ACTUATOR
Per le probabilità devo ancora decidere la sintassi con cui inserirle 
nel file di testo.
Quello che segue è l'esempio contenuto nelle dlib, su cui mi ero basato 
per capire come gestire le reti bayesiane.
/*
     This is an example illustrating the use of the Bayesian Network
     inference utilities found in the dlib C++ library.
     In this example all the nodes in the Bayesian network are
     boolean variables.  That is, they take on either the value
     0 or the value 1.
     The network contains 4 nodes and looks as follows:
         B     C
         \\   //
          \/ \/
            A
            ||
            \/
             D
            ||
             E
     The probabilities of each node are summarized below.  (The probability
     of each node being 0 is not listed since it is just P(X=0) = 
1-p(X=1) )
         p(B=1) = 0.01
         p(C=1) = 0.001
         p(A=1 | B=0, C=0) = 0.01
         p(A=1 | B=0, C=1) = 0.5
         p(A=1 | B=1, C=0) = 0.9
         p(A=1 | B=1, C=1) = 0.99
         p(D=1 | A=0) = 0.2
         p(D=1 | A=1) = 0.5
         p(E=1 | D=0) = 0.2
         p(E=1 | D=1) = 0.5
*/
#include "dlib/bayes_utils.h"
#include "dlib/graph_utils.h"
#include "dlib/graph.h"
#include "dlib/directed_graph.h"
#include <iostream>
using namespace dlib;
using namespace std;
int main()
{
// Use an enum to make some more readable names for our nodes.
enum nodes
     {
         A = 0,
         B = 1,
         C = 2,
         D = 3,
	E = 4
     };
     // The next few blocks of code setup our bayesian network.
     // The first thing we do is tell the bn object how many nodes it has
     // and also add the three edges.  Again, we are using the network
     // shown in ASCII art at the top of this file.
     bn.set_number_of_nodes(5);
     bn.add_edge(A, D);
     bn.add_edge(B, A);
     bn.add_edge(C, A);
     bn.add_edge(D, E);
     // Now we inform all the nodes in the network that they are binary
     // nodes.  That is, they only have two possible values.
     set_node_num_values(bn, A, 2);
     set_node_num_values(bn, B, 2);
     set_node_num_values(bn, C, 2);
     set_node_num_values(bn, D, 2);
     set_node_num_values(bn, E, 2);
     assignment parent_state;
     // Now we will enter all the conditional probability information 
for each node.
     // Each node's conditional probability is dependent on the state of 
its parents.
     // To specify this state we need to use the assignment object. 
This assignment
     // object allows us to specify the state of each nodes parents.
     // Here we specify that p(B=1) = 0.01
     // parent_state is empty in this case since B is a root node.
     set_node_probability(bn, B, 1, parent_state, 0.01);
     // Here we specify that p(B=0) = 1-0.01
     set_node_probability(bn, B, 0, parent_state, 1-0.01);
     // Here we specify that p(C=1) = 0.001
     // parent_state is empty in this case since B is a root node.
     set_node_probability(bn, C, 1, parent_state, 0.001);
     // Here we specify that p(C=0) = 1-0.001
     set_node_probability(bn, C, 0, parent_state, 1-0.001);
     // This is our first node that has parents. So we set the parent_state
     // object to reflect that A has both B and C as parents.
     parent_state.add(B, 1);
     parent_state.add(C, 1);
     // Here we specify that p(A=1 | B=1, C=1) = 0.99
     set_node_probability(bn, A, 1, parent_state, 0.99);
     // Here we specify that p(A=0 | B=1, C=1) = 1-0.99
     set_node_probability(bn, A, 0, parent_state, 1-0.99);
     // Here we use the [] notation because B and C have already
     // been added into parent state.
     parent_state[B] = 1;
     parent_state[C] = 0;
     // Here we specify that p(A=1 | B=1, C=0) = 0.9
     set_node_probability(bn, A, 1, parent_state, 0.9);
     set_node_probability(bn, A, 0, parent_state, 1-0.9);
     parent_state[B] = 0;
     parent_state[C] = 1;
     // Here we specify that p(A=1 | B=0, C=1) = 0.5
     set_node_probability(bn, A, 1, parent_state, 0.5);
     set_node_probability(bn, A, 0, parent_state, 1-0.5);
     parent_state[B] = 0;
     parent_state[C] = 0;
     // Here we specify that p(A=1 | B=0, C=0) = 0.01
     set_node_probability(bn, A, 1, parent_state, 0.01);
     set_node_probability(bn, A, 0, parent_state, 1-0.01);
     // Here we set probabilities for node D.
     // First we clear out parent state so that it doesn't have any of
     // the assignments for the B and C nodes used above.
     parent_state.clear();
     parent_state.add(A,1);
     // Here we specify that p(D=1 | A=1) = 0.5
     set_node_probability(bn, D, 1, parent_state, 0.5);
     set_node_probability(bn, D, 0, parent_state, 1-0.5);
     parent_state[A] = 0;
     // Here we specify that p(D=1 | A=0) = 0.2
     set_node_probability(bn, D, 1, parent_state, 0.2);
     set_node_probability(bn, D, 0, parent_state, 1-0.2);
     // Here we set probabilities for node E.
     // First we clear out parent state so that it doesn't have any of
     // the assignments for the B and C nodes used above.
     parent_state.clear();
     parent_state.add(D,1);
     // Here we specify that p(E=1 | E=1) = 0.5
     set_node_probability(bn, E, 1, parent_state, 0.5);
     set_node_probability(bn, E, 0, parent_state, 1-0.5);
     parent_state[D] = 0;
     // Here we specify that p(D=1 | A=0) = 0.2
     set_node_probability(bn, E, 1, parent_state, 0.2);
     set_node_probability(bn, E, 0, parent_state, 1-0.2);
     // We have now finished setting up our bayesian network.  So lets 
compute some
     // probability values.  The first thing we will do is compute the 
prior probability
     // of each node in the network.  To do this we will use the join 
tree algorithm which
     // is an algorithm for performing exact inference in a bayesian 
network.
     // First we need to create an undirected graph which contains set 
objects at each node and
     // edge.  This long declaration does the trick.
     typedef graph<set<unsigned long>::compare_1b_c, set<unsigned 
long>::compare_1b_c>::kernel_1a_c join_tree_type;
     join_tree_type join_tree;
     // Now we need populate the join_tree with data from our bayesian 
network.  The next to
     // function calls do this.  Explaining exactly what they do is 
outside the scope of this
     // example.  Just think of them as filling join_tree with 
information that is useful
     // later on for dealing with our bayesian network.
     create_moral_graph(bn, join_tree);
     create_join_tree(join_tree, join_tree);
     // Now we have a proper join_tree we can use it to obtain a 
solution to our
     // bayesian network.  Doing this is as simple as declaring an 
instance of
     // the bayesian_network_join_tree object as follows:
     bayesian_network_join_tree solution(bn, join_tree);
     // now print out the probabilities for each node
     cout << "Using the join tree algorithm:\n";
     cout << "p(A=1) = " << solution.probability(A)(1) << endl;
     cout << "p(A=0) = " << solution.probability(A)(0) << endl;
     cout << "p(B=1) = " << solution.probability(B)(1) << endl;
     cout << "p(B=0) = " << solution.probability(B)(0) << endl;
     cout << "p(C=1) = " << solution.probability(C)(1) << endl;
     cout << "p(C=0) = " << solution.probability(C)(0) << endl;
     cout << "p(D=1) = " << solution.probability(D)(1) << endl;
     cout << "p(D=0) = " << solution.probability(D)(0) << endl;
     cout << "p(E=1) = " << solution.probability(E)(1) << endl;
     cout << "p(E=0) = " << solution.probability(E)(0) << endl;
     cout << "\n\n\n";
}

 "createConcept" ed usarle come nodi.
Dunque mi pare che creare l'albero non ti dia problemi. Quello che ti 
manca è il parser per leggere il file.
Tipicamente il parsing si va con una struttura a livelli. Al primo 
livello leggi una riga alla volta e la metti in memoria. A questo 
livello scarti le righe con i commenti. Al secondo livello leggi un 
token alla volta. I tuoi token sono:
1. parentesi: '(' o ')'
2. identificatori
3. costanti numeriche
Il terzo livello è quello che legge ricorsivamente le liste e quindi 
costruisce l'albero in memoria.
E.

 E.
Grazie, mi potresti fare un esempio di codice?
L'algoritmo + o - l'avevo pensato (io avrei scorso più volte il file, lo 
so sarebbe inefficiente, ma pensavo che avrei evitato di usare un buffer 
tra testo e grafo) i problema è ce non so come implementarlo, di C++ ho 
giusto un'infarinatura.

 giusto un'infarinatura.
Usa un parser generator. Io suggerisco antlr.
-- 
-riko

 giusto un'infarinatura.
   TokenType TokenReader::get_token() {
     char c;
     if (rejected) {
       rejected=0;
       return token;
     }
     command="";
     while (isspace(c=in.get())) {
       if (c=='\n') return token=eof_token;
     }
     if (!in || c==-1) return token=eof_token;
     if (isdigit(c)) {
       int num=0,den=1;
       bool dot=false;
       do {
	if (isdigit(c)) {
	  num=10*num+(c-'0');
	  if (dot) den*=10;
	} else if (c=='.') {
	  if (dot) break;
	  dot=true;
	} else break;
	c=in.get();
       } while (in);
       in.putback(c);
       if (dot) {
	store_double=(double)num/(double)den;
	return token=double_token;
       } else {
	integer=num;
	return token=integer_token;
       }
     } else if (isalpha(c)) {
       command=string(1,c);
       while(true) {
	c=in.get();
	if (!in) break;
	if (!(isalnum(c) || c=='_')) {in.putback(c);break;}
	command+=c;
       }
       return token=identifier_token;
     } else {
       MAP::iterator i=commands.find(string(1,c));
       if (i!=commands.end()) {
	command=i->first;
	return token=command_token;
       }
       throw ParseError("unknown token '"+string(1,c)+"'");
     }
   }
E.


Grazie, ma potresti darmi qualche spiegazione?


 Grazie, ma potresti darmi qualche spiegazione?
Cosa non ti è chiaro?


 Cosa non ti è chiaro?
Praticamente tutto!


 Praticamente tutto!
Penso che sarebbe utile un buon libro sul C o il C++.
E.