プログラミング入門(金曜1・2限)講義資料(2008年後期)

プログラミング入門 第6回

文字列

数値の扱いはできるようになったので,次は文字列 (文字の並び) を扱えるようになりましょう.

Javaで文字列を扱うには,Stringクラスを使ってこんな風に書きます.

    String h;		// Stringクラスの変数 h を作る
    h = "Hello, ";	// 文字列定数は "" でくくる
    String w = "World";

    System.out.print(h);
    System.out.println(w);
  

書き方はintやdoubleと変わりません.Stringクラスとは何かという話は後でやります.

文字列の連結

+ 記号を使って2つの文字列を連結することができます.

    String h = "Hello, ";
    String w = "World";

    String hw = h + w;
    System.out.println(hw);		// Hello, World と表示する
  

文字列を,文字列でないものと連結するときは,後者は文字列に変換されます.

String tetsujin = "鉄人" + 28 + "号"; // 数値である28が,文字列である"28"に変換されて連結される

この機能を使うと,計算結果を簡単に表示できます.

    double menseki = 3.14 * r * r;
    System.out.println("面積は " + menseki + " です");
  

文字列の長さ

キーボードから入力された文字列の文字数を数えるプログラムをつくってみましょう.キーボードから文字列を入力するには Keyboard.stringValue() を使います.

文字数を数えるためには (Stringクラスの)変数名.length() と書きます.


このファイルをダウンロード ■ UNIX用(EUC版) ■ Windows用(SJIS版)
(上のどちらかのリンクを右ボタンでクリックして「リンク先を名前をつけて保存」して下さい)

// 文字数を数える
public class Mojisu {
    public static void main(String[] arg) {
        System.out.print("名前は? ");
        String name = Keyboard.stringValue();
        System.out.println(name + "さん,こんにちわ!");
	
	// nameの文字数を計算して表示
        int mojisu = name.length();
        System.out.println(name + "さんの名前は" + mojisu + "文字ですね!");
    }
}


部分文字列

同様に,文字列の中の一部分を取り出すには,次のようにsubstringを使います.

    String hello = "Hello";
    // helloの1文字目から4文字目の直前まで取り出す
    String s1 = hello.substring(1, 4);  // s1 は "ell" になる
    // helloの1文字目以降を取り出す.
    String s2 = hello.substring(1);     // s2 は "ello" になる

Javaでは文字列の最初の文字を0文字目と数えることになっています.

この例では,部分文字列を取り出すときにどこを取り出せば良いのかを,(1, 4)のように指定しています.

文字列の比較

ある文字列が別の文字列と等しいかどうかは,== では比較できません.

String a と String b が等しいかを判定するためには,a.equals(b)(あるいは b.equals(a)) のように書きます.

    String str = Keyboard.stringValue();
    // 入力された文字列が End だったら…
    if (str.equals("End")) {
      System.out.println("もうおしまい?");
    }

演習 (一人しりとり)

文字列をくりかえし入力して,最後に「ん」がつく単語が入力されたら終了するプログラム Shiritori を書いてください.ちゃんと「しりとり」になっているかのチェックは不要です.

単語: りんご
単語: ごりら
単語: らいおん
あなたの負け!
  

ヒント: 文字列の最後の文字を得るためには,length()で長さを求めてsubstring()を使う.

演習 (文字列の逆転)

文字列を入力して,逆順に表示するプログラム StringReverse をつくってみましょう.

文字列を入力してください: This is a pen.
逆順にすると,.nep a si sihT です.
  

ヒント: 文字列の長さを length() で求めて,右端から左端に向かって1 文字づつ表示する.

演習 (単語分解)

余裕がある人用

文字列を入力して,単語毎に分解して表示するプログラム WordSplit をつくってみましょう.

文字列を入力してください: This is a pen.
This
is
a
pen.
  

ヒント: 文字列の長さを length() で求めて,左端から右端に向かって1 文字ずつ表示する.ただし,空白文字の場合は改行する.

オブジェクト指向の考え方

例えば,車全体を一つのモノとしてつくるよりも,シャーシ,エンジン,変速機,タイヤ等の様々な部品を組み合わせてつくったほうが簡単です.コンピュータのプログラムも,最初から全部を一枚岩でつくるよりも,いろいろな部品を組み合わせて作る方が簡単です.

このように部品(モノ)を組み合わせてプログラミングする方法がオブジェクト指向プログラミング(Object Oriented Programming,OOP)です.

さて,Stringクラスの変数は以下の機能を持っています.

このように,オブジェクト指向プログラミングでは,文字列というモノと,文字列に対する操作をペアにして考えます.

車は,ハンドルやアクセル,ブレーキ等の使い方さえを知っていれば運転できます.車を運転するために,ハンドルを回したときのタイヤの回転角度や,アクセルを踏んだ角度に応じたガソリンの噴射量の計算法,エンジンの点火タイミング等を知る必要はありません.オブジェクト指向プログラミングでは,この考え方を取り入れています.文字列を使うためには,文字列の代入の仕方や長さの求め方等の使い方だけを知っていれば良く,その文字列がコンピュータの中でどのように表現されているか,どのようにして文字列の長さを計算しているのか等は知る必要はありません(これを情報隠蔽と呼びます).

オブジェクト指向プログラミング以前は手続き型プログラミングという方法が流行していました.これは「ある処理をどうやれば良いのか」に注目してプログラミングする方法です.

クラスとは

Stringクラスの変数には,いろいろな文字列を格納することができますが,基本的な性質(文字列を格納して長さを求めたり,連結したりできる)は同じです.

クラスとは,このように同じ性質をもつモノの設計図に相当するものです.(クラスの例: 文字列,数値,車のエンジン,ある学年の生徒,あるゲームのあるキャラクタ,画面のウインドウ,etc.)

あるクラスの実体(モノのこと)のことをオブジェクト(あるいはインスタンス)と呼びます.一つのクラス(設計図)から複数のオブジェクト(モノ)を作ることができます.

Javaでは,クラスを自由に定義することができます.つまり,いろいろな機能を持つモノを自由に作ることができるわけです.クラスを定義できるのはオブジェクト指向言語の特徴です.Stringクラスは,Javaで初めから用意されているクラスの一つです.

クラスには,次の要素が含まれています.

オブジェクトの生成

Stringクラスを使うためには下のように書きました.

String str;
str = "Hello, World";

実は,上の書き方は以下の形の省略形です(※).(Javaでは文字列を自然に扱えるようにStringクラスをちょっと特別扱いしています.)

String str;
str = new String("Hello, World");

※ わかりやすく説明するためにこう書いていますが,本当はちょっと違います

オブジェクトを作るためにはnew 演算子を使います.

new String("Hello, World")によって,中身が "Hello, World" であるStringクラスのオブジェクトがメモリ上のある領域(ヒープ領域と呼ばれます)に生成されます.str にはそのオブジェクトがどこに格納されているかという情報が代入されます.

str のような変数をオブジェクト変数と呼びます.オブジェクト変数は,オブジェクトを指し示す(参照する)ための変数です.(参照のことをリファレンスということもあります.)

String str; だけではオブジェクトは作られません.オブジェクトが作られるのはあくまで new した時です.ですから,以下のようなプログラムはエラーになります.

String str;
int i;
i = str.length();  // エラー.オブジェクトがまだ存在しない.

配列を使ったときに new を使いました.実はJavaでは配列もオブジェクトです.

int[] array = new int[10];


一般に,オブジェクトを生成するには以下のように書きます.

   // 引数がある場合
   オブジェクト変数 = new クラス名(引数, ...);
   // 引数がない場合
   オブジェクト変数 = new クラス名();

引数は,どのようなオブジェクトを作成するかを指定するために使われます(赤い車,青い車…).どのような引数が必要かということは,クラスによって予め決まっています.引数はない場合もありますが,その場合でも(かっこ)は必要です.

クラスには,「new されたときにそのオブジェクトを作り出す」ための処理が含まれています.これはそのクラスのコンストラクタが行います.

Stringの場合,オブジェクトを生成するときの引数には文字列を書くことも,何も書かないこともできます.

  // 最初から中身が入ったStringオブジェクトを作る
  String str1 = new String("Hello, World");
  // 中身が空なStringオブジェクトを作る
  String str2 = new String();

これは,Stringクラスのコンストラクタに,引数として文字列がある場合,何もない場合の両方が定義されているためです.

メソッド

オブジェクトに命令する(インスタンスメソッド)

String クラスのオブジェクト変数 str に対して,文字数を数えるには str.length(),部分文字列を取り出すには str.substring(1, 4) のように書きました.length や substring は,String クラスのオブジェクトに対して働くもので,インスタンスメソッドと呼びます.インスタンスメソッドは,特定のオブジェクトに対して何らかの操作を実行するためのものです.

インスタンスメソッドを使う(インスタンスメソッドを呼び出す)ためには以下のように書きます.

// 引数がある場合
オブジェクト変数 . メソッド名(引数, ... );

// 引数がない場合
オブジェクト変数 . メソッド名();

これによって,そのオブジェクト変数が指しているオブジェクトに対してメソッド名で表される操作が実行されます.

(インスタンスメソッドは,オブジェクトを new で作った後でしか呼び出すことはできません.)

引数は,行う操作の内容を細かく指定する必要がある場合に使います(例えば substring の場合は何文字目から何文字目かということを引数で指定しました.) 引数がない場合にも(かっこ)は必要です.

メソッドは処理をした結果を,値や別のオブジェクトという形で返す場合があります.例えば,Stringクラスのlengthメソッドは,文字列の長さをint型の数値で返します.

あるクラスにどのようなメソッドがあって,それぞれどのような引数が必要で,どのような値を返すのか(あるいは返さないのか)ということは,クラスによって予め決まっています.

クラスに命令する(クラスメソッド)

インスタンスメソッドは特定のオブジェクトに対して働きますが,特定のオブジェクトと関連しない操作もあります.例えば,ある車を表すクラスがあったとして,「車の幅を知る」「車の定員を知る」のような操作は,車の実体が存在しなくても可能です.このように,あるクラス(この場合は車)に関連してはいるものの,特定のオブジェクト(車の実体)が無くても実行可能な操作があります.

→車の定員を知るために車を製造してみないといけないということはないですよね?

このような操作には,クラスメソッド(静的メソッドとも)を使います.クラスメソッドは,あるクラスに密接に関連しているものの,特定のオブジェクトに結び付かない操作を行うために使われます.

クラスメソッドを使うには,次のように書きます.

// 引数がある場合
クラス名 . メソッド名(引数, ... );

// 引数がない場合
クラス名 . メソッド名();

例えば String クラスには,valueOfというクラスメソッドがあります.これは,引数で与えた値を文字列に変換するメソッドです.

int a = 10;
String str = String.valueOf(a);  // str には文字列 "10" が入る

「数値を文字列に変換する」という操作は,文字列に関連してはいますが,文字列のオブジェクトに対して行うわけにはいきません(変換する前には文字列オブジェクトが存在しないので).このため,valueOfという操作はクラスメソッドになっています.

今までに出てきた,Math.random() 等は Mathクラスのクラスメソッドを使っていたのでした.Mathクラスは,クラスメソッドしかない例外的なクラスです.Mathクラスのメソッドが全てクラスメソッドなのは,特定のオブジェクトが無くてもこれらの計算はできるからです.

Javaのクラス名は大文字から始める慣習なので,XXX.メソッド()のXXXの部分が大文字から始まっていればクラスメソッドの呼び出しと推測することができます(それはつまり,変数名は小文字から始めたほうが良いと言うことでもあります).

APIドキュメント

Stringクラスには,どのようなコンストラクタやメソッドがあるのでしょうか.

Stringクラスの場合,http://java.sun.com/j2se/1.4/ja/docs/ja/api/java/lang/String.htmlに全てのコンストラクタやメソッドの解説があります.

コンストラクタの概要
使用可能なコンストラクタの説明.new するときの引数のパターン毎に記述されている.
メソッドの概要
使用可能なメソッドの説明.static と書いてあるのがクラスメソッドで,そうでないものはインスタンスメソッド.左端はそのメソッドが返す値の型.voidは返す値が存在しないことを示す.

他の予め用意されているクラスの解説は,http://java.sun.com/j2se/1.3/ja/docs/ja/api/overview-summary.htmlから辿ることができます.

このようなドキュメントのことを,APIドキュメントと呼びます.(API = Application Program Interface).プログラマーでも全てのクラスやそのメソッドを覚えられるわけはないので,このようなドキュメントを眺めながらプログラムを書くことになります.

パッケージ

みなさんは,たくさんのファイルを整理するのにディレクトリを作成すると思います.同じように Java でもクラスはパッケージと呼ばれる単位で整理されています.

Javaの標準的なクラスはjava.lang というパッケージに含まれています(ディレクトリの場合は / で区切りましたが,パッケージの場合は . で区切ります).例えば String は正式には java.lang.String というクラス名になります.でも,毎回プログラム中で java.lang.String と書くのは大変なので,java.lang の下にあるクラスはいちいち java.lang をつけなくても良いことになっています.

java.lang 以外のクラス(例えば次で使う java.math.BigInteger クラス)を使う場合は,java.math.BigInteger と書いても良いのですが,やっぱりこれも面倒なので簡単に済ます方法があります.プログラムの先頭に,

import java.math.*;

と書くと,java.math パッケージの全てのクラスについて java.math. と書かなくても済むようになります.つまり,BigInteger と書くだけで java.math.BigInteger を意味するようになるわけです.

複数の import 文を書いてもかまいません.

オブジェクトとオブジェクトでないもの

Javaでは,基本型(int, long, float, double, boolean等)の変数はオブジェクトではありません.

それ以外の型(String等)は全てクラスになります.

まとめ

String以外のクラスも使ってみよう

java.math.BigIntegerクラスは,基本の整数型よりも大きな値を扱うためのクラスです.これを使って,階乗のプログラムを書いてみましょう.

まず,long型を使った場合のプログラムを以下に示します.


このファイルをダウンロード ■ UNIX用(EUC版) ■ Windows用(SJIS版)
(上のどちらかのリンクを右ボタンでクリックして「リンク先を名前をつけて保存」して下さい)

public class Factorial {
    public static void main(String[] args) {
	// n を入力する
	System.out.print("Input n: ");
	int n = Keyboard.intValue();

	// 結果を格納する変数を1にしておいて…
	long f = 1;
	// 1からnまでをfに掛ける
	for (int i = 1 ; i <= n ; i++) {
	    f = f * i;
	    System.out.println(i + "! = " + f);
	}
    }
}


BigInteger クラスの説明は http://java.sun.com/j2se/1.4/ja/docs/ja/api/java/math/BigInteger.html にあります.

オブジェクトを作るとき(newするとき)の引数の説明は,コンストラクタのところに書いてあります.コンストラクタに BigInteger(String val)というものがあるので,newするときにString型の文字列を与えることができることがわかります.

BigInteger クラスのオブジェクトを作成して,1 を代入するには以下のように書きます.

BigInteger bigf = new BigInteger("1");

BigInteger型のオブジェクトと int 型の値を直接乗算することはできないので,一旦 int 型の値を BigInteger 型に変換する必要があります.

int 型の変数 i の値を BigInteger 型に変換するにはどうするのでしょうか? bigf = i とは書けません.bigfは(BigInteger型のオブジェクトの場所を保持している)オブジェクト変数なので,関係のない int 型の値を入れることはできないのです.これはクラスメソッド valueOf を使って,以下のように書きます.

bigi = BigInteger.valueOf(i);

乗算はどうするのでしょうか? BigInteger は Java の基本型ではないので単純に * で乗算することはできませんAPIドキュメントを見ていくと,multiply というメソッドが用意されていることがわかります.

BigIntegermultiply(BigInteger val)
値が (this * val) の BigInteger を返します。

これを読み解くと,以下のような意味になります.

このメソッドは BigInteger 同士の乗算を行って,新しいBigInteger型のオブジェクトを返してくれることがわかります.

BigInteger biga = new BigInteger("1");
BigInteger bigb = new BigInteger("2");
BigInteger bigc = biga.multiply(bigb); // 1 * 2 を計算して bigc に入れる

結果の表示は心配しなくても,System.out.println(bigf) のようにして行うことができます.

さて,これで役者は揃いました.BigIntegerを使って階乗を計算するプログラム BigFactorial は次のようになります.


このファイルをダウンロード ■ UNIX用(EUC版) ■ Windows用(SJIS版)
(上のどちらかのリンクを右ボタンでクリックして「リンク先を名前をつけて保存」して下さい)

// java.math.BigInteger を使う
import java.math.*;

public class BigFactorial {
    public static void main(String[] args) {
	BigInteger bigf;
	
	System.out.print("Input n: ");
	int n = Keyboard.intValue();
	
	//f = 1;
	bigf = new BigInteger("1");
	
	for (int i = 1 ; i <= n ; i++) {
	    //f = f * i;

	    // int型の変数 i を BigInteger 型の変数 bigi に変換する.
	    // APIドキュメントを見るとvalueOfメソッドはlong型の引数を取るが,引数に
	    // int型を与えても大丈夫(int→longへの変換は自動的に行われる)
	    BigInteger bigi = BigInteger.valueOf(i);

	    // bigf = bigf * bigi を計算する
	    bigf = bigf.multiply(bigi);
	    System.out.println(i + "! = " + bigf);
	}
    }
}


課題6-1 (電卓)

StringクラスやBigIntegerクラスを使って,大きな整数を扱える四則演算電卓(BigCalc)を作ってください.

何回も計算できるようにしてください.(強制的に止めるにはコントロール-Cを押します)

値1は? 100
値2は? 200
計算の種類は?(+,-,*,/): *
100 * 200 = 20000
値1は?

余裕がある人は他の機能を追加してもかまいません.

1. ソースプログラム,2. 実行結果,3. 苦労したところや改善すべき点,感想 を書くこと.

ヒント1: まず,大まかな構造を考える(段階的詳細化)

ヒント2: 最初は,加算専用電卓から始める.

ヒント3: キーボードから入力する値は,long型で納まらないような大きい値を入れられるように一旦文字列で受け取る.計算するときは文字列からBigIntegerに変換する(ヒント4参照).

ヒント4: 文字列からBigInteger型のオブジェクトを作るには,以下のようにコンストラクタを使います.

String s = "12345";
// "12345" という文字列を 12345 を表す BigInteger のオブジェクトに変換
BigInteger bigs = new BigInteger(s);

ヒント5: 以下の2つのコンパイルエラーは,「BigIntegerクラスのコンストラクタにはそのような引数を取るものが存在しない]というエラーです.

BigCalc.java:XX: シンボルを解釈処理できません。
シンボル: コンストラクタ BigInteger  ()
位置    : java.math.BigInteger の クラス
                        BigInteger answer = new BigInteger();
(BigIntegerクラスには,引数なしのコンストラクタは存在しません)
    
BigCalc.java:XX: BigInteger(long) は java.math.BigInteger で private アクセスされます。
                        BigInteger answer = new BigInteger(10);

(BigIntegerクラスには,long型を引数とするコンストラクタは存在しません)

課題6-2 (APIドキュメント)

APIドキュメントを見て,授業で解説していないメソッド(なんでも良い)を少なくとも3つ使うプログラム ApiExercise を書いてください.使うクラスは何でもかまいませんが,きちんとAPIドキュメントを読むにはまだ解説していないことがたくさんあるので,String, Math, BigInteger等の授業で使ったクラスを使うのが無難でしょう.

使ったクラス,メソッド名と,そのメソッドが行うことを書いてください.

プログラムは特に意味がある必要はありません.

1. ソースプログラム,2. 実行結果,3. 苦労したところや改善すべき点,感想 を書くこと.