プログラミング基礎講座第6回

ゼロから始めるプログラミング基礎講座

条件分岐:複合条件の指定とネスト

講座全体の概要

プログラミング基礎講座の第6回の延長戦です。講座全体の概要は以下の記事ゼロから始めるプログラミング基礎講座 第1回【講座の概要・目的】をご覧ください。

前半の記事はこちら。 後半の記事はこちら。

前半、後半の内容

前半はif文での基本の条件分岐を学習しました。後半はこれを発展させたif-else文と、画面入力機能の説明を途中まで学習しました。延長戦では画面入力機能を使って、条件分岐での複合条件ネストについて学習していきます。また、もう1つの条件分岐switch文についても簡単に紹介します。

プロジェクトは後半で作成した「BasicCourse6_2」を使用します。

ボタンクリック時の処理 asp-page-handlerとOnPost処理の追加

後半の最後で、画面の初期表示時に結果等を非表示にする処理が完成しました。続いてコールボタンクリック時の処理を追加して、正しく判定結果が表示されるようにしていきます。

以下のコードを参照してください。

Index.cshtml

@page
@model IndexModel
@using static IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class="text-center">
    <h1>ハイ&ロー</h1>
    <form method="post">
        <div class="form-group w-100">
            <div class="form-inline form-row mb-2">
                <label asp-for="ParentNumber" class="col-form-label col-sm-2 offset-sm-3 justify-content-end">親の数字</label>
                <input asp-for="ParentNumber" type="text" class="form-control col-sm-2" readonly="readonly" />
            </div>
        </div>

        <div class="form-group w-100 justify-content-center">
            <div class="form-row w-100">
                <label asp-for="HighOrLow" class="col-form-label col-12 justify-content-center">ハイかローを選択してコールしてください。</label>
            </div>
            <div class="form-check form-check-inline justify-content-center mb-2">
                <div class="mx-3">
                    <input class="form-check-input" asp-for="HighOrLow" type="radio" value=1 id="radioHigh" required />
                    <label class="form-check-label" for="radioHigh">ハイ</label>
                </div>
                <div>
                    <input class="form-check-input" asp-for="HighOrLow" type="radio" value=2 id="radioLow" required />
                    <label class="form-check-label" for="radioLow">ロー</label>
                </div>
            </div>
        </div>

        <div class="row m-0 w-100 mt-3 mb-5">
            @* ボタンの記述にasp-page-handlerを追加 *@
            <input asp-page-handler="BtnCallClick" class="btn btn-primary col-sm-2 offset-sm-5" type="submit" value="コール" />
        </div>

        <div class="form-group w-100">
            <div class="form-inline form-row mb-2">
                <label asp-for="ChildNumber" class="col-form-label col-sm-2 offset-sm-3 justify-content-end">子の数字</label>
                <input asp-for="ChildNumber" type="text" class="form-control col-sm-2" readonly="readonly" />
            </div>
            <div class="form-inline form-row mb-2">
                <label asp-for="JudgedResult" class="col-form-label col-sm-2 offset-sm-3 justify-content-end">結果</label>
                <input asp-for="JudgedResult" class="form-control col-sm-2" type="text" readonly="readonly" />
            </div>
        </div>
    </form>
</div>

前回までのコードから、以下の部分だけ変更を加えています。


            @* ボタンの記述にasp-page-handlerを追加 *@
            <input asp-page-handler="BtnCallClick" class="btn btn-primary col-sm-2 offset-sm-5" type="submit" value="コール" />

ボタンのinputタグにC#独自の記述であるasp-page-handlerを追加しています。これを記述することにより、ボタンクリック時にIndex.cshtml.csで実行する処理と関連付けができます。

asp-page-handlerの記述方法

asp-page-handler="アクション名"

このアクション名が関連付ける処理の名前になります。名前は自由に付けられますが、何をした時の処理なのかがわかりやすいようにしてください。Index.cshtml.csは以下の様になります。

Index.cshtml.csの抜粋

namespace BasicCourse6_2.Pages
{
    public class IndexModel : PageModel
    {

                中略

        public void OnGet()
        {
                中略
        }

        // ここが関連付ける処理部分
        public IActionResult OnPostBtnCallClick()
        {
            return Page();
        }
    }
}

実際に関連付けられる処理はアクション名の前にOnPostを付け加えたものになります。これは、ブラウザーからのPostという種類の要求を受け付けることを意味しますが、詳細は別途、Webアプリケーションの講座で解説したいと思います。

asp-page-handlerと関連付ける処理の記述方法

public IActionResult OnPostアクション名()
{
    処理ブロック

    return Page();
}

IActionResultの部分とreturn Page()の部分は、処理した結果をブラウザーに返信する記述で、関数戻り値というものの指定です。asp-page-handlerで関連付ける処理では必ず記述するものとして覚えてください。関数及び戻り値については、第8回の講座で解説します。

処理ブロックの部分に、自分が行いたい処理を記述します。

ここまでの内容を実際にプログラミングしたら、return Page()の行にブレークポイントを設定してデバッグ実行してください。コールボタンをクリックしてブレークポイントで止まれば成功です。

入力項目と関連付けるプロパティにはBindProperty属性を付与する

ボタンクリック時のプロパティの値をウォッチで確認してみてください。全て初期値(0またはfalse)になっているはずです。ボタンをクリックすると数字が全て0になるのはこのためです。

ボタンクリック時には画面のform内にあるinput要素からデータが送信されているのですが、今のままでは送信されたデータがプロパティに反映されません。画面のinputからデータを反映させるには、プロパティにBindPropertyという属性(Attribute)を付与する必要があります。書き方は簡単で、以下の様にします。

Index.cshtml.csの抜粋

[BindProperty]
public int ParentNumber { get; set; }

[BindProperty]
public int ChildNumber { get; set; }

public string JudgedResult { get; set; }

[BindProperty]
public int HighOrLow { get; set; }

public bool ChildIsHidden { get; set; }

入力コントロールからデータを受け取りたいプロパティの直前の行に以下の記述をします。


[BindProperty]

今回はReadOnlyの項目(親の数と子の数)も画面から取得しています。本来、ユーザー入力以外のデータは別の方法で保持するべきなのですが、覚える内容が多くなりすぎるので、BindProperty属性を付与して済ませます。

これでボタンをクリックしても、数字が0にならずに表示されるはずです。実装して確認してください。(プログラミングすることをよく実装すると言います。)

if-else文のネスト(入れ子)

では、実際にクリック時の処理を実装して最後の仕上げをしましょう。以下のコードを参照して、実装してください。

Index.cshtml.csの抜粋

namespace BasicCourse6_2.Pages
{
    public class IndexModel : PageModel
    {
        private readonly ILogger<IndexModel> _logger;

        public IndexModel(ILogger<IndexModel> logger)
        {
            _logger = logger;
        }

        [BindProperty]
        public int ParentNumber { get; set; }
        [BindProperty]
        public int ChildNumber { get; set; }
        public string JudgedResult { get; set; }
        [BindProperty]
        public int HighOrLow { get; set; }
        public bool ChildIsHidden { get; set; }

        public void OnGet()
        {
            ChildIsHidden = true;

            ParentNumber = RandomNumberGenerator.GetInt32(1, 14);
            ChildNumber = RandomNumberGenerator.GetInt32(1, 14);
        }

        public IActionResult OnPostBtnCallClick()
        {
            ChildIsHidden = false;

            // if-else文①
            if (HighOrLow == 1)
            {
                // if-else文②
                if (ChildNumber > ParentNumber)
                {
                    JudgedResult = "当たり";
                }
                else if ((ChildNumber < ParentNumber))
                {
                    JudgedResult = "はずれ";
                }
                else
                {
                    JudgedResult = "引き分け";
                }
            }
            else if (HighOrLow == 2)
            {
                if (ChildNumber > ParentNumber)
                {
                    JudgedResult = "はずれ";
                }
                else if ((ChildNumber < ParentNumber))
                {
                    JudgedResult = "当たり";
                }
                else
                {
                    JudgedResult = "引き分け";
                }
            }
            else
            {
                return BadRequest();
            }
            return Page();
        }
    }
}

if-else文①の中に、更にif-else文②が記述されています。この様に、条件分岐の中で、更に別の条件分岐をすることをネストといいます。入れ子と言う方もいますが、同じ意味です。これは条件分岐だけではなくループでも同様なことが言えます。(ループの中で更にループする)

条件分岐をネストすると、外側(if-else文①)の条件を満たし、かつ、内側(if-else②)の条件を満たすという条件で分岐処理が行えます。

なお、条件式で同値比較(イコールの比較)する場合は「==」と書きます。「=」では左辺への代入として解釈されてエラーになるので気を付けてください。

以下の部分はブラウザーから不正な要求が来た場合に、ブラウザーに対してエラーとして返信する処理です。また、returnの行が実行されると、それ以降の処理はスキップされます。


                return BadRequest();

Index.cshtmlのラジオボタンから「required」の記述を削除すると、未選択でも処理ができますので試してみてください。

画面の表示部分の処理は以下の様になります。

Index.cshtml

@page
@model IndexModel
@using static IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class="text-center">
    <h1>ハイ&ロー</h1>
    <form method="post">
        <div class="form-group w-100">
            <div class="form-inline form-row mb-2">
                <label asp-for="ParentNumber" class="col-form-label col-sm-2 offset-sm-3 justify-content-end">親の数字</label>
                <input asp-for="ParentNumber" type="text" class="form-control col-sm-2" readonly="readonly" />
            </div>
        </div>
        <div class="form-group w-100 justify-content-center">
            <div class="form-row w-100">
                <label asp-for="HighOrLow" class="col-form-label col-12 justify-content-center">ハイかローを選択してコールしてください。</label>
            </div>
            <div class="form-check form-check-inline justify-content-center mb-2">
                <div class="mx-3">
                    <input class="form-check-input" asp-for="HighOrLow" type="radio" value=1 id="radioHigh" required />
                    <label class="form-check-label" for="radioHigh">ハイ</label>
                </div>
                <div>
                    <input class="form-check-input" asp-for="HighOrLow" type="radio" value=2 id="radioLow" required />
                    <label class="form-check-label" for="radioLow">ロー</label>
                </div>
            </div>
        </div>

        <div class="row m-0 w-100 mt-3 mb-5">
            @* 結果の表示をする時にはボタンを押せないように「disabled="disabled"」を追加する *@
            @{
                if (Model.ChildIsHidden)
                {
                    <input asp-page-handler="BtnCallClick" class="btn btn-primary col-sm-2 offset-sm-5" type="submit" value="コール" />
                }
                else
                {
                    <input asp-page-handler="BtnCallClick" class="btn btn-primary col-sm-2 offset-sm-5" type="submit" value="コール" disabled="disabled" />
                }
            }
        </div>

        @{
            string hiddenAttribute = string.Empty;
            if (Model.ChildIsHidden)
            {
                hiddenAttribute = "hidden=\"hidden\"";
            }
        }
        <div class="form-group w-100" @hiddenAttribute>
            <div class="form-inline form-row mb-2">
                <label asp-for="ChildNumber" class="col-form-label col-sm-2 offset-sm-3 justify-content-end">子の数字</label>
                <input asp-for="ChildNumber" type="text" class="form-control col-sm-2" readonly="readonly" />
            </div>
            <div class="form-inline form-row mb-2">
                <label asp-for="JudgedResult" class="col-form-label col-sm-2 offset-sm-3 justify-content-end">結果</label>
                <input asp-for="JudgedResult" class="form-control col-sm-2" type="text" readonly="readonly" />
            </div>
        </div>
    </form>
</div>

ボタンを押せないように制御する処理を加えています。@{ }の中ではC#のコードに混じって、HTMLの記述も可能です。HTMLのタグで始まる行はHTMLとして解釈されます。

以上で完成です。実際に仕様通りに動くか確かめながら、プログラムが理解できるようにデバッグ実行で1行1行処理を追ってみてください。

複合条件 AND OR

今回のプログラムではネストを用いて複数の条件を指定しました。これとは異なり、条件式に複数の条件(複合条件)を記述する方法もあります。下の例は先ほどのプログラムと同じ結果となりますが、複合条件で書き換えたものとなります。

複合条件の例

        public IActionResult OnPostBtnCallClick()
        {
            ChildIsHidden = false;

            if (!(HighOrLow == 1 || HighOrLow == 2))
            {
                return BadRequest();
            }

            if (ChildNumber == ParentNumber)
            {
                JudgedResult = "引き分け";
            }
            else if (((HighOrLow == 1) && (ChildNumber > ParentNumber)) ||
                    ((HighOrLow == 2) && (ChildNumber < ParentNumber)))
            {
                JudgedResult = "当たり";
            }
            else
            {
                JudgedResult = "はずれ";
            }

            return Page();
        }
AND条件(AかつB)の書き方

条件A && 条件B
OR条件(AまたはB)の書き方

条件A && 条件B
NOT条件(否定)の書き方

!条件式

慣例として、条件式1つ1つを( )で括ります。また、OR条件、NOT条件は( )の位置で意味が変わるため注意が必要です。

下の例をデバッグ実行で確認してください。条件A~Cのtrue/falseの組み合わせを変更して、理解できるように試してください。

ORとNOTの例

bool 条件A = true;
bool 条件B = false;
bool 条件C = true;
bool 結果;

結果 = false;
// 「条件Aを満たす」 かつ 「条件Bを満たす」、または「条件Cを満たす」
if ((条件A) && (条件B) || (条件C))
{
    結果 = true;
}

結果 = false;
// 「条件A かつ 条件Bを満たす」、または「条件C」を満たす(結果的には上の例と同じ)
if ((条件A && 条件B) || (条件C))
{
    結果 = true;
}

結果 = false;
// 「条件Aを満たす」かつ「条件Bまたは条件Cを満たす」
if ((条件A) && ((条件B) || (条件C)))
{
    結果 = true;
}

結果 = false;
// 「条件Aを満たさない」かつ「条件Bを満たさない」(どちらも満たさないという意味になる)
if (!(条件A) && !(条件B))
{
    結果 = true;
}

結果 = false;
// 「条件Aかつ条件B」を満たさない(どちらか一方、または両方満たさないという意味になる)
if (!((条件A) && (条件B)))
{
    結果 = true;
}

結果 = false;
// 「条件Aを満たさない」かつ「条件Bを満たす」
if ((!(条件A) && (条件B)))
{
    結果 = true;
}

switch文

単純に変数の値により分岐するような場合はif-else文をswitch文に書き換えることができます。

switch文の例

switch (HighOrLow)
{
    case 1:
        // HighOrLow == 1 の場合の処理ブロック
        break;
    case 2:
        // HighOrLow == 2 の場合の処理ブロック
        break;
    case 3:
    case 4:
        // HighOrLow == 3 の場合と HighOrLow == 4 の場合の処理ブロック
        break;
    default:
        // 上記のどのcaseにも該当しなかった場合の処理ブロック
        break;
}

各caseの処理ブロックの後には必ずbreakを記述しなければなりません。他の言語では必須でないものもありますが、その場合でも記述することが推奨されます。

breakはそのswitch文を終了する(switchから抜ける)ことを表します。必須でない言語の場合、続くcaseも処理されて意図しない動作になることがあるため、注意が必要です。

switch文は一見わかりやすいですが、if-else文で代替可能なため、積極的に使用する必要はありません。想定される利用シーンとしては、変数の値に限りがある場合、例えば1~5の整数とあらかじめ決まっている場合に、1~5全ての分岐処理を網羅していることを明らかにしたい場合等に使用します。

改善した方がいい部分

実は、今回作成したプログラムには改善した方が良い部分が2つあります。

1つは、HighOrLow == 1HighOrLow == 2という条件式です。今は1の時は「ハイ」、2の時は「ロー」とわかっていますが、後で見返した時や、他人が見た時に意味がわからなくなってしまいます。

もう1つは、当たり、はずれ、引き分けという文字列が複数の箇所で使われている部分です。タイプミスの修正や表示内容の変更をもれなく行うために1ヶ所で管理すべきです。

これらを簡単に解消する方法として、定数を使用することが挙げられます。また、数値についてはEnumというものを使用するとなお良いです。定数については以前の講座で出てきましたが、Enumについてはまだ説明していません。Enumについては下の記事をご覧ください。

長くなりましたが、以上で第6回講座を終了します。

プログラミング基礎講座第6回条件分岐:条件により処理を変える「if-else」前のページ

「+」や「-」は演算子の一種。その他にも良く使うものを紹介。次のページプログラミング基礎講座第7回

コメント
  1. この記事へのコメントはありません。

  1. この記事へのトラックバックはありません。

CAPTCHA


ピックアップ

  1. 4Kディスプレイで作業効率UP #3
  2. 4kdisplay for video

最近の記事

  1. recommended pc
  2. おすすめ動画配信サービス3選
  3. 4kdisplay for video
  4. オブジェクト指向の基本概念
  5. basic-knowledge-of-web-app
PAGE TOP