Newsgroup: it.comp.lang.c++
Titolo: multiple choice test

Ciao a tutti,
Sto imparando il c++, sono alle prime armi....
Il mio problema principale e' capire come fare a leggere un file di
testo per utilizzarlo come fonte dati per un multiple choice test.
qualcuno mi sa aiutare?

...
    [snip]

Per leggere un file di testo, a qualunque scopo tu voglia farlo, il C++
fornisce gli "iostream", accedibili con "#include <iostream>" (iostream.h
nei vecchi compilatori, ma in tutto il seguito supporro` che tu abbia a tua
disposizione compilatori aggiornati); per gestire le stringhe, analogamente,
"#include <string>", e per gestire i vettori (ad esempio di stringhe),
"#include <vector>".
Ecco dunque ad esempio come leggere in memoria tutto un file
di testo, presentandolo come vettore di stringhe:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
bool
leggifile(string filename, vector<string>&result)
{
    ifstream fin(filename);
    if(!fin) return false;
    while(fin) {
        string s;
        getline(fin,s);
        result.push_back(s);
    }
    return true;
}
Questa e` una funzione del tutto generale e quindi
riusabile; mettila nel suo sorgente leggifile.cpp, e il
risultante file oggetto leggifile.obj ti servira` in questo
progetto e anche in altri futuri.    Ti vorra` anche un
header file leggifile.h per i "clienti" di questa routine:
#include <string>
#include <vector>
bool leggifile(std::string name,
    std::vector<std::string>&result);
Nota che qui uso esplicitamente il prefisso std, NON
obbligando il cliente che include questo header ad
"importare" volente o nolente gli identificatori string e
vector dal namespace std al suo!
Come questo "file di testo" (ora divenuto un vettore di
stringhe in memoria) debba essere "usato come fonte
dati per un multiple choice test" e` ancora, naturalmente,
tutto da decidere.  Anzitutto dovra` esserci una piccola
sintassi interna al file, che permetta di determinare:
-- dove comincia e finisce il testo di una domanda (e se
   quel testo debba essere in qualche modo soggetto a
    formattazione, ad es. HTML?);
-- dove comincia e finisce il testo di ogni possibile
    risposta da presentare a quella domanda, con quale
    eventuale formattazione;
-- cosa identifichi la risposta giusta fra le tante.
Questa sintassi devi deciderla tu, come progettista,
tenendo conto sia della comodita` di farne il "parsing",
sia di quella di scrivere i file di testo nel formato da
te deciso.
Un semplice esempio, potresti decidere:
-- nessuna formattazione, bensi` puro testo liscio;
-- la prima riga di ogni domanda inizia con '?'
-- le possibili risposte seguono la domanda
-- la prima riga di ogni possibile risposta sbagliata
   inizia con '!'
-- la prima riga della risposta giusta inizia con '*'
E` un codice arbitrario, ma usabile, avendo scelto
come marcatori caratteri che poco importa usare
all'inizio di una normale riga di testo.
A questo punto possiamo concepire una piccola
struttura di classi:
-- chiaramente occorre una classe "blocco di testo",
   fatta da un vettore di righe e corrispondente ad
    un singolo testo da presentare come domanda o
    come possibile risposta -- e questa e` facile:
class blocco {
public:
    std::vector<std::string> testo;
};
senza incapsulamento o altro, un puro contenitore
di dati.  Potremo, volendo, incapsulare le tipiche
funzionalita` come appendere una riga al testo gia`
presente, o emettere l'intero blocco su ostream.
-- una "domanda" comprendera`:
    -- blocco di testo "testo della domanda"
    -- vector di blocchi di testo "possibili risposte"
    -- indice nel vector della "risposta giusta"
Costruendo l'oggetto-domanda dovremo anche
fare i controlli-base:
    -- c'e` un testo della domanda,
    -- c'e` un numero complessivo di possibili
        risposte pari almeno a due,
    -- c'e` esattamente una risposta giusta fra
        quelle possibili
e segnalare errore, terminando il programma, se
qualche condizione e` violata.
"Presentare una domanda" consiste nell'emettere
il testo della domanda, e quello delle possibili
risposte, con opportuna formattazione (ad esempio
numerandole, ma in termini di GUI e` invece anche
concepibile ragionare in termini di radiobutton ad
esse associati, ecc); bisognera` poi decidere come
accettare i tentativi di risposta dell'utente e cosa fare
in caso di risposta sbagliata (dare un'altra chance, o
piu`; o semplicemente registrare l'errore; ecc ecc).
Come vedi si tratta solo di un abbozzo di progetto,
perche` troppi sono ancora i "gradi di liberta`" presenti
(GUI o meno, ripetizione o meno della risposta se la
prima e` errata, ecc ecc).  Il punto fondamentale e`
di costruirsi al piu` presto le astrazioni "evidenti" (e di
usare quelle, come string e vector, gia` fornite dalla
libreria standard, invece di arrabattarsi in termini di
bassissimo livello come char* e array!), farsene un
piccolo "toolkit" anzitutto concettuale, e iterativamente
costruire e determinare l'esatta funzionalita` necessaria
ragionando nei termini piu` astratti e produttivi che sono
resi accessibili da questo piccolo processo di astrazione
e concettualizzazione.
Alex

 ...
Innanzitutto grazie mille per le ottime delucidazioni!
Qualche notizia maggiore sul mio progettino...
Ho circa una cinquantina di test in formato TXT
in cui e' presente una domanda e quattro possibili risposte (di cui
una sola esatta)
Il mio obiettivo quindi e' quello di creare un semplice programma che
giri sotto dos senza alcun bisogno di interfacce grafiche ( anche
perche' non ho la piu' pallida idea di come si creino!) nel quale sia
possibili indicare il file da utilizzare per il test ( da qui il
problema di come leggerlo).
I file sono strutturati con frasi racchiuse da doppie virgole "
del tipo:
"NumeroDomanda"
"Domanda"
"RispostaA"
"RispostaB"
"RispostaC"
"RispostaD"
"Soluzione"
Ovviamente le frasi sono di lunghezza variabile.
Un ulteriore evoluzione e' quella di inserire un Numero di indicazione
dell'argomento della domanda attraverso il quale, oltre ad indicare
all'utente lesito del test e' possibile conoscere anche l'area in cui
si e' maggiormente impreparati. E' aggiungere anche la possibilita di
conoscere le risposte errate e veder un possibile commento alle
soluzioni.
L'evoluzione dovrebbe essere di questo tipo:
"NumeroDomanda"
"AreaArgomento"
"Domanda"
"RispostaA"
"RispostaB"
"RispostaC"
"RispostaD"
"Soluzione"
"NoteAllaSoluzione"
Qualsiasi consiglio e' molto ben accetto :-)
Qualche notizia su di me...
Ho iniziato a studiare il c++ proprio come sfida per riuscire a creare
programmi di questo tipo... E' da pochissimi mesi che lo sto studiando
e sono consapevole di aver iniziato con un obiettivo difficile... ma
se non fosse difficile che gusto ci sarebbe??
Grazie ancora
AlessandroT

Qualsiasi consiglio e' molto ben accetto :-)
Allora, la prima osservazione e` che il formato di questi file non
e` molto "compatibile", prima e dopo la loro "evoluzione".  Cioe`,
il formato vecchio prevede rigidamente che la seconda riga sia
la domanda, mentre nel formato nuovo la seconda riga e` l'area
dell'argomento, la domanda viene solo alla terza riga, ecc ecc.
Una soluzione sarebbe di rendere i file piu` "auto-descriventi":
ad esempio, iniziare ogni riga con un codice di un carattere
che dica cosa la riga ci sta a fare.  Questo ti riduce molto la
rigidita`... ad esempio non e` piu` obbligatorio che ci siano
esattamente 4 risposte (le risposte si autoidentificano come
1, 2, 3 eccetera).  Ogni programma che legge questi file puo`
serenamente ignorare i codici che non conosce (puoi avere
un separato "programma-diagnostico" che aiuta l'autore dei
file, identificandogli i codici sconosciuti).
Quanto e` rigido e gia` deciso il formato dei file, e quanto
invece negoziabile?  Se negoziabile, aggiungi il "codice di
record" e togli quelle virgolette... a che servono?  Se no,
dovrai toglierle da programma.  Se anche in questo momento
hai i file fatti cosi`, potresti fare una "singola passata" (un
piccolo programma ad hoc) per trasformarli nel formato
che ti serve, tipo (indico commenti con ! sino a fine riga):
N 54        ! numero di domanda
D Il C++ e` il linguaggio migliore per quest'applicazione?
1 Manco morto
2 Neppure per sogno
3 Ma scherzi?
4 All of the above
G 4
Una volta che le righe si auto-identificano, avrai anche la
possibilita` di avere molteplici domande (e relative risposte)
una dopo l'altra nello stesso file di testo (che puo` essere
comodo).  Comunque sara` sempre necessario indicare
al programma l'esatto insieme di file di testo che deve
leggere in un certo run, e ci sono vari modi possibili, tipo
elencarli fra gli argomenti del programma.  Ma ti fara`
certo anche comodo avere un codice F "leggi quest'altro
file", cosi` un file di testo puo` specificarne altri:
F dom54.txt
F dom55.txt
F dom58.txt
Considera anche la possibilita` di avere la "espansione
automatica" delle wildcard in linea argomenti, ma cio` non
ti servira` se vuoi le wildcard anche nelle righe-F.
Cosi` impostati i dati in pasto al programma, la struttura
del programma a sua volta si comincia a delineare:
--  una classe si occupera` dell'input, una riga alla
    volta, con qualsiasi "pulizia" e "analisi" sia necessario
    fare sulle righe (rimozione commenti, separazione
    del carattere di comando dal resto della riga, ecc)
    e soprattutto "concatenazione logica" dei vari file
    separati ecc -- a questa classe "passera` la palla"
    il comando F per dirgli "a questo punto leggimi
    tutto quest'altro file prima di continuare con quello
    corrente", ecc ecc.
-- dalla classe di cui sopra usciranno coppie
    carattere-di-comando -- riga-argomento del comando
    e bisognera` associare l'azione giusta ai vari
    caratteri di comando.  La soluzione classica e`
    uno switch, poco object-oriented; piu` potente
    una mappa da caratteri a classi che sanno trattare
    le righe in modo opportuno (classi-comando),
    tutte derivate da una stessa classe astratta e
    con l'opportuna funzione virtuale (tenute nella
    mappa per puntatore o reference...!).
    Molte delle righe andranno implicitamente
    aggiunte alla "domanda corrente che stiamo
    costruendo", altre dovranno causare invece
    il "completamento" della domanda corrente
    e l'inizio della costruzione di una nuova.  Ci
    vorra` una classe (singleton globale) che ci
    gestisca questi concetti su richiesta delle
    classi-comando.
Tutto questo ti costruisce in memoria un piccolo
"database" -- un vettore di classi-domanda, senza
dubbio, ognuna col suo numero, testo domanda,
possibili risposte, ecc ecc.  Terminata la fase di
input, che costruisce questo vettore, ci sara` la
fase interattiva in cui, secondo qualche criterio,
si sceglie la prossima domanda da porre, la si
pone, si raccoglie la risposta, la si valuta, e si
agisce di conseguenza.
Questa interattiva e` anche la parte cui in futuro
interessera` sicuramente dare una veste grafica,
ma per il momento non occorre preoccuparsene.
Avendo in mente una falsariga della struttura, a
questo tipo di livello, puoi iniziare a tracciare il
design delle singole classi.  Buon lavoro!-)
P.S.: senza dubbio il programma sarebbe di piu`
agevole sviluppo in Perl 5, ma con una libreria
opportuna (e la Standard C++ Library non e` male)
si puo` sicuramente fare anche in C++ senza troppo
mal di testa... le considerazioni di design che ho
fatto a questo livello sono d'altra parte piuttosto
indifferenti rispetto al linguaggio usato, purche` sia
ad oggetti (C++, Java, Perl, Eiffel, tutto fa brodo:-).
Alex

giri sotto dos
In questo periodo sto sviluppando qualcosa del genere pure io.
All'indirizzo
http://www.eurolink.it/scuola/didaweb/materiali/dwsup-inftel/linguaggi/ft00a3a.zip
puoi trovare la versione standard I/O di FreeTest alfa 3.
Nel file zip trovi i sorgenti ed il manuale oltre che l'eseguibile per DOS. I
sorgenti dovrebbero compilarsi correttamente sotto qualsiasi piattaforma in
quanto ho utilizzato un C++ piuttosto arcaico (BC++ 3.1 del 1992) e tutte le
operazioni di I/O sono eseguite utilizzando lo standard I/O
Per quanto riguarda il formato dei dati, il test deve essere descritto in un
file testo utilizzando dei tag tipo html
Il programma e' di pubblico dominio e non puo' essere utilizzato per fini di
lucro.
Entro un mesetto dovrei avere completato una applet java che permettera' di
inserire i test descritti nel formato freetest direttamente nelle pagine web.
Ciao
---------------------------------------
Email: sostituire "nospam" con "netsis"

lucro.
Attenzione alla contraddizione in termini: se l'hai posto nel
pubblico dominio, non hai piu` voce in capitolo sul come venga
usato (fini di lucro compresi); se vuoi dettare i termini d'uso
devi invece conservare il copyright e "concedere l'uso sotto
licenza".  Se un'opera e` stata pubblicata nel pubblico dominio
dall'autore (o altro possessore dei diritti d'autore), allora il
passaggio al pubblico dominio e` definitivo (l'autore o altri
non puo` retroattivamente togliere cio` che aveva concesso).
Alex
--
 ____    Alex Martelli, Bologna, Italia -- email: 
al...@magenta.com
 \SM/___                                 
http://magenta.com/~alex
  \/\bi/     You never know what is enough unless you know what
     \/          is more than enough.

licenza".
Purtroppo sono piuttosto ignorante sull'argomento free-share-pd ecc.
Cosa ne dici di correggermi la licenza d'uso  ?  O:)
Allegata alla documentazione ho scritto la seguente licenza :
------------------------------------------------------------
Licenza d'uso del programma FreeTest
Il programma Freetest è di pubblico dominio e può essere liberamente copiato ed
utilizzato
Gli unici vincoli da rispettare sono i seguenti : 
Il codice sorgente non può essere utilizzato per fini commerciali
Chi altera il codice sorgente per realizzare delle nuove versioni di FreeTest
deve distribuire il nuovo programma gratuitamente completo dei nuovi codici
sorgenti
------------------------------------------------------------


Cosa ne dici di correggermi la licenza d'uso  ?  O:)
Non sono un avvocato (quello ti ci vorrebbe...), e comunque 
per quel che so (ma sembra sensato) non puoi retroattivamente 
cambiare una licenza se non ti eri riservato il diritto di farlo (e
ponendo software nel pubblico dominio non riservi diritti).
Mi pare di capire che tu cerchi di ottenere una specie di clone
del GPL (la licenza dell'FSF) riducendo pero` ulteriormente i 
diritti dei tuoi utenti per vietargli l'uso commerciale -- siamo a
mille miglia dal pubblico dominio e quindi ti suggerisco, per
evitare confusioni, di evitare assolutamente questa frase e
anzi al contrario asserire recisamente il tuo copyright e il fatto
che concedi solo una licenza d'uso limitata e soggetta alle
seguenti condizioni.  Le condizioni poi devono essere una
marea, come ad esempio quella, che non nomini, se se il
software viene ridistribuito, sia in forma integra o parziale,
intonsa ovvero alterata, non devono pero` essere rimosse
ne` alterate le notifiche del tuo copyright e delle condizioni
di licenza (altrimenti se A lo passa a B togliendo le tue
notifiche, B non sapendo del divieto di uso commerciale
non puo` certo rispettarlo, ecc ecc).
Ti suggerisco di cominciare dalla General Public License
(GPL) che trovi allegata a qualsiasi programma della Free
Software Foundation (FSF), che si e` dimostrata abbastanza
legalmente solida negli anni, e provare ad aggiungervi le
ulteriori restrizioni che desideri imporre ai tuoi utenti.
Alex