Lab 5

Lab5: 継承

この Lab では1~2人のクラスメートと協力することは問題ありませんが、そのグループのすべての受講生が等しく実習に貢献することが求められます。

家族の各メンバーの血液型の継承をシミュレートします。

$ ./inheritance
Generation 0, blood type OO
    Generation 1, blood type AO
        Generation 2, blood type OA
        Generation 2, blood type BO
    Generation 1, blood type OB
        Generation 2, blood type AO
        Generation 2, blood type BO

〆切

2021年12月31日金曜日11:59 PM (東部標準時) までに提出してください。

背景

人の血液型は2つの対立遺伝子 (異なる形態の遺伝子) によって決まります。可能性のある3つの対立遺伝子はA、B、Oであり、それぞれの人は2つ (同じかもしれないし、違うかもしれません) をもちます。子供の両親はそれぞれ、2つの血液型対立遺伝子の1つを子供にランダムに渡します。したがって、考えられる血液型の組み合わせは、OO、OA、OB、AO、AA、AB、BO、BA、およびBBです。

たとえば、片方の親の血液型がAOで、もう一方の親の血液型がBBである場合、それぞれの親からどの対立遺伝子を受け取るかによって、子の血液型はABまたはOBになります。同様に、片方の親の血液型がAOで、もう一方がOBである場合、子の血液型はAO、OB、AB、およびOOになります。

始め方

IDE に lab 5 という新しいディレクトリを作成します。このディレクトリで wget https://cdn.cs50.net/2020/fall/labs/5/inheritance.c を実行し、このプロジェクトの配布コードをダウンロードします。

理解を深める

inheritance.cの配布コードを見てください。

person という型の定義に注目してください。各 person は2つの親の配列を持ち、それぞれが別の person 構造体へのポインタになります。それぞれの人は2つの対立遺伝子の配列をもち、それぞれがchar ('A''B'、または'O') で表されます。

では、main関数を見てみましょう。この関数は、乱数生成器を「シード」する (すなわち、いくつかの初期入力を提供する) することから始まります。この乱数生成器を使って、あとでランダムな対立遺伝子を生成します。次に、main 関数は create_family 関数を呼び出して、3世代 (すなわち、本人とその両親、祖父母) の家系に対する person 構造体の作成をシミュレートします。次に print_family を呼び出して、それぞれの家族とその血液型を出力します。最後に、この関数は free_family を呼び出して、malloc で割り当てられていたメモリを解放します。

create_family 関数と free_family 関数の作成があなたの課題です。

実装の詳細

inheritance.c の実装を完了して、特定の世代サイズの家系を作成し、各家系のメンバーに血液型対立遺伝子を割り当てます。最も古い世代には、ランダムに対立遺伝子が割り当てられます。

  • create_family 関数は整数 (世代) を入力として受け取り、 (mallocを介して) その世代数の家族の各メンバーに対して1人を割り当て、最も若い世代の人へのポインタを返します。
    • 例えば、create_family(3) は両親が2人いる人へのポインタを返さなければなりません。
    • 各個人には対立遺伝子が割り当てられていなければなりません。最も古い世代はランダムに ( random_allele 関数を呼び出すなどして) 対立遺伝子をもち、若い世代はそれぞれの親から (ランダムに) 1つの対立遺伝子を受け継ぐべきです。
    • 各自に親を割り当てます。最も古い世代では、両方の親を NULL に設定し、より若い世代では、それぞれが異なる親を指す2つのポインタの配列にする必要があります。

create_family 関数をいくつかのTODOに分割して完成させましょう。

  • まず、新しいユーザーにメモリを割り当てる必要があります。malloc を使用してメモリーを割り当て、sizeof(person) を使用して割り当てるバイト数を取得できることを思い出してください。
  • 次に、世代数が1より大きいか (generations > 1) どうかをチェックする条件を含めます。
    • 世代数が1を超える場合は、さらに多くの世代を割り当てる必要があります。関数で create_family を再帰的に呼び出して、両方の親を設定する必要があります (それぞれの親には、入力として何世代を渡す必要がありますか?)。 この関数は、各親から1つの対立遺伝子をランダムに選択することにより、両方の対立遺伝子を設定します。
    • そうでない場合 ( generations==1 の場合) 、この個人の親データはありません。両親は両方とも NULL に設定し、それぞれの対立遺伝子はランダムに生成します。
  • 最後に、関数は割り当てられた人のポインタを返します。

free_family 関数は、人へのポインタを入力として受け付け、その人のメモリーを解放してから、その人のすべての祖先のメモリーを再帰的に解放します。

  • これは再帰関数なので、最初にベースケースを処理する必要があります。関数への入力が NULL の場合、解放するものがないため、関数はすぐに戻ることができます。
  • そうでない場合は、子を解放する前に、その人の両方の親を再帰的に解放する必要があります。

ウォークスルー

ヒント

  • rand() 関数は対立遺伝子をランダムに割り当てるのに有用です。この関数は、0からRAND_MAX の整数、つまり32767を戻します。
    • 特に、0または1の擬似乱数を生成するには、式 rand() % 2を使用します。
  • 特定の人にメモリを割り当てるには、malloc(n) を使うことができます。この関数は引数としてサイズを取り、nバイトのメモリを割り当てます。
  • ポインタを介して変数にアクセスするには、矢印表記を使用できます。
    • たとえば、p が個人へのポインタである場合、この個人の最初の親へのポインタは p->parents[0]. によってアクセスできます。

コードのテスト方法

./inheritance の実行時、プログラムは前段で説明されているルールに従う必要があります。子供は両親から1つずつ、2つの対立遺伝子をもつべきです。両親はそれぞれの両親から1つずつ、2つの対立遺伝子をもつべきです。

例えば、以下の例では、0世代の子供は世代1の両親からO対立遺伝子を受け取りました。世代1の片親は片側の祖父母からAを、もう片側の祖父母からOを受け取りました。同様に、もう片側の親は祖父母からOとBを受け取りました。

$ ./inheritance
Generation 0, blood type OO
    Generation 1, blood type AO
        Generation 2, blood type OA
        Generation 2, blood type BO
    Generation 1, blood type OB
        Generation 2, blood type AO
        Generation 2, blood type BO

解決方法がわかりませんか?

check50を使用して以下を実行し、コードの正確さを評価してください。ただし、コンパイルとテストは必ず自分で行ってください。

check50 cs50/labs/2021/x/inheritance

以下を実行し、style50 を使用してコードのスタイルを評価します。

style50 inheritance.c

提出方法

次のコマンドを実行し、GitHub のユーザー名とパスワードを入力してログインします。セキュリティのため、パスワードには実際の文字ではなくアスタリスク (*) が表示されます。

submit50 cs50/labs/2021/x/inheritance