[C] Problema produzione di una statistica

fabiett1
Il testo dell'esercizio è il seguente:

Si scriva un programma che acquisisca utilizzando la funzione gets una stringa composta da un massimo di 5 parole separate da spazi, per un totale di massimo 60 caratteri.
Il programma deve:
a. Stabilire quante sono le parole contenute effettivamente nella stringa;
b. Calcolare la media della lunghezza delle parole;
c. Produrre una statistica sulla lunghezza delle parole;
Esempio:
Se la stringa inserita è “questa stringa contiene cinque parole” allora visualizzerà a video:
- La stringa contiene 5 parole
- La lunghezza media delle parole è 6,6 caratteri
- La stringa contiene:
o 3 parole da 6 caratteri
o 1 parola da 7 caratteri
o 1 parola da 8 caratteri

I punti (a) e (b) sono riuscito a risolversi, tuttavia sto trovando alcune difficiltà a risolvere il punto (c), in quanto il mio algoritmo pare non funzionare. Potete darmi una mano, perfavore? :)

Di seguito trovate il mio codice...
#include <stdio.h>
#include <stdlib.h>
#define SIZE 60
#include <string.h>
#include <ctype.h>

int conteggioParole(char* s, int dim);
float calcoloMedia(char* s, int dim, int n_parole);


int conteggioParole(char* s, int dim)
{
    int i, parole=0;

    for(i=0; i<=dim; i++)
    {
        if((s[i]==' ' && s[i-1]!=' ') || (s[i]=='\0' && s[i-1]!=' '))
            parole++;
    }

    return parole;
}


float calcoloMedia(char* s, int dim, int n_parole)
{
    int i, n_caratteri=0;
    float media=0;

    for(i=0; i<dim; i++)
    {
        if(isalnum(s[i])!=0)
            n_caratteri++;
    }
    if(n_caratteri>60)
        return -1;

    media=(float)n_caratteri/n_parole;

    return media;
}


int main()
{
    char s[SIZE];
    int lunghezza, n_parole,i,n_caratteri,p[SIZE]={0},y;

    printf("Inserire una stringa di caratteri:\n");
    gets(s);

    lunghezza=strlen(s);

    n_parole=conteggioParole(s,lunghezza);
    if(n_parole>5)
        printf("Errore: sono state inserite piu' di 5 parole");
    else
        {
            printf("Nel testo sono presenti %d parole\n", n_parole);
            if(calcoloMedia(s,lunghezza,n_parole)==-1)
                printf("Errore: sono stati inseriti piu' di 60 caratteri");
            else
                printf("La lunghezza media delle parole e': %f\n",calcoloMedia(s,lunghezza,n_parole));
        }

    //STATISTICA
    printf("La stringa contiene:\n");
    for(i=0;s[i]!='\0';i++)
    {
        if(isalnum(s[i])!=0)
            n_caratteri++;
        else
        {
            y=n_caratteri;
            p[y]++;
            n_caratteri=0;
        }
    }

    printf("La stringa contiene:\n");
    for(i=0;i<SIZE;i++)
    {
       if(p[i]!=0)
            printf(" %d parole da %d caratteri\n",i,p[i]);
    }

    return 0;
}

Risposte
Raptorista1
Cosa fa che non dovrebbe fare o cosa non fa che dovrebbe fare? Qual è l'output di quel codice?

fabiett1
Il programma deve contare il numero di parole che sono presenti nella frase, fare la media dei caratteri che ciascuna parola possiede e, infine, produrre una statistica sulla base dell'esempio nel testo dell'esercizio.
I primi due punti sono riuscito a farli, però non riesco a produrre una statistica del tipo:

La stringa contiene:
o 3 parole da 6 caratteri
o 1 parola da 7 caratteri
o 1 parola da 8 caratteri

Raptorista1
Bene, adesso che ne diresti di rispondere alla mia domanda invece?

fabiett1
i valori non sono corretti

Super Squirrel
Premesso che conosco pochissimo il C, mi sembra che il codice presenti ampi margini di miglioramento sia dal punto di vista della leggibilità che dell'efficienza.

Inoltre se la stringa inizia con uno spazio, la funzione "conteggioParole" ritorna una parola in più. Mi sembra anche strano che il programma non crashi visto che in questo caso si tenta di accedere a s[-1].

Per quanto riguarda la "statistica", proprio non riesco a trovarci nessuna logica in quella parte di codice:
- nell'array "p" immagino dovrebbe essere conservato il numero di caratteri della singola parola. Se così fosse la sua dimensione dovrebbe essere 5 e non SIZE=60.
- al di là della logica che sta alla base dell'algoritmo la variabile "n_caratteri" va inizializzata visto che altrimenti assume un valore casuale.
- se l'intuizione sull'utilizzo di "p" è giusta, un modo per impostare il problema potrebbe essere il seguente:

//STATISTICA
    int n_caratteri = 0; 
    int p[5] = {0};
    int y = 0;
    for(i = 0; s[i] != '\0'; i++)
    {
        if(isalnum(s[i]) != 0)
        {
            ++n_caratteri;
        }
        else
        {
            p[y] = n_caratteri;
            ++y;
            n_caratteri = 0;
        }
    }


non conosco la funzione nella condizione dell'if, ma supponiamo che quella parte di codice faccia il suo dovere. Al termine del ciclo for quindi avremo nei primi y elementi dell'array p il numero di caratteri delle y parole. Quindi confrontando i vari elementi di p si può facilmente ottenere la "statistica" ricercata.

Non so se conosci il C++, in ogni caso ho provato a svolgere l'esercizio, te lo posto, magari ti può essere di aiuto.

#include <algorithm>
#include <iostream>
#include <vector>
#include <string>

void analizza_stringa(const std::string& stringa, std::vector <unsigned int>& parola)
{
    unsigned int n;
    for(unsigned int i = 0; i < stringa.size(); i++)
    {
        if(stringa[i] != ' ')
        {
            n = 0;
            do
            {
                ++n;
                ++i;
            }
            while(i < stringa.size() && stringa[i] != ' ');
            parola.push_back(n);
        }
    }
}

int main()
{
    std::string stringa;
    std::cout << "INSERIRE STRINGA (MASSIMO 60 CARATTERI E 5 PAROLE):" << std::endl;
    std::getline(std::cin, stringa);
    if(stringa.size() > 60)
    {
        std::cout << "ERRORE: SONO STATI INSERITI PIU' DI 60 CARATTERI";
    }
    else
    {
        std::vector <unsigned int> parola;
        analizza_stringa(stringa, parola);
        if(parola.size() > 5)
        {
            std::cout << "ERRORE: SONO STATE INSERITE PIU' DI 5 PAROLE";
        }
        else
        {
            unsigned int n;
            unsigned int caratteri = 0;
            std::sort(parola.begin(), parola.end());
            std::cout << "LA STRINGA CONTIENE " << parola.size() << " PAROLE:" << std::endl;
            for(unsigned int i = 0; i < parola.size(); i++)
            {
                n = 1;
                for(unsigned int i_1 = i + 1; i_1 < parola.size(); i_1++)
                {
                    if(parola[i_1] == parola[i])
                    {
                        ++n;
                        ++i;
                    }
                    else
                    {
                        break;
                    }
                }
                caratteri += n * parola[i];
                std::cout << "- " << n << " PAROLE DA " << parola[i] << " CARATTERI" << std::endl;
            }
            std::cout << "LA LUNGHEZZA MEDIA DELLE PAROLE E' DI " << (float) caratteri / parola.size() << " CARATTERI" <<std::endl;
        }
    }
}

fabiett1
Ho modificato la funzione che conta le parole e ora funziona.
mentre per quanto riguarda la statistica la mie idea era: creo un vettore di interi dove l'indice corrisponde al numeri di caratteri delle varie parole, e il contenuto del vettore in quella posizione contiene il numero di volte che quel numero di caratteri è presente nella stringa.

PS:
Osservando attentamente i risultati ho constatato che la mia idea funziona, ma non del tutto in quanto nella statistica non viene contata l'ultima parole della stringa

Super Squirrel
Ho modificato il codice che avevo postato utilizzando la tua idea. Te lo posto, magari ti può essere di aiuto per capire perchè ti trovi con una parola in meno. Inoltre puoi notare che basta scorrere una volta gli elementi della stringa.

#include <iostream>
#include <string>

void analizza_stringa(const std::string& stringa, unsigned int& parola, unsigned int* v)
{
    unsigned int n;
    for(unsigned int i = 0; i < stringa.size(); i++)
    {
        if(stringa[i] != ' ')
        {
            n = 0;
            do
            {
                ++n;
                ++i;
            }
            while(i < stringa.size() && stringa[i] != ' ');
            ++parola;
            ++v[n - 1];
        }
    }
}

int main()
{
    std::string stringa;
    std::cout << "INSERIRE STRINGA (MASSIMO 60 CARATTERI E 5 PAROLE):" << std::endl;
    std::getline(std::cin, stringa);
    if(stringa.size() > 60)
    {
        std::cout << "ERRORE: SONO STATI INSERITI PIU' DI 60 CARATTERI";
    }
    else
    {
        unsigned int parola = 0;
        unsigned int v[60] = {0};
        analizza_stringa(stringa, parola, v);
        if(parola > 5)
        {
            std::cout << "ERRORE: SONO STATE INSERITE PIU' DI 5 PAROLE";
        }
        else
        {
            unsigned int caratteri = 0;
            std::cout << "LA STRINGA CONTIENE " << parola << " PAROLE:" << std::endl;
            for(unsigned int i = 0; i < 60; i++)
            {
                if(v[i] != 0)
                {
                    std::cout << "- " << v[i] << " PAROLE DA " << i + 1 << " CARATTERI" << std::endl;
                    caratteri += (i + 1) * v[i];
                }
            }
            std::cout << "LA LUNGHEZZA MEDIA DELLE PAROLE E' DI " << (float) caratteri / parola << " CARATTERI" <<std::endl;
        }
    }
}

fabiett1
Chiedo scusa ma non conosco il linguaggio C++ e, pertanto, mi risulta difficile capire dov'è l'errore che compromette la buona riuscita del mio programma. :cry:
Per caso qualcuno può aiutarmi? Ho sempre il problema dell'ultima parola della stringa omessa nella stampa della statistica.

Super Squirrel
ho provato a scrivere il codice che ho postato in C, ma non conoscendo bene il linguaggio è probabile ci sia qualche errore.
In ogni caso sembra funzionare.

#include <stdio.h>
#include <string.h>

void analizza_stringa(const char* stringa, const int dim, int* parole, int* v)
{
    int n;
    for(int i = 0; i < dim; i++)
    {
        if(stringa[i] != ' ')
        {
            n = 0;
            do
            {
                ++n;
                ++i;
            }
            while(i < dim && stringa[i] != ' ');
            ++*parole;
            ++v[n - 1];
        }
    }
}

int main()
{
    char stringa[100];
    int dim;
    printf("INSERIRE STRINGA (MASSIMO 60 CARATTERI E 5 PAROLE):\n");
    gets(stringa);
    dim = strlen(stringa);
    if(dim > 60)
    {
        printf("ERRORE: SONO STATI INSERITI PIU' DI 60 CARATTERI");
    }
    else
    {
        int parole = 0;
        int v[60] = {0};
        int* punt = &parole;
        analizza_stringa(stringa, dim, punt, v);
        if(parole > 5)
        {
            printf("ERRORE: SONO STATE INSERITE PIU' DI 5 PAROLE");
        }
        else
        {
            int caratteri_validi = 0;
            float media;
            printf("LA STRINGA CONTIENE ");
            printf("%d", parole);
            printf(" PAROLE:");
            for(int i = 0; i < 60; i++)
            {
                if(v[i] != 0)
                {
                    printf("\n- ");
                    printf("%d ", v[i]);
                    printf(" PAROLE DA ");
                    printf("%d ", i + 1);
                    printf(" CARATTERI");
                    caratteri_validi += (i + 1) * v[i];
                }
            }
            media = (float) caratteri_validi / parole;
            printf("\nLA LUNGHEZZA MEDIA DELLE PAROLE E' DI ");
            printf("%.3f", media);
            printf(" CARATTERI");
        }
    }
}

fabiett1
Ti ringrazio molto per la pazienza e la grande disponibilità, forse dovuta anche alla passione.
Se può interessarti alla fine sono riuscito a trovare il problema: la statistica non mi contava le ultime lettere della parole probabilmente perché una volta che il ciclo for raggiungeva il terminatore di stringa, i caratteri contati non venivano copiati nella rispettiva locazione del vettore. Ho risolto contando la lunghezza della stringa con la funzione di libreria strlen() e ho impostato la fine del ciclo una volta che il conteggio arriva a i=lenght (ultimo carattere della stringa).

Sempre se può interessarti posto il codice di seguito.
Ciao e grazie ancora! :D
//STATISTICA
            lenght=strlen(s);
            for(i=0;i<=lenght;i++)
            {
                if(isalnum(s[i])!=0)
                    n_caratteri++;
                else
                {
                    y=n_caratteri;
                    p[y]++;
                    n_caratteri=0;
                    y=0;
                }
            }

vict85
"fabiett":
Si scriva un programma che acquisisca utilizzando la funzione gets una stringa composta da un massimo di 5 parole separate da spazi, per un totale di massimo 60 caratteri.


La funzione [inline]gets[/inline] era una funzione standard del C, ma non lo è più da ben 6 anni. Prima di essere stata tolta era stata deprecata e lo è stata per circa 12 anni. E immagino che fosse sconsigliata ben prima di essere deprecata ufficialmente (direi che saranno 30 anni che questa funzione viene sconsigliata e considerata poco sicura). Pertanto trovo davvero osceno di leggere ancora oggi degli esercizi che ne prevedono l'uso (considerando che non andrebbe neanche spiegata o presa in considerazione come opzione). Sostituiscila con una chiamata a [inline]fgets[/inline].

fabiett1
Posso chiedere, a puro titolo informativo, perché tale funzione è così tanto sconsigliata?
Può essere sostituita con una scanf("%[^\n]",....)

vict85
Non vedo perché dovresti usare [inline]scanf[/inline] invece di [inline]fgets[/inline]: [inline]scanf[/inline] è sia più complesso da scrivere che più lento.

In ogni caso puoi leggerti la seconda risposta in questo e questa serie di 10 post.

fabiett1
Grazie! :-o

Rispondi
Per rispondere a questa discussione devi prima effettuare il login.