Herencia

Problema a resolver

El tipo de sangre de una persona est谩 determinado por dos alelos (es decir, diferentes formas de un gen). Los tres posibles alelos son A, B y O, de los cuales cada persona tiene dos (posiblemente el mismo, posiblemente diferente). Cada uno de los padres de un ni帽o transmite aleatoriamente uno de sus dos alelos de tipo sangu铆neo a su hijo. Las posibles combinaciones de tipos sangu铆neos, entonces, son: OO, OA, OB, AO, AA, AB, BO, BA y BB.

Por ejemplo, si un padre tiene el tipo de sangre AO y el otro padre tiene el tipo de sangre BB, entonces los posibles tipos de sangre del hijo ser铆an AB y OB, dependiendo de qu茅 alelo se reciba de cada padre. De manera similar, si un padre tiene el tipo de sangre AO y el otro OB, entonces los posibles tipos de sangre del hijo ser铆an AO, OB, AB y OO.

En un archivo llamado inheritance.c en una carpeta llamada inheritance, simule la herencia de los tipos sangu铆neos para cada miembro de una familia.

Demostraci贸n

C贸digo de distribuci贸n

Para este problema, ampliar谩s la funcionalidad del c贸digo proporcionado por el personal de CS50.

Inicia sesi贸n en cs50.dev, haz clic en tu ventana de terminal y ejecuta cd por s铆 solo. Deber铆as encontrar que el mensaje de tu ventana de terminal se asemeja a lo siguiente:

  $

A continuaci贸n, ejecuta

  wget https://cdn.cs50.net/2023/fall/psets/5/inheritance.zip

para descargar un ZIP llamado inheritance.zip en tu espacio de c贸digos.

Luego ejecuta

  unzip inheritance.zip

para crear una carpeta llamada inheritance. Ya no necesitas el archivo ZIP, por lo que puedes ejecutar

  rm inheritance.zip

y responder con "y" seguido de Enter en el mensaje para eliminar el archivo ZIP que descargaste.

Ahora escribe

  cd inheritance

seguido de Enter para moverte a (es decir, abrir) ese directorio. Tu mensaje ahora deber铆a parecerse al siguiente.

  inheritance/ $

Ejecuta ls por s铆 solo, y deber铆as ver un archivo llamado inheritance.c.

Si tienes alg煤n problema, sigue estos mismos pasos de nuevo y mira si puedes determinar d贸nde te equivocaste.

Detalles de implementaci贸n

Completa la implementaci贸n de inheritance.c, de manera que cree una familia de un tama帽o de generaci贸n especificado y asigne alelos de tipo sangu铆neo a cada miembro de la familia. La generaci贸n m谩s antigua tendr谩 alelos asignados aleatoriamente.

  • La funci贸n create_family toma un entero (generations) como entrada y debe asignar (como mediante malloc) una persona para cada miembro de la familia de ese n煤mero de generaciones, devolviendo un puntero a la persona en la generaci贸n m谩s joven.
    • Por ejemplo, create_family(3) debe devolver un puntero a una persona con dos padres, donde cada padre tambi茅n tiene dos padres.
    • A cada persona se le deben asignar alelos. A la generaci贸n m谩s antigua se le deben asignar alelos elegidos aleatoriamente (como llamando a la funci贸n random_allele), y las generaciones m谩s j贸venes deben heredar un alelo (elegido aleatoriamente) de cada padre.
    • A cada persona se le deben asignar padres. La generaci贸n m谩s antigua debe tener ambos padres establecidos en NULL, y las generaciones m谩s j贸venes deben tener padres como una matriz de dos punteros, cada uno apuntando a una estructura persona diferente.

Sugerencias

Comprende el c贸digo en inheritance.c

Echa un vistazo al c贸digo de distribuci贸n en inheritance.c.

Observa la definici贸n de un tipo llamado persona. Cada persona tiene una matriz de dos padres, cada uno de los cuales es un puntero a otra estructura persona. Cada persona tambi茅n tiene una matriz de dos alelos, cada uno de los cuales es un char (ya sea 'A', 'B' o 'O').

  // Cada persona tiene dos padres y dos alelos
  typedef struct person
  {
      struct person *parents[2];
      char alleles[2];
  }
  person;

Ahora, echa un vistazo a la funci贸n main. La funci贸n comienza "sembrando" (es decir, proporcionando alguna entrada inicial a) un generador de n煤meros aleatorios, que usaremos m谩s adelante para generar alelos aleatorios.

  // Sembrar generador de n煤meros aleatorios
  srand(time(0));

La funci贸n main luego llama a la funci贸n create_family para simular la creaci贸n de estructuras persona para una familia de 3 generaciones (es decir, una persona, sus padres y sus abuelos).

  // Crear una nueva familia con tres generaciones
  person *p = create_family(GENERATIONS);

Luego llamamos a print_family para imprimir cada uno de esos miembros de la familia y sus tipos de sangre.

  // Imprimir 谩rbol geneal贸gico de tipos sangu铆neos
  print_family(p, 0);

Finalmente, la funci贸n llama a free_family para liberar cualquier memoria que se haya asignado previamente con malloc.

  // Liberar memoria
  free_family(p);

隆Las funciones create_family y free_family quedan para que las escribas!

Completa la funci贸n create_family

La funci贸n create_family deber铆a devolver un puntero a una persona que haya heredado su tipo de sangre del n煤mero de generaciones dado como entrada.

  • Primero, nota que este problema plantea una buena oportunidad para la recursividad.
    • Para determinar el tipo de sangre de la persona presente, primero necesitas determinar los tipos de sangre de sus padres.
    • Para determinar los tipos de sangre de esos padres, primero debes determinar los tipos de sangre de sus padres. Y as铆 sucesivamente hasta llegar a la 煤ltima generaci贸n que deseas simular.

Para resolver este problema, encontrar谩s varios TODOs en el c贸digo de distribuci贸n.

Primero, deber铆as asignar memoria para una nueva persona. Recuerda que puedes usar malloc para asignar memoria y sizeof(person) para obtener el n煤mero de bytes a asignar.

  // Asigna memoria para una nueva persona
  person *new_person = malloc(sizeof(person));

Luego, deber铆as verificar si todav铆a quedan generaciones por crear: es decir, si generaciones > 1.

Si generaciones > 1, entonces hay m谩s generaciones que a煤n necesitan ser asignadas. Ya hemos creado dos nuevos padres, padre0 y padre1, llamando recursivamente a create_family. Entonces, tu funci贸n create_family deber铆a establecer los punteros de los padres de la nueva persona que creaste. Finalmente, asigna ambos alelos para la nueva persona eligiendo aleatoriamente un alelo de cada padre.

  • Recuerda, para acceder a una variable a trav茅s de un puntero, puedes utilizar la notaci贸n de flecha. Por ejemplo, si p es un puntero a una persona, entonces se puede acceder a un puntero al primer padre de esta persona mediante p->parents[0].
  • Puede que encuentres 煤til la funci贸n rand() para asignar alelos aleatoriamente. Esta funci贸n devuelve un entero entre 0 y RAND_MAX, o 32767. En particular, para generar un n煤mero pseudoaleatorio que sea 0 o 1, puedes utilizar la expresi贸n rand() % 2.

    // Crea dos nuevos padres para la persona actual llamando recursivamente a create_family person parent0 = create_family(generations - 1); person parent1 = create_family(generations - 1);

    // Establece los punteros de los padres para la persona actual new_person->parents[0] = parent0; new_person->parents[1] = parent1;

    // Asigna aleatoriamente los alelos de la persona actual en funci贸n de los alelos de sus padres new_person->alleles[0] = parent0->alleles[rand() % 2]; new_person->alleles[1] = parent1->alleles[rand() % 2];

Digamos que no quedan m谩s generaciones para simular. Es decir, generaciones == 1. Si es as铆, no habr谩 datos de padres para esta persona. Ambos padres de tu nueva persona deben configurarse en NULL y cada alelo debe generarse aleatoriamente.

  // Establece los punteros de los padres en NULL
  new_person->parents[0] = NULL;
  new_person->parents[1] = NULL;

  // Asigna alelos aleatoriamente
  new_person->alleles[0] = random_allele();
  new_person->alleles[1] = random_allele();

Finalmente, tu funci贸n deber铆a devolver un puntero para la persona que fue asignada.

  // Devuelve la persona reci茅n creada
  return new_person;

Completa la funci贸n free_family

La funci贸n free_family deber铆a aceptar como entrada un puntero a una persona, liberar la memoria para esa persona y luego liberar recursivamente la memoria para todos sus antepasados.

  • Como esta es una funci贸n recursiva, primero debes manejar el caso base. Si la entrada a la funci贸n es NULL, entonces no hay nada que liberar, por lo que tu funci贸n puede regresar inmediatamente.
  • De lo contrario, deber铆as liberar recursivamente a los dos padres de la persona antes de liberar al ni帽o.

La siguiente es una gran pista, 隆pero aqu铆 tienes c贸mo hacerlo!

  // Libera `p` y todos los antepasados de `p`.
  void free_family(person *p)
  {
      // Maneja el caso base
      if (p == NULL)
      {
          return;
      }

      // Libera a los padres recursivamente
      free_family(p->parents[0]);
      free_family(p->parents[1]);

      // Libera al ni帽o
      free(p);
  }

Tutorial

驴No est谩s seguro de c贸mo resolverlo?

C贸mo probar

Al ejecutar ./inheritance, tu programa deber铆a cumplir con las reglas descritas en los antecedentes. El ni帽o deber铆a tener dos alelos, uno de cada padre. Los padres deber铆an tener cada uno dos alelos, uno de cada uno de sus padres.

Por ejemplo, en el siguiente ejemplo, el ni帽o en la generaci贸n 0 recibi贸 un alelo O de ambos padres de la generaci贸n 1. El primer padre recibi贸 una A del primer abuelo y una O del segundo abuelo. De manera similar, el segundo padre recibi贸 una O y una B de sus abuelos.

  $ ./inheritance
  Ni帽o (Generaci贸n 0): tipo de sangre OO
      Padre (Generaci贸n 1): tipo de sangre AO
          Abuelo (Generaci贸n 2): tipo de sangre OA
          Abuela (Generaci贸n 2): tipo de sangre BO
      Padre (Generaci贸n 1): tipo de sangre OB
          Abuelo (Generaci贸n 2): tipo de sangre AO
          Abuela (Generaci贸n 2): tipo de sangre BO

Correcci贸n

  check50 cs50/problems/2024/x/inheritance

Estilo

  style50 inheritance.c

Enviar

  submit50 cs50/problems/2024/x/inheritance