米ハーバード大学のコンピューターサイエンス講座を受講し始めてから早いものでもう2週目。大学時代に闇雲に覚えさせられた色々な構文には様々な意味があった(当然!)ことが分かり、今回はきちんと脳に定着しそうな気がする。授業の進むスピードは比較的速く、高速「アハ体験」を連続してできている感覚がする。せっかくだから、学んだ内容とそれに対する僕の感想も添えて自分用のノートをまとめる代わりに記事を更新していこうと思う。動画の後半には、我らが任天堂ゲームの代表作、スーパーマリオブラザーズを使ってどうやってゲームは作られているのか簡単に触れており、最近のゲーム開発のプログラムがどう設計されているのか知れて面白かった。

 

CS50第2回目の講義「C言語」

C(正確性 × デザイン × スタイル)

まずは、プログラムを書く上での重要なこと3つ「正確性 × デザイン × スタイル」。大学時代と違い、かなり細かく改行や{}(括弧)の位置を指定するというのが正直な感想。ここまで統一した方が確かに気持ちが良い。

  • Correctness(正確性), or whether our code works correctly, as intended
  • Design(デザイン), or a subjective measure of how well-written our code is, based on how efficient it is and how elegant or logically readable it is, without unnecessary repetition
  • Style(スタイル), or how aesthetically formatted our code is, in terms of consistent indentation and other placement of symbols. Differences in style don’t affect the correctness or meaning of our code, but affect how readable it is visually

 

CS50 IDE(開発環境)

この講義ではクラウド上に開発環境(CS50 IDE)提供してくれている。ちなみに、IDEは integrated development environment の略。CS50 IDEにアクセスすると以下のスクリーンショットの画面が現れ、上半分にソースコードを、下半分がターミナルになっている。

 

Compiling(コンパイル)

人間が書いたプログラム(ソースコード)を、コンピューターが読める言語に変換する作業をコンパイルというが、これも簡単にコマンドラインからできるようになっている。

make hello

 

Functions and arguments(関数と引数)

Functions とは、プログラムの中で使用できる小さなアクションや動詞のこと。Arguments は、Functions に対する input (インプット)を意味する。

CS50 専用の Library(ライブラリー)も用意されていて、get_string は文字列を取得するための関数。

// ask the user for a string (or some sequence of text)
string answer = get_string("What's your name? ");

これを使った構文は次の通り。

#include <cs50.h>
#include <stdio.h>

int main(void)
{
    string answer = get_string("What's your name? ");
    printf("hello, %s", answer);
}

 

main, header files(メイン、ヘッダーファイル)

main とは、int main (void) の括弧の中身を指す。header files は、プログラムの冒頭で宣言する .h ファイルで、ライブラリーをこのプログラムで使用することを宣言している。例えば、#include <stdio.h>は、standard input/output library であり、ここにはよく使用するprintf関数が含まれている。

 

Tools(ツール、これすごい!)

プログラムを書いているとお世辞にも分かりやすいとは言えないコンパイルエラーメッセージに苦戦することがある。そのエラーメッセージが理解できず、プログラムのどこが間違っているのか特定できずに何時間も経っている、そんな経験は皆さんにもあるのではないだろうか?そこで、CS50 IDE環境では、help50というコンパイルエラーメッセージを解析して分かりやすい英語に変換してくれるツールが用意されている。これはすごい助かる気がする!

help50 make hello で結果を返してくれる。helloはコンパイルを実行したいプログラム名。

Compile_error

さらには、style50 というプログラムのスタイルをチェックしてくれるツールも用意されている!自己満で終わっていたプログラムのスタイルは、厳しく精査されることになる。これは助かる。昔教授に良く質問をしていたことを思い出した。style50 hello.c で実行可能。

style50

さらにさらに、check50 という正確性をテストするツールも用意されている!10年前にも欲しかった。check50 hello.c で実行可能。課題を提出する前に簡単にチェックできるのは大変有り難い。この3つのツールは自習する上でかなり役に立つはずだ。

check50

Commands(コマンド)

CS50 IDEは、クラウド上のバーチャルコンピュータなので、コマンドを実行することもできる。以下、授業中に紹介されたコマンド。

~/$ ls
hello* hello.c

//lists the contents
~/ $ ls

// executable file is expressed with *
~/ $ hello*

// remove a file
~/ $ rm hello

// move a file or rename a file
~/ $ mv hello.c goodbye.c

// create folders or directories
~/ $ mkdir lecture

// change directory
~/ $ cd lecture/

// in lecture directory
~/lecture/ $

// change to parent folder (single . refers to current directory)
~/ $ cd ..

// move the file to the folder above (parent directory)
~/ $ mv hello.c ..

// remove directory
~/ $ rmdir lecture/

// copy files and folders
~/ $ cp

 

Types, format codes(型宣言、Placeholder等)

  • C言語で使用するデータ型の紹介:
    • bool, a Boolean expression of either true or false
    • char, a single ASCII character like a or 2
    • double, a floating-point value with more digits than a float
    • float, a floating-point value, or real number with a decimal value
    • int, integers up to a certain size, or number of bits
    • long, integers with more bits, so they can count higher than an int
    • string, a string of characters
  • CS50 の library はデータ型取得するためのライブラリーを用意している:
    • get_char
    • get_double
    • get_float
    • get_int
    • get_long
    • get_string
  • printf で使用する placeholders 各種:
    • %c for chars
    • %f for floats, doubles
    • %i for ints
    • %li for longs
    • %s for strings

 

Operators, limitations, truncation(演算子、限界、切り捨て)

  • 四則演算を行うためのオペレーター:
    • + 足し算
    • - 引き算
    • * 掛け算
    • / 割り算
    • % 余り
  • 足し算をするためのプログラムを作ってみると以下のようになる
    • ヘッダーファイルで get_int を使用するためにCS50 libraryを読み込み、xy に読み込み
    • printf で placeholder を使って型を指定する
    • 32 bitsの限度:int 型は約4 billionしか使えないかつプラスマイナスを考慮するとその半分の領域しかない
#include <cs50.h>
#include <stdio.h>

int main(void)
{
    int x = get_int("x: ");
    int y = get_int("y: ");
    printf("%i\n", x + y);
}

それを解決するために long 型に変えることも可能

#include <cs50.h>
#include <stdio.h>

int main(void)
{
    long x = get_long("x: ");
    long y = get_long("y: ");
    printf("%li\n", x + y);
}

最後にもう一つ

#include <cs50.h>
#include <stdio.h>

int main(void)
{
    // Get numbers from user
    int x = get_int("x: ");
    int y = get_int("y: ");
    // Divide x by y
    float z = x / y;
    printf("%f\n", z);
}
  • xy で割った値を z に代入するというシンプルなプログラムだが、整数を整数で割った後に小数点が発生しているが、整数型を使用しているため小数点以下が切り落とされ 0.0000001.000000 と表示されてしまう。
  • これを直すためには割り算をする前にキャスト(変換)してあげれば良い。
  • float z = (float) x / (float) y;

 

Variables, syntactic sugar(変数、糖衣構文)

  • 変数への初期化(値の設定) int counter = 0; 、カウンターの作成(1ずつ増える)counter = counter + 1 その簡単な表記の仕方 counter += 1;counter++; があり、これを糖衣構文と呼ぶ。
  • 糖衣構文とは、プログラミング言語において、複雑でわかりにくい書き方と全く同じ意味になるものをよりシンプルでわかりやすい書き方で書くことができるもののことである。

 

Conditions(条件分岐)

もし X が Y だったら、Zを実行するといったプログラム。

#include <cs50.h>
#include <stdio.h>

int main(void)
{
    // Prompt user for x
    int x = get_int("x: ");

    // Prompt user for y
    int y = get_int("y: ");

    // Compare x and y
    if (x < y)
    {
        printf("x is less than y\n");
    }
    else if (x > y)
    {
        printf("x is greater than y\n");
    }
    else
    {
        printf("x is equal to y\n");
    }
}

ユーザーに同意するかを問いかけ、その結果によって表示内容を変えるというプログラム

#include <cs50.h>
#include <stdio.h>

int main(void)
{
    char c = get_char("Do you agree? ");

    // Check whether agreed
    if (c == 'Y' || c == 'y')
    {
        printf("Agreed.\n");
    }
    else if (c == 'N' || c == 'n')
    {
        printf("Not agreed.\n");
    }
}

|| は論理演算子の OR(どちらかが True 正しい), && は、AND(両方とも True 正しい)

 

Boolean expressions, loops(ブーリアン演算、ループ)

while は、〜する間という意味なので、以下のプログラムでは、ずっとプログラムを走り続けさせる(無限ループ)場合は、ブーリアン演算を使って True とする

while (true)
{
    printf("hello world\n")
}

ある一定回数プログラムを回す場合は、条件を与えてあげればいい

int i = 0;

while (i < 50)
{
    printf("hello, world\n");
    i++;
}

i を0にセットし、50になるまでカウンターを使って制御している。

for (int i = 0; i < 50; i++)
{
    printf("hello, world\n");
}

最もよく使われるのが for 文。

 

Abstraction(抽象化)

ここでは、プログラムを function を使って複雑になりがちなプログラムを軽減するために、効率的に書くことを習う。ただ、そこでの注意点がScope(スコープ)。型宣言の対象範囲に注意する必要がある。

#include <cs50.h>
#include <stdio.h>

int get_positive_int(void);

int main(void)
{
    int i = get_positive_int();
    printf("%i\n", i);
}

// Prompt user for positive integer
int get_positive_int(void)
{
    int n;
    do
    {
        n = get_int("Positive Integer: ");
    }
    while (n < 1);
    return n;
}

この例では、get_positive_int(void) の中で宣言している int n; に注目して欲しい。do 文の前に宣言しているが、これは do 文の後にも使用しないといけないからである。do 文の中で宣言してしまうとその中でしか使えない。

 

Mario(スーパーマリオブラザーズ!)

「さて、このプログラムどうやって書く?」って「え、いきなり難しすぎないか?」と思ったが、実際は一部分のプログラム。

Super Mario

このはてな「?」をどうやってプログラムをするか、ということだった。昔はハードコード可能性はあるが、最近のゲームは動的に書いているらしい。

Super Mario 2

これをC言語で表すと次のようになる。

#include <stdio.h>

int main(void)
{
    for (int i = 0; i < 4; i++)
    {
        printf("?");
    }
    printf("\n");
}

仮に、ユーザーから「?」の長さを受け取るとすると、

#include <cs50.h>
#include <stdio.h>

int main(void)
{
    // Get positive integer from user
    int n;
    do
    {
        n = get_int("Width: ");
    }
    while (n < 1);

    // Print out that many question marks
    for (int i = 0; i < n; i++)
    {
        printf("?");
    }
    printf("\n");
}

そして、今度は以下のブロックを作ることを考える。

mario blocks

C言語で記述すると、for 文の中に for 文を書くことで立体にすることができる。

#include <cs50.h>
#include <stdio.h>

int main(void)
{
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            printf("#");
        }
        printf("\n");
    }
}

 

Memory, imprecision, and overflow

コンピュータに積まれたメモリ(RAM)について、それは有限である。使用可能な領域を超えると、floating-point imprecisionという現象が起きる。

#include <cs50.h>
#include <stdio.h>

int main(void)
{
    float x = get_float("x: ");
    float y = get_float("y: ");

    printf("%.50f\n", x / y);
}
x: 1
y: 10
0.10000000149011611938476562500000000000000000000000

結果は、数学的には正しくない。

仮に3ビットで111(8)に1を足すと1000(9)になる。よって、3ビットの領域では、1は枠外のため、000が残る。これが、integer overflow。

 

感想

第1回目の講義「C言語」の動画を見終えての感想は基本の復習は大事ということと、やはり何度も見直せるオンライン動画はどの学習にも向いている気がする。まだ基本だからそこまで動画を繰り返し見ることはないが、これからは増える気がしている。分からないところは何度も何度も見直せる、良い時代になったもんだ。