入力と条件分岐

先週記事が投稿されていない件について。
3時間ほどかけて記事を書いて投稿したら「メンテ中」ですという WEB ブラウザからの返事とともに、すべてが消滅するという初歩的ミスをしてしまいました;

さて本題。前回まではプログラムに書かれているものが一方的に出力されるだけでしたが、今回はユーザ側からプログラムに対して何か入力するということをできるようにしていきましょう。

今回学ぶ考え方は「入力」と「条件分岐」です。
いろいろな Java の機能を合わせて紹介しますが、あくまで上記の概念を表現するための機能であるということを念頭に置いてください。

ユーザインターフェース

ユーザインターフェースとは、ユーザが対象を操作する手段(入力)とユーザが操作した結果対象が生成したものを提示する手段を提供するものを指します。
今まではコマンドラインベースで、文字列が出力されてきましたが、キーボードからの文字列を受け付けるようにしたプログラムの作成が今回のお題です。

キーボードで文字列を入力し、ディスプレイに文字列を出力するインターフェースを、キャラクタユーザインターフェース (CUI) と呼びます。
対して、マウスでボタンをクリックしたりしながら進めるユーザインターフェースは グラフィカルユーザインターフェース (GUI) と呼びます。

# 余談ですが、分かりやすいインターフェースデザインというのを考えるのは難しいもので、たとえば「誰のためのデザイン」という書籍を読むとマウスとキーボードでの操作の差などを知ることができます。

入力入門 (オウム返し)


import java.util.Scanner; // java の機能として、Scanner というライブラリが用意されているので使うことにします。

public class HelloCUI {

    public static void main(String[] args) {
        System.out.print("なにかメッセージを入力してください>");
        Scanner scanner = new Scanner(System.in); // System.in(標準入力) をスキャンしたスキャナを用意します。
        String message = scanner.nextLine(); // scanner に1行メッセージスキャンしてもらいます。それを message 変数に入れておきます。
        System.out.println("あなたが入力したメッセージは以下ですね?");
        System.out.println(message); // out に message を println してもらいます。
        System.out.println("以上、オウム返しプログラムでした。");
    }

}
コメントの使い方

今回はソースコード中にコメントを使いました。
コメントとは、コンパイルするときには無視される部分で、難しい処理をしていてコードが読みにくいときなどに補足メッセージを入れておくことができます。
# とはいえ、「// scanner に1行メッセージスキャンしてもらいます。それを message 変数に入れておきます。」などは Java プログラマにとっては「読めば分かる」話なので、このようなコメントを書くことは冗長です。コメントを書きすぎて可読性を下げては本末転倒なので気をつけましょう。

// コメント文 (// より後ろが1行コメントとして無視されます)
Scanner による入力について

このプログラムを動かすと、入力待ち状態になり、キーボードからの入力を受け付けるようになったと思います。
そして、キーボードから入力されたメッセージに従って、出力するメッセージが動的に変わります。
# プログラミングの世界では静的・動的という言葉がよく使われますが、何度も聞くうちに慣れると思います。

Scanner というのは Java が提供している入力用ライブラリです。
System.in (標準入力) をスキャンしてくれるスキャナを用意することで、以後、スキャナに対して nextLine() というメッセージで「次の行を読んでくれ」と頼み入力を与えることができるようになっています。

安易に「入力するときには Scanner って書けばいいんだ」と思わないように気をつけてください。
大事なのは「プログラミングの世界には入力という概念がある」ということであり、Java では「Scanner というオブジェクトにメッセージをスキャンしてもらう」という形で入力を表現することが可能であるということです。
# C言語では scanf(), ruby では gets といった形で入力機能が提供されています。

リストへの要素の入力

キーワード: ArrayList<型>

import java.util.ArrayList; // java が提供しているライブラリ、リスト。
import java.util.Scanner; // java が提供しているライブラリ、スキャナ。 

public class HelloCUI {

    public static void main(String[] args) {
        System.out.println("ここに5つの要素(テキスト)が入るリストがあります。");
        System.out.println("何か文字列を入力していってみてください。");
        Scanner scanner = new Scanner(System.in);
        ArrayList<String> list = new ArrayList<String>(); // String が入るリストです。
        System.out.print("1つ目>");
        list.add(scanner.nextLine());
        System.out.print("2つ目>");
        list.add(scanner.nextLine());
        System.out.print("3つ目>");
        list.add(scanner.nextLine());
        System.out.print("4つ目>");
        list.add(scanner.nextLine());
        System.out.print("5つ目>");
        list.add(scanner.nextLine());
        System.out.print("リストには以下の5つの要素が挿入されました。");
        System.out.println(list.toString());
    }

}
JavaDoc について

Java から提供されるオブジェクトとして、ArrayList や Scanner を使ってきました。
「オブジェクトにはメッセージで話しかけることができる」と何度も言ってきましたが、これらのオブジェクトに対してどんな風に話しかければ答えてくれるのでしょうか。
これからは、JavaDoc と呼ばれる API 仕様書で確認しながら使うようにしてみましょう。
# Java のプログラミングは JavaDoc でメソッドを確認して話しかけるのが基本です。

概要 Java Platform SE 6

メソッドの概要というところを見ていきます。
ArrayList に対して要素を挿入するには add(element) と話しかければよいことが分かります。
ArrayList (Java Platform SE 6)

Scanner で現在行を読むには nextLine() を呼べばよさそうということが分かります。
[http://java.sun.com/javase/ja/6/docs/ja/api/java/util/Scanner.html:title=
Scanner (Java Platform SE 6)]

ArrayList の型指定について

前回、ArrayList というリストにコインを追加していく例を挙げましたが、今回はこのリストに好きな文字列を入力していく例です。
ArrayList というライブラリは、どんな要素が入ってくるのかを明示しておくことができます。
この例では、scanner.nextLine で得られるものは文字列(String)なので、ArrayList と書いて「このリストは String しか入りません!」と明示しています。
これは書かなくても動くのですが、数字など String 以外の要素が入ってこないように Java コンパイラが未然に防いでくれる機能です。
安全であることが保障されるプログラムを書くことは基本になりますので、ArrayList を使うときには要素に何が入るかを明示するようにしていきましょう。

条件分岐 if/else

Scanner だけでは、一方的にプログラムに対して入力しているだけじゃないかという話になってきます。
プログラムと対話するために、もし○○なら△△する。そうでなければ××するといったことを表現したくなるものです。

このような「もしなら」という表現する機能 Java では if/else という構文で記述することが可能であり、他の様々なプログラミング言語でも、それぞれ記述方法は異なることもありますが、その機能は提供されていることが多いです。

以下はパスワード認証の例です。

import java.util.Scanner;

public class HelloCUI {

    public static void main(String[] args) {
        String password = new String("HelloHello"); // パスワードは HelloHello ということにします。

        System.out.print("パスワードを入力してください>");
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();

         // 入力文字列とパスワードの比較を行います。
        if(password.equals(input)) {
            System.out.println("認証に成功しました。");
        } else {
            System.out.println("認証に失敗しました。");
        }
    }

}

もしパスワード(password)と入力文字列(input)が同じ文字列であれば認証に成功するというシナリオです。
文字列(String)はオブジェクトなのでメッセージを投げて比較確認を行うことができます。
password に対して、あなたは input ですか?と聞くと、一致していれば if の中に書いた処理を行い、異なれば else の中に書いた処理を行います。

import java.util.Scanner;

public class HelloCUI {

    public static void main(String[] args) {
        String password = new String("HelloHello"); // パスワードは HelloHello ということにします。

        System.out.print("パスワードを入力してください>");
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();

         // 入力文字列とパスワードの比較を行います。
        if(password.equals(input)) {
            System.out.println("認証に成功しました。");
        } else if(input.length() > 10) { // input の長さは?それは10より大きい?
            System.out.println("ヒント:パスワードは10文字以内です。");
        } else {
            System.out.println("認証に失敗しました。");
        }
    }

}

複数条件を書きたいときには、間に else if と書くことができます。
input.length() というのは、String オブジェクトに対して、あなたの長さはいくつですか?と聞くメッセージです。
そしてその結果を > という比較演算子で比較しています。数字はオブジェクトではなくリテラルだったので、メッセージで尋ねることができません。なので比較演算子という Java の機能で比較しています。

入力と制御の応用例

Scanner による入力と、変数、if/else 構文を覚えただけでもいろいろなことができるようになっています。
状態遷移という言葉がキーワードになります。

自動販売

ある一定金額まではお金を投入し続けるように入力を何度も求めるようにします(入力状態とします)。もしある金額を超えたら飲み物を選ぶように促します。もしある飲み物が選択されたら、飲み物を出し、入れた金額から飲み物代を引いた金額をお釣りとして返します。
# オートマトンという言葉を調べてみると面白いかもしれません。

ゲームのフラグ

例えばシミュレーションゲームRPG
もしあるアイテムを手に入れたら次のマップに進めるようになります。

ボードゲームの CPU 戦

例えばオセロなどのボードゲーム
「もしここに打たれたらここに打てば勝てる」という情報が全て書かれていれば次に打つべき手が自動的に決まることになります。
# if/else でルールベースを構築してしまうということですね。

ファンの制御

もし暑かったら空気を読んで涼しくしてくれるような知的なエアコンがあったらいいですね。
# ファジィ理論と呼ばれるものがあります。

if(部屋の温度が "非常に寒い") {
    fan.stop();
} else if(部屋の温度が "寒い") {
    fan.speedDown();
} else if(部屋の温度が "普通") {
    fan.speedKeep();
} else if(部屋の温度が "暑い") {
    fan.speedUp();
}
状態遷移という考え方

「ある変数(状態)」が「ある集合の要素」に一致していたら「アクション」を起こすことができるため、様々な処理の自動化などが行えるということです。
変数と if/else 人工知能のルール定義やネットワークの状態遷移、コンパイラの作成など、今後も深く使える考え方になってきます。