論理式

プログラムが実行する実際の作業は、式によって行われる。値または副作用を生じるプログラムのあらゆる部分を式と呼ぶ。42のような定数値や"hello"も式である。これらは、それぞれ定数値42および"hello"を生成するからだ。

注釈:値を生成することと変数を定義することを混同しないでほしい。値は変数に関連付ける必要はない。

writelnのような関数呼び出しも、副作用があるため式だ。writelnの場合、その効果は、出力ストリームに文字が配置されることである。これまでに作成したプログラムからのもう1つの例は、左辺の変数に影響を与える代入演算だ。

式は値を生成するため、他の式の一部として使用することができる。これにより、より単純な式からより複雑な式を形成することができる。例えば、現在の気温の値を生成するcurrentTemperatureという関数があるとする。この関数が生成する値は、writeln式で直接使用することができる。

writeln("It's ", currentTemperature(),
        " degrees at the moment.");
D

この行は4つの式で構成されている。

  1. "It's "
  2. currentTemperature()
  3. " degrees at the moment."
  4. 他の3つを使用するwriteln()

この章では、条件文で使用される特定の型の式について説明する。

しかし、先に進む前に、代入演算子をもう一度繰り返して、その左右にある2つの式に焦点を当てて説明しよう。代入演算子(=)は、右側の式の値を左側の式(例えば変数)に代入する。

temperature = 23      // 温度の値は23になる
D
論理式

論理式は、ブール演算で使用される式だ。論理式は、"答えが"はい"の場合は、ファイルを保存する"といった、コンピュータプログラムが判断を行うためのものである。

論理式は、偽を表すfalseと真を表すtrueの2つの値しかとることができない。

以下の例では、writelnの式を使用する。行の最後にtrueと表示されている場合、その行に表示されている内容は真であることを意味する。同様に、falseは、その行の内容が偽であることを意味する。例えば、プログラムの出力結果が以下の場合

There is coffee: true

"コーヒーがある"という意味になる。同様に、

There is coffee: false

は"コーヒーはない"を意味する。私は"... is ...: false"という構文を"ではない"または"偽である"という意味で使用している。

論理式は、条件文ループ関数パラメータなどで広く使われている。その仕組みを理解することは不可欠だ。幸い、論理式は説明も使用も簡単だ。

論理式で使用される論理演算子は、次の通りだ。

式のグループ化

式を評価する順序は、括弧を使って式をグループ化することで指定できる。より複雑な式に括弧で囲まれた式が含まれる場合、括弧で囲まれた式は、それらが含まれる式で使用される前に評価される。例えば、"コーヒーまたは紅茶があり、さらにクッキーまたはスコーンがあるなら、私は幸せだ"という式は、次のようにコード化できる。

writeln("I am happy: ",
(existsCoffee || existsTea) && (existsCookie || existsScone));
D

サブ式が括弧で囲まれていない場合、式は Dの演算子の優先順位規則(C言語から継承)に基づいて評価される。この規則では、&&||よりも優先順位が高いため、括弧で囲まないで式を記述すると、意図したとおりに評価されない。

writeln("I am happy: ",
existsCoffee || existsTea && existsCookie || existsScone);
D

&&演算子が最初に評価され、式全体は次の式と意味的に同等になる。

writeln("I am happy: ",
existsCoffee || (existsTea && existsCookie) || existsScone);
D

これは全く異なる意味になる:"コーヒーがあるか、または紅茶とクッキー、またはスコーンがある場合、私は幸せだ"。

演算子の優先順位表は、この本の後半で説明する。

boolの入力の読み取り

上記の boolの値はすべて、自動的に "false"または "true"として自動的に表示される。逆の場合も同じで、readf()は文字列を自動的に "false""true"を、それぞれfalseおよびtrueというbool値に変換する。小文字と大文字の任意の組み合わせも受け付ける。例えば、 "False""FALSE"false"True""TRUE"trueに変換される。

これは、bool変数に読み込む場合のみに当てはまることに注意。それ以外の場合、string変数に読み込むと、入力は変換されずにそのまま読み込まれる。(文字列の章で後で説明するように、文字列を読み込むときはreadln()を使わなければいけない。)

演習
  1. <および>演算子は、ある値が別の値よりも小さいか大きいかに応じて判断するために使用されることをこれまで見てきた。しかし、ある値が2つの値の間にあり、その2つの値のどちらにも属さないかを判断するための演算子は存在しない。

    プログラマーが、valueが10と20の間にあるかどうかを判断する次のコードを書いたと仮定しよう。このコードは、このままではコンパイルできないことに注意しよう:

    import std.stdio;
    
    void main() {
        int value = 15;
    
        writeln("Is between: ",
                10 < value < 20);        // ← コンパイルエラー
    }
    D
    logical_expressions.2

    式全体を括弧で囲んでみよう。

    writeln("Is between: ",
            (10 < value < 20));      // ← コンパイルエラー
    D

    それでもコンパイルできないことに注意。

  2. この問題の解決策を探しているうちに、同じプログラマーは、次のよう括弧を使用するとコードがコンパイルできることを発見した:
    writeln("Is between: ",
            (10 < value) < 20);      // ← コンパイルできるが、間違っている
    D

    これで、プログラムは期待どおりに動作し、"true"と出力されるようになった。残念ながら、この出力はプログラムにバグがあるため、誤解を招くものである。このバグの影響を確認するには、15を20より大きい値に置き換えてみよう。

    int value = 21;
    D

    プログラムは、21が20未満ではないにもかかわらず、"true"と表示し続けることに注意。

    ヒント:論理式の型はboolであることを思い出してみよう。boolの値が20未満であるかどうかは意味がないはずだ。コンパイルできる理由は、コンパイラがブール式を1または0に変換し、それを20と比較して20未満であるかどうかを評価するためだ。

  3. "は、~の間にあるか?"という質問に答える論理式は、次のようにコーディングする必要がある。"下限値より大きく、上限値より小さいか?"

    その論理に従ってプログラム内の式を変更し、期待どおり"true"と表示されることを確認。さらに、論理式が他の値でも正しく機能することをテスト。例えば、valueが50または1の場合、プログラムは"false"と表示され、12の場合、プログラムは"true"と表示されるはずだ。

  4. 次の条件の1つが真の場合にビーチに行けるものと仮定しよう:
    • ビーチまでの距離が10マイル未満で、全員分の自転車がある場合
    • 私たちの人数が6人未満で、車があり、少なくとも1人が運転免許を持っている場合

    このプログラムでは、常に"true"と表示される。上記の条件のいずれかが真の場合に"true"と表示される論理式を作成しよう。(プログラムを試すときは、"あるか"で始まる質問には"false"または"true"と入力)。

    import std.stdio;
    
    void main() {
        write("How many are we? ");
        int personCount;
        readf(" %s", &personCount);
    
        write("How many bicycles are there? ");
        int bicycleCount;
        readf(" %s", &bicycleCount);
    
        write("What is the distance to the beach? ");
        int distance;
        readf(" %s", &distance);
    
        write("Is there a car? ");
        bool existsCar;
        readf(" %s", &existsCar);
    
        write("Is there a driver license? ");
        bool existsLicense;
        readf(" %s", &existsLicense);
    
        /* 以下の'true'を、
         * 質問に記載されている条件のいずれかが満たされたときに
         * 値'true'を生成する論理式に置き換えてください: */
        writeln("We are going to the beach: ", true);
    }
    D
    logical_expressions.3

    さまざまな値を入力して、作成した論理式が正しく動作することを確認しよう。