Aikの技術的な進捗部屋

技術的な進捗とか成果とかを細々と投稿するブログです。時々雑記も。

Javaで書く括弧検証プログラム その3

前前回の記事
前回の記事

括弧検証プログラムを作ろう その3

またまた学校の課題で括弧検証プログラムを作る事になりました。
今度はHTMLのコメント行(<!---->)、JavaC言語などのコメント行(/* */)などの、
「複数文字を一括りに括弧の様に扱うものでも対応出来る様に」すると言うもの。

また「後に対応する括弧が増えても、なるべく拡張性に富む様に。
理想は拡張のために1箇所のみを変更すれば良い様にする」とも告げられました。
折角だし、ユーザー入力で対応する括弧を拡張できたらなぁ…と思います。

そして、いつもの様にスクリプト言語は禁止みたいです。
まぁ前回前前回の様にJavaで行きましょうかね。

前回の課題時に、先輩のふつくしいソースコードを見れたので…。
それを参考にしつつ、良いソースコードを書いていきましょう!

複数のクラスを使って書こう!

先輩が書いたソースコードを見ると、
単一のクラスで全ての処理をするのではなく、複数のクラス同士を結びつけて処理を行わせていました。
しかもUML記法を用いたクラス設計図まであり…。
講義で学んだものはこう使えるのか!と凄く感動しました。

早速自分でも設計しちゃいましょう。
最終的には下記の様になりました。

BracketType
- typeN: String // 括弧の種類
- rawO: String // 対象の生の開き括弧データ
- rawC: String // 対象の生の閉じ括弧データ
Bracket
- type: BracketType // 括弧の種類
- rawD: String // 対象の生のデータ
- row: int // その括弧があった行数
- col: int // その括弧があった列数

それにしてもこうして書くとわかりやすいものなんですねぇ。
システム設計法的な講義でUML記法を学んでよかった。
それでは実装に向けてゴリゴリ書いて行きましょう。

今回のプログラムの全体の流れ

実装に向けてゴリゴリ書き始めている中、前回などの「ファイルから一文字ずつ参照して行く」方式では、
「複数文字から構成される括弧の対応を確認する」事自体がボトルネックになってしまいました。
2時間くらいウンウン悩んだ結果、これは一から考え直した方がいいなと思い…。
全体的な流れを今一度考えて見ることにしました。

具体的にはどこが問題点か?

今回問題点となった事は「ファイルから1文字ずつ参照すると次の文字を予測していかないといけない」と言う事です。
逆に言えば、それ以外は全く問題ないということ。

色々と考えた結果、最終的に「1文字ずつではなく1行で文字判定をする」事に落ち着きました。
この方式にすれば<!--が来た時に、「<の次に!が来たら…」と判定する必要はなくなりますしね。
ただ、今度は「複数文字列からどうやって目的の文字列を探し当てるか?」という問題に直面しました。

indexOfメソッドを使おう

これまたいろいろ探した結果Javaの文字列メソッドには、文字列から指定した文字列を取り出す「indexOf」というものがある事がわかりました。
つまり、1行ずつ読み込んだファイルの文字列から、「indexOf」メソッドで検証したい括弧を探せば…と思ったのですが。

ここでもまた問題点が2つ出てしまいました。
何とか解決は出来たので、その解決法も載せましたが…。
正直これがスマートな解決法かは分かりません…。
もし他の解決法等ありましたら、コメントしてくれると嬉しいです。

indexOfメソッドを使う問題点1: 行の先頭から検索ができない

従来は「1文字ずつ順番に文字列を検証していた」ので、
何も考えず閉じ括弧が来たらプッシュして、開き括弧が来たらポップして、と出来たのですが…。
今回の方法は「検証したい括弧を1行の中から探す」ため、
行の先頭から検索が出来ず、括弧を検知しながらプッシュポップをする事が不可能となってしまいました。

indexOfメソッドを使う問題点1-解決法: 括弧検知を全て終了→括弧対応検知を行う

解決法として「ファイル全ての括弧を抽出してからプッシュポップをしよう」と少し思い切った方法でやってみる事にしました。

全てのファイル内の括弧をBracket型のリストに1つ1つ追加していき、ComparatorとCollectorを使って行順と列順にリスト内をソート、
最後に前々回で考えた方法で括弧検証をする事に。

本当は括弧を検知しながらプッシュポップを…とする方がスマートだと思いますが、今回ばかりはしょうがないかもですね。

indexOfメソッドを使う問題点2: 検知した括弧を「検知済」と出来ない

当たり前ですが、1行の中に複数個同種の括弧が入っているパターンも考えられます。
このため、私は検索したい括弧の種類ごとにループを回せばいいと思ったのですが…。

indexOfはあくまで「検索するだけ」なので、元の文字列には何も変更を加えません。
これだとせっかく括弧を検知しても、ループさせてしまうとまた同じ括弧を検出してしまう事に…。

indexOfメソッドを使う問題点2-解決法: 自作関数の作成

こちらは関数を自作することで対応しました。
指定した文字を除外し、無関係な文字を入れた文字列を返すremoveStrという関数です。
この関数は今後も使いまわせそうですね。

参考までに、この関数のソースコードを貼っておきます。

最終的なプログラムの流れ

色々とまとめた結果、最終的にはこんな形に収まりました。

  • 専用クラス: Bracket、BracketTypeを定義
  • 検証する括弧の種類をBracketType型で定義
  • 括弧検証したい任意のファイルから文字列を1行ずつ読み込み
  • 各1行に対してその括弧が含まれているかを検索
    • 該当部分がある: その括弧をBracket型として定義し、リストに追加(検査した文字列はremoveStrで検査済み文字列に変換)
  • リストを行番号と列番号でソート
  • スタックを用いて括弧対応検証

後は書くだけです。ゴリゴリ書いていきましょう。

書きながら気づいた事

プログラムを書く時に気づいた、ちょっとした小ネタなどを記します。

クラスについて改めておさらい

折角クラスを使うのですから、Javaのクラスという概念について改めておさらいしてみる事に。

クラスとは:
プログラムを実行するための処理をまとめたオブジェクトで、クラスの処理の中にはメンバ変数や処理を実行するためのメソッドがあります。
メンバ変数とはクラス内で使用する変数のことで、メソッドとはメンバ変数などを使って行う処理をひと塊にまとめたものです。
参考記事

オブジェクト=部品、メソッド=手段/方法と考えれば分かりやすいでしょうか。
もう少しちゃんと言うなら「クラスとはプログラムの任意の処理を行える部品」…と言う感じでしょうか。
C言語とかにも外部ファイルを読み込んで〜とありますし、似た様なものでしょうか。

関数とメソッドの違い

これは別にJavaに限った話ではなく、他の言語にもありますけどね。
自分としては

  • 関数: 単独で実行できる処理の集まり
  • メソッド: オブジェクトに対する手段(このオブジェクトにこの動作をしてね、とか)/オブジェクトの後に.(ドット)をつけて記述する

くらいの認識だったのですが…。
本当にこの認識でOKなのか調べてみました。

結論からすると「大体あってる」という感じです。
参考までに、IT用語辞典に載ってるこれらの情報を載っけておきます。

関数とは: 引数と呼ばれるデータを受け取り、定められた通りの処理を実行して結果を返す一連の命令群。
多くのプログラミング言語では、関数がプログラムを構成する要素となっている。
多くの言語や処理系では、開発者の負担を軽減するため、よく使う機能が関数としてあらかじめ用意されている。
参考記事

メソッドとは: 方法、方式、手法、やり方、などの意味を持つ英単語。
ITの分野では、オブジェクト指向プログラミングにおいて各オブジェクトに属する処理や操作のことや、
通信プロトコルにおける要求の種類などのことをメソッドということが多い。
一般の外来語としては、一定の形式として確立した奏法、教授法、指導法、その他様々な技法のことを「○○メソッド」のように言う。
参考記事

オブジェクト指向プログラミングにおけるメソッドについて:
オブジェクト指向ではデータと手続きをオブジェクトとして一体化(カプセル化)して定義、利用する。
この、オブジェクトに内包された手続き(データに対する処理内容を記述したプログラム)のことをメソッドという。
言語によっては「メンバ関数」などということもあるが、ほぼ同じ機能を表す。
メソッドはそのオブジェクトに対する操作内容の詳細が実装されており、外部からメソッドを呼び出して起動することにより、その内容が実行される。
操作の詳細をオブジェクト内部に隠蔽することができ、プログラムの再利用性や生産性を高めやすくなると言われている。
参考記事

privateとpublicについて

以前から気になってた、"private"と"public"の違い。
この際なのでガッツリ調べてみました。

まずこれら二つは「アクセス修飾子」というもので、
クラス、インタフェース、メソッド、コンストラクタ、変数に対してこれを付けることが出来るとの事。

そしてアクセス修飾子というものは、

「指定した変数やクラスなどを、どの範囲から参照可能かのスコープを制御する」のに用いられるもの
参照記事

だそうです。
また、このアクセス修飾子には"private"と"public"の他にも"protected"と言うものもあるらしく、
それぞれの違いは下記のようになっております。

アクセス修飾子 概要 自ファイル 他ファイル
自クラス サブクラス 他クラス サブクラス 他クラス
public すべてのクラスからの参照を許す
private 自クラスからのアクセスしか許さない × × × ×
protected 他ファイル・他クラスからのアクセスをプロテクトする ×
なし × ×

参照記事

完成です!

いつものようにソースコードを貼り付けておきます。
※長ったらしいので折りたたんでおきました

余談

他の方のプログラムを見ると、正規表現やMapを用いてとってもスマートに完成させておられました。
もう括弧検査プログラムの改良課題はありませんが、個人的にものすごく気になる分野でもあるため、
ぜひJava正規表現について学んで見たいと思います。

学べば学ぶほど、学びたい事が出てくる!