-
Notifications
You must be signed in to change notification settings - Fork 0
Generates aleatory text based on input data (Operating Systems 2 project)
License
CuriousCI/word-chain
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
AWC NOTA* - se preferisce, è possibile eseguire `sudo make install` per installare il comando `awc` (il progetto) e invocare `man awc` per vedere la pagina man; alternativamente può anche eseguire `man ./awc.1` DESCRIZIONE Il programma ha due opzioni principali '--csv' (esegue il compito 1) e '--text' (esegue il compito 2). Di default l'input viene preso da `stdin`, e l'output viene scritto su `stdout` in modo da facilitare l'interazione con altri comandi tramite redirection. È comunque possibile specificare con -f, --file il file di input e con -o, --output il file di output. OPZIONI COMPITO 1 -c, --csv Genera un CSV a partire dal testo in input OPZIONI COMPITO 2 -t, --text Genera un TESTO a partire da un CSV in input -n, --number=NUMERO Obbligatorio, specifica il numero di parole da generare -w, --word=PAROLA Specifica la parola iniziale (case insensitive) OPZIONI COMUNI -p, --parallel Eseguire il comando con 3 processi paralleli -f, --file=FILE Specifica il file in input -o, --output=FILE Specifica il file in output -l, --locale=LOCALE Specifica il locale da usare per decodificare e codificare i testi Per un elenco dei 'locale' installati sulla macchina, c'è il comando `locale -a` ESEMPI D'USO awc --csv -f SRC -o DST (compito 1) awc --text -n 1000 -f SRC -p > DST (compito 2) awc --csv -f SRC -p --locale it_IT.UTF-8 > DST (compito 2) awc --text -n 1000 -f SRC -p -w ciao > DST (compito 2) cat tests/lotr | grep -i frodo | awc --csv | awc --text -n 100 -w frodo echo "Ciao come stai? Ciao, tutto bene!" | awc -c | awc -t -n 100 -w CIAO COLLECTION, COLLECT, CONSUME API La parte più intressante del progetto potrebbe essere questa l'API COLLECTION, COLLECT e CONSUME. A cosa serve? Nel single-process è utile mettere le parole generate in un ARRAY o un VECTOR Nel multi-process è utile mandarle direttamente su una PIPE Per non scrivere due volte la funzione, mi sono ispirato un po' ad altri linguaggi (come Rust). - `void *collection` - può essere qualsiasi cosa: un VEC, un ARRAY, una PIPE, un FILE etc... - `int (*collect)(void *, char *)` - salva un dato (nel mio caso sempre una stringa) dentro alla `collection` - tramite collect() si può decidere di fare `push()` in un VETTORE, o di fare `fprintf()` in un FILE o `write()` in una PIPE. - `char *(*consume)(void *)` - tramite consume() si può leggere un dato da una collection (magari con un `getline()` da un FILE o con un `next()` su un ITER) SCELTA DELLA PAROLA SUCCESSIVA Possiamo immaginare una linea fra 0 e 1 divisa in tante sezioni; ogni sezione è associata ad una parola, ed è grande quanto la frequenza di tale parola Ad esempio, una parola che ha il 0.3 di frequenza occuperà una sezione grande 0.3 0 1 |________|__|__|_____| Con `rand()` viene scelto un punto a caso fra 0 e 1 0 1 |____________x_______| Ora l'obiettivo è cercare la sezione associata a quel punto, e la parola associata a tale sezione sarà la successiva. 0 1 |________|__|x_|_____| In questo modo, dato che `rand()` dovrebbe essere equiprobabile, le parole avranno una probabilità di essere scelte uguale alla loro frequenza. STRUTTURE DATI Per le varie strutture ho deciso di usare l'ENCAPSULATION per nascondere la struttura interna, mettendo a disposizione solamente l'API necessario ad usare la struttura (limitato all'uso che ne faccio nel programma; è inutile scrivere una funzione `remove()` per la MAP se non serve) VEC_T È una struttura dati simile al `std::vector` di C++ o al `Vec<T>` di Rust: un ARRAY. Permette solo di aggiungere elementi in coda, ed è accessibile solo tramite ITER. Non ho usato la LinkedList per provare a sfruttare al massimo il caching delle aree di memoria contigue. MAP_T Ho sperimentato con diverse strutture per memorizzare i dati, e ho deciso di rimanere su una MAP implementata tramite Red Black Tree (studiato al corso di "Introduzione agli algoritmi", lo avevo già implementato l'anno scorso). Il Red Black Tree è un po' più lento dell'HashMap con cui avevo inizialmente provato, ma non ho il problema del dimensionamento della memoria: occupa solo la memoria che gli serve e non fa allocazioni inutili, e non devo decidere una formula per ottimizzare le dimensioni della mappa in base alla dimensione dell'input. Inoltre, non mi andava di scrivere il codice per ridimensionare l'HashMap, e non avrei fatto in tempo ad implementare la SwissMap di Google. ITER_T Iteratore su una collezione. Quando viene invocato `next()` su un iteratore, ritorna l'elemento attuale se presente e sposta l'iteratore all'elemento successivo, altrimenti ritorna NULL. COUNTER_T Per contare le frequenze delle parole uso una MAP che ad ogni parola associa un COUNTER_T COUNTERS = { WORD1: (COUNTER_T) { COUNT, // Quante volte compare WORD1 NEXT_WORDS = { WORD2: COUNT // Quante volte compare WORD2 dopo WORD1 } } } ROW_T Per rapprsentare il CSV nel compito 2 uso una MAP che ad ogni parola associa la una riga, che è a sua volta una MAP che associa ad ogni parola la rispettiva frequenza CSV = { WORD1: (ROW_T) { WORD2: FREQUENCY // La frequenza di WORD2 dopo WORD1 } } GESTIONE ERRORI La maggior parte degli errori nelle funzioni interne vengono propagati (ritornando -1 o NULL). Il programma imposta all'inizio errno = 0 per fare in modo che se l'errore è generato per motivi di sistema, venga stampato tramite perror(), altrimenti si tratta di un errore della funzione, e stampo il messaggio generico. Quando non è possibile propagare un errore, viene usato `panic()` che stampa l'errore, eventualmente con `perror()` se necessario ed esegue exit(EXIT_FAILURE); PARSER Alcune funzioni dell'API di C hanno uno `stato interno` che persiste (grazie a `static`) nelle varie chiamate. Ad esempio `strtok()` o `wctomb()`. Per il parser, ho deciso di prendere in input un carattere alla volta, e di generare 0, 1 o 2 TOKEN in base allo stato interno (il TOKEN attuale). - ad esempio, se lo stato interno è "cia" e leggo 'o', lo stato interno diventerà "ciao" e genero 0 token; se poi leggo ' ', genero il token "ciao" e lo stato interno diventa "" Questa decisione è comoda perché permette di lavorare con i wchar_t solo all'inizio (per vedere se il carattere è effettivamente una lettera o meno), per poi convertirli uno ad uno in una stringa di 1 o più byte (in base alla codifica UTF-8) Di fatto, il programma lavora con char, tranne il parser e una piccolissima parte del generatore di testo (serve per redere le lettere accentate maiuscole). ALTRO Uso un formattatore automatico per formattare il codice. Di fatto, ho un file .clang-format per specificare al formattatore alcune regole su come formattare. Ho deciso anche di scrivere una pagina MAN e aggiungere MAKE INSTALL e MAKE UNINSTALL perchè per una volta ho voluto provare a scrivere un comando relativamente "completo" Durante la scrittura il codice è stato compilato con `-ansi` e `-Wpedantic` per rispettare il più possibile lo standard ANSI C. Per questo ci sono alcune cose strane come - tutti i commenti sono della forma /* */ - tutte le variabili sono dichiarate all'inizio del blocco * - FILE *STREAM - is the input stream (stdin, a file, a pipe, a socket etc...) * - int (*COLLECT)(void *, char *) - is takes COLLECTION and a NULL terminated string; * - this API let's the user do anything push to an array, insert in a set, write to a pipe or a file etc... * - it could generate -1 (ERROR), case in which SPLIT propagates it * - void *COLLECTION - the collection in which to collect the TOKENS * - it could be an array, a set, a file, a pipe etc... * - void *COLLECTION - collection from which to read TOKENS * - char *(*CONSUME)(void *) - generates token from collection * - when CONSUME returns NULL it means there aren't any tokens left * - it is very versatile, as it could read tokens from an array, or read them from a FILE
About
Generates aleatory text based on input data (Operating Systems 2 project)
Topics
Resources
License
Stars
Watchers
Forks
Releases
No releases published
Packages 0
No packages published