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 mediantemalloc
) unapersona
para cada miembro de la familia de ese n煤mero de generaciones, devolviendo un puntero a lapersona
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 asignaralelos
. A la generaci贸n m谩s antigua se le deben asignar alelos elegidos aleatoriamente (como llamando a la funci贸nrandom_allele
), y las generaciones m谩s j贸venes deben heredar un alelo (elegido aleatoriamente) de cada padre. - A cada
persona
se le deben asignarpadres
. La generaci贸n m谩s antigua debe tener ambospadres
establecidos enNULL
, y las generaciones m谩s j贸venes deben tenerpadres
como una matriz de dos punteros, cada uno apuntando a una estructurapersona
diferente.
- Por ejemplo,
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 mediantep->parents[0]
. -
Puede que encuentres 煤til la funci贸n
rand()
para asignar alelos aleatoriamente. Esta funci贸n devuelve un entero entre0
yRAND_MAX
, o32767
. En particular, para generar un n煤mero pseudoaleatorio que sea0
o1
, puedes utilizar la expresi贸nrand() % 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 deliberar
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