Ejercicio 3 | Ocurrencias

Ejercicio 3 | Ocurrencias

de Mauricio Costanzo Silveira -
Número de respuestas: 13

Hola ¿cómo están?, 

En general tengo algunas dudas sobre las funciones nativas de c/c++ que se permiten usar en el curso. Asumiendo que no se permiten utilizar casi ninguna, para resolver el ejercicio realice lo siguiente. 


#include <stdio.h>

int ocurrencias(char frase[100], int largo, char letra[1])
{
    int ocurrencias = 0;

    for (int i = 0; i < largo; i++)
    {
        if (frase[i] == *letra)
        {
            ocurrencias++;
        }
    }

    return ocurrencias;
}

int largoDeCadena(char frase[100])
{
    int largo = 0;
    char *cptr = &frase[largo];

    while (*cptr != false)
    {
        largo = largo + 1;
        cptr = &frase[largo]; // puntero a la letra de esa posición
    }

    return largo - 1; // Le resto el espacio en blanco el cual es generado por el lenguaje de manera automática
}

int main()
{
    char frase[100];
    char letra[1];

    printf("Ingresa la frase: ");

    fgets(frase, 100, stdin);

    printf("Ingresa la letra a buscar: ");

    scanf("%c", letra);

    int cantidad_ocurrencias = ocurrencias(frase, largoDeCadena(frase), letra);

    printf("La cantidad de caracteres (de '%s') es: %d \n", frase, largoDeCadena(frase));
    printf("La letra '%c' aparece : %d veces", *letra, cantidad_ocurrencias);

    return 0;
}


Mi duda es la siguiente. Para poder leer de la entrada por teclado la frase (pudiendo tener varias palabras) tuve que usar  fgets() de lo contrario se complicaba con scanf() para leer caracteres en blanco (quizás el usuario quiera saber los espacios en blanco). 


¿Cuáles funciones nativas se permiten usar para resolver los ejercicios? ¿Hay algún listado? 


Por otro lado, ¿Me podrán dar un poco de feedback sobre el código de arriba para optimizarlo un poco más?


Saludos,

Mauricio.

En respuesta a Mauricio Costanzo Silveira

Re: Ejercicio 3 | Ocurrencias

de Ignacio Facello Marcotte -
Dos comentarios rápidos:

* `letra` lo tenés como un array de `char` de tamaño uno, pero al final siempre lo estás usando como un puntero a un `char`. Queda un poco raro de leer. Capaz que lo que querés ahí es que `letra` sea un `char` directamente, en lugar de un puntero (y sí, al final un array de tamaño uno y un puntero son esencialmente lo mismo en C, pero está el tema de que sea clara la intención del código)
* El método `largoDeCadena` lo podrías sacar y en lugar de hacer el `for` con el largo del string lo podés hacer verificando si llegaste al final. De esa manera recorrés el string sólo una vez en lugar de dos veces, y de yapa te queda mucho más corto el código.
En respuesta a Ignacio Facello Marcotte

Re: Ejercicio 3 | Ocurrencias

de Mauricio Costanzo Silveira -
Ignacio ¿cómo estás?

Antes que anda gracias por la pronta respuesta.

1 - Con respecto a 'letra', me encantó la sugerencia, la voy a implementar.

2 - En el método 'largoDeCadena' lo veo necesario, ya que se necesita pasar como parámetro el largo de la frase al método 'ocurrencias'. Si eso no fuera necesario ahí si veo que no tendría sentido. Creo que no hay maneras de evitar recorrer 2 veces la frase. Porque es necesario antes de ver las ocurrencias cuál es el largo de la palabra (lo cual es totalmente innecesario). Quizás estoy omitiendo algo ¿Te parece bien el razonamiento?

Saludos,
Mauricio.
En respuesta a Mauricio Costanzo Silveira

Re: Ejercicio 3 | Ocurrencias

de Fernando Fernandez -
Hola.
Lo más importante es que la solución es conceptualmente correcta.
Con respecto al lenguaje, es C con mínimas extensiones de C++. Nos basamos en lo que está en Introducción a C* .
En este curso no vamos a poner mucho énfasis en detalles del lenguaje, en particular en lo que tiene que ver con entrada/salida.
De todas maneras se podría usar todo lo que esté en la biblioteca estándar de C, aunque lo que se va a necesitar es mínimo.

Lo que es importante es que no vamos a usar lo que se denomina aritmética de punteros. En concreto, los elementos de un arreglo se acceden con el operador de índice, como en Programación 1.  Por ejemplo en tu código, como letra es un arreglo de un elemento usamos letra[0], en vez de *letra.

Relacionado con letra, y en el único detalle conceptual a revisar de tu solución, es que no hace falta que letra sea un arreglo, alcanza con que sea de tipo char. 

Saludos,
En respuesta a Fernando Fernandez

Re: Ejercicio 3 | Ocurrencias

de Nicolas Brignoni Dardano -

Buenas, haciendo este ejercicio me surgieron algunas dudas. En primer lugar yo encare este ejercicio al mejor estilo programación 1, leyendo carácter por carácter y usando un WHILE con centinela(punto). Lo que sucede en que tengo entendido que usando:

char frase[100];   scanf("%s", frase);

Se puede leer todo el nombre de una sola vez. Esto parece mas rápido pero no obtengo el largo de la cadena que me sirve para recorrer el arreglo con un FOR que cuente las ocurrencias. Me gustaría saber si hay alguna forma de leer el nombre "de un saque" y extraer su largo luego, tal vez existe algo en C/C++ que desconozco (algo que se pueda usar en el curso de P2).

Por otro lado cuando leo el nombre ya sea carácter por carácter y luego escribiéndolo de la misma manera, o leyendo el nombre con scanf("%s", frase) y escribiéndolo con printf("%s", frase) obtengo el mismo resultado. Parece ser un primer carácter basura y luego si el nombre pero faltándole la ultima letra. Dejo captura de este ultimo problema.

Ademas en un momento leo dos caracteres seguido cuando en realidad solo preciso la letra a buscar. Esto es así por que la primera letra supongo que se come el salto de linea o algo del estilo, en varios ejercicios tuve problemas con eso y no se arreglarlo de otro forma.

Dejo código de programa.



En respuesta a Nicolas Brignoni Dardano

Re: Ejercicio 3 | Ocurrencias

de Federico Andrade -
Hola Nicolás,
La recomendación es hacerlo con scanf.
Para saber el largo te recomiendo usar la función strlen ().
Para poder usarla tenés que incluir la bibliotega string.h
Saludos
En respuesta a Mauricio Costanzo Silveira

Re: Ejercicio 3 | Ocurrencias

de Bruno Capote Hernández -
Entiendo la forma de ir avanzando en la palabra utilizando un puntero, no comprendo del todo la condición del while (*cptr != false)
cuando el puntero apunta a una casilla vacía es interpretada como falsa?.

edit: Es válido usar la función Sizeof para obtener fácilmente el largo?


En respuesta a Bruno Capote Hernández

Re: Ejercicio 3 | Ocurrencias

de Ignacio Facello Marcotte -
En C, false es 0. Decir que algo es igual a false es igual a decir que es igual a cero

Por otro lado, en C los strings se representan como secuencias de char terminadas en el chat de valor 0. Entonces lo que hace es recorrer hasta que encuentra un cero.
En respuesta a Ignacio Facello Marcotte

Re: Ejercicio 3 | Ocurrencias

de Mauricio Costanzo Silveira -
Ignacio ¿cómo estás?

En realidad lo que intente hacer es ver sí la posición x del array de chars (string) tenía un puntero con referencia a un char. Cuando eso no pasa es cuando me puedo dar cuenta que el array ya no tiene más elementos.

¿Qué te parece?

Saludos,
Mauricio.
En respuesta a Mauricio Costanzo Silveira

Re: Ejercicio 3 | Ocurrencias

de Ignacio Facello Marcotte -
No es una mala idea, en principio, pero no funciona exactamente así.
Vos lo que estás comparando con false no es el puntero, sino el valor al que apunta el puntero. Es un poco confuso pero:

int *puntero; // un puntero a int, es decir, *puntero es un int
int variable; // una variable
puntero = &variable; // & te da la dirección de memoria de variable, puntero ahora apunta a variable
variable = *puntero; // Asignar a variable el valor al que apunta puntero
(puntero == 0) // Esto es true si puntero apunta a la dirección de memoria nula
(*puntero == 0) // Esto es true si puntero apunta a una dirección de memoria en la que hay un 0 guardado (por ejemplo, en esta serie de líneas, si variable == 0)

Lo que estás haciendo funciona porque vos estás comparando *cptr (es decir, el valor al que apunta cptr) con false (que es igual a cero). Y los strings en C son terminados en cero. El string "hola" internamente se va a guardar como ['h', 'o', 'l', 'a', '\0']. Entonces vos estás recorriendo, y cptr va apuntando a distintos chars, primero 'h', después, 'o', etc, hasta que apunta a '\0', ahí ve que apunta a un 0 ('\0' es un char con valor 0, _no_ es lo mismo que '0'), y dice "se me acabó el string, chau".
En respuesta a Ignacio Facello Marcotte

Re: Ejercicio 3 | Ocurrencias

de Mauricio Costanzo Silveira -
Excelente respuesta Ignacio.

Mil gracias por la aclaración.

Solo para aclarar, para hacer lo que quiero solo debería cambiar:

 while (*cptr != false)  por  while (cptr != false)

y ahí sí estaría comparando la nulidad del puntero. 

¿Estaría bien?

Saludos,
Mauricio.
En respuesta a Mauricio Costanzo Silveira

Re: Ejercicio 3 | Ocurrencias

de Ignacio Facello Marcotte -
Sí, en cuanto a que es lo que estarías haciendo, pero ojo que no va a funcionar como vos esperás. Vos nunca estás asignándole el valor nulo al puntero, siempre estás asignando &frase[largo]. Eso siempre es una dirección de memoria. No es 0.

Es decir, podés chequear si el puntero es nulo. Pero eso sólo te va a decir que está apuntando a nada (a la dirección cero técnicamente; no importa). No te va a decir nada sobre a qué datos está apuntando.
En respuesta a Bruno Capote Hernández

Re: Ejercicio 3 | Ocurrencias

de Mauricio Costanzo Silveira -
Bruno ¿cómo estás?

Hasta donde yo tengo entendido (y esto tomalo con pinzas) decir que un puntero es igual a false significa que no apunta a ningún espacio en memoria. Sería lo equivalente a nil en Pascal. Por eso me puedo dar cuenta el largo del array haciendo referencia a sí éxiste el puntero de la posición [x] del array de char

Aquí tenés info -> https://www.delftstack.com/es/howto/cpp/cpp-check-if-pointer-is-null/

Saludos,
Mauricio.