技術とか戦略とか

IT技術者が技術や戦略について書くブログです。

実務で良く見かけるループ処理

実務で使われるプログラム、特にバッチ処理では、ループ処理は頻出です。
ループ処理には、良く使われるパターンがいくつかあります。
そのパターンを覚えておけば、既存のソースコードの理解がスムーズになります。
 
今回の記事では、実務で良く使われるループ処理のパターンを挙げていきます。
 
1.二重ループ
ループの内側にループがあるというのは良くあるパターンです。
javaでサンプルを書くと以下のようなパターンです。
 
public class Main {
    public static void main (String[ ] args){

        int arraySizeX = 3;
        int arraySizeY = 3;
        int array[ ][ ] = new int[arraySizeX][arraySizeY];
        boolean endFlag = false;

        array[0][0] = 1;
        array[0][1] = 2;
        array[0][2] = 3;
        array[1][0] = 4;
        array[1][1] = 5;
        array[1][2] = 6;
        array[2][0] = 7;
        array[2][1] = 8;
        array[2][2] = 9;

        int i = 0;
        while (!endFlag) {
            int j = 0;
            while (!endFlag && j < arraySizeY) {
                System.out.println(array[i][j]);
                if (array[i][j] == 5) {
                    endFlag = true;
                }
                j++;
            }
            i++;
            if (i == arraySizeX) {
                endFlag = true;
            }
        }

    }
}
 
実行結果は
 
1
2
3
4
5
 
です。
 
何をしているのかと言うと、
・2次元配列に格納した値を順番に取り出している
・ただし、5を取り出したらその時点で処理を終了する
ということをしています。
(実務では、1次元目が親項目、2次元目が子項目のことが多いです)
 
このパターンのループの場合、原則として、内側のループの終了条件は外側のループの終了条件を内包しています。
今回の例で言うと、
・内側のループの終了条件…!endFlag && j < arraySizeY
・外側のループの終了条件…!endFlag
となっています。
 
このような場合、内側のループにしかない終了条件に着目すると、それぞれのループで何をしているのか把握しやすくなります。
今回の例で言うと、内側のループにしかない終了条件として「j < arraySizeY」があります。
この終了条件に着目することで、内側のループでは2次元目の配列を順番に読んでいる、ということを把握することができます。
 
2.リトライ処理
タイミングによって失敗する可能性がある処理を行う場合、失敗してもすぐに異常終了しないように、その処理が失敗した際にリトライをかける制御を入れることがあります。
例えば、通信やデータベースアクセス等で良く使われる制御です。
 
フローチャートで言うと以下のようになります。
f:id:akira2kun:20190821211459j:plain

 
「処理が失敗した時だけループを継続する」というロジックになっていたら、このパターンである可能性が高いです。
 
なお、今回のフローチャートでは省略していますが、無限ループで負荷がかからないように、ループ時にスリープを入れたり、ループ回数を設定したりすることも多いです。
 
3.先読みRead
レコードの中身を見てループ継続条件を判断する場合は、1レコード目のみループの前に先読みして、2レコード目以降はループの中で読み込む、ということをします。
 
フローチャートで言うと以下のようになります。
(EOFの場合の処理は省略しています)
f:id:akira2kun:20190821211648j:plain

 
派生パターンとしては、コントロールブレイクやマッチング処理があります。
詳しくは以下のページを参照してください。
 
・コントロールブレイク

https://cyzennt.co.jp/blog/2019/05/18/%e3%82%b3%e3%83%b3%e3%83%88%e3%83%ad%e3%83%bc%e3%83%ab%e3%83%96%e3%83%ac%e3%82%a4%e3%82%af%e3%81%ae%e3%83%ad%e3%82%b8%e3%83%83%e3%82%af/

 
・マッチング処理

https://cyzennt.co.jp/blog/2019/06/01/%e3%83%9e%e3%83%83%e3%83%81%e3%83%b3%e3%82%b0%e5%87%a6%e7%90%86%e3%81%ae%e3%83%ad%e3%82%b8%e3%83%83%e3%82%af/

 
なお、ファイルの場合はReadですが、SQLのカーソルの場合はFetchです。どちらにしても制御としては同じです。

unix/linux:perlの複数命令を1行のコマンドで実行する(例:文字列のバイト位置走査)

表題の通り、unixlinuxでは、perlの複数命令を1行のコマンドで実行することができます。
-eオプションによりコマンドライン上で実行可能となり、1つ1つの命令を ; で区切ることで複数命令を記述可能となります。
 
perlはファイル操作や正規表現に優れているスクリプト言語であるため、コマンドライン上でperlを駆使することができれば作業の幅が広がります。
 
以下は、ファイルの中から特定の文字列のバイト位置を走査する例です。
ファイルの先頭で見つかった場合は0、次のバイトで見つかった場合は1、…といった具合で値が返ります。
見つからなかった場合は-1が返ります。
(例えば、改行コード無しのファイルで特定のデータをcutで除外したい時に、除外する位置を確認するのに使えます)
 
#> cat hoge.txt
#> abcde
#> perl -e 'my $str; my $pos; $str = `cat hoge.txt`; $pos = index($str,"a"); print "$pos\n";'
#> 0
#>
 
-------------------------
 
ちなみに、perl -eには便利な追加オプションがいくつもあります。
(標準入力(インプット)を1行1行処理、改行の強制付与、等)
perl ワンライナー」で検索すると参考になるページが出てきます。

情報処理技術者試験対策「RDBMSのインデックス」

RDBMSの性能分析を依頼された、という個人的な理由で、RDBMSのインデックスについて記事にしていきたいと思います。
 
今回の記事では、情報処理技術者試験の出題範囲内で、要点を箇条書きで書きたいと思います。
実務で使うには+αの知識(主にRDBMS固有の知識)が必要になりますが、情報処理技術者試験の知識はその+αの知識を身に付ける上での土台になります。
 
----------------------
 
■インデックス全般の知識
 ・インデックスは、テーブルの検索を高速化する目的で用いる。
 ・インデックスは、検索条件として指定するカラムに対して設定する。
 ・テーブルのレコード数が少ない場合は、インデックスの効力が落ちる。
  (インデックスを使わずに上から愚直に走査した方が早い場合もある)
 ・インデックスには、レコードの挿入や更新や削除が遅くなるデメリットがある。
  (それらの操作を行う度にインデックスの再構成が発生するため)
  
■インデックスの種類と特徴
 ①B+木インデックス
  ・RDBMSで一般的に用いられるインデックスである。
   (情報処理技術者試験で「インデックス」と出たら、指定がない限りこれ)
  ・検索キー値を用いて二分検索を行うインデックスである。
  ・検索値が等しく分布している時に効果が高い。
 
   効果が高い例
   カラムの値,件数
   a                ,200
   b                ,200
   c                ,200
   d                ,200
   e                ,200
   f                ,200
 
   効果が低い例
   カラムの値,件数
   a                ,1200
   b                ,200
   c                ,0
   d                ,0
   e                ,0
   f                ,0
   
  ・少ない件数に絞り込めるカラムに設定すると効果が高い。
  
   効果が高い例
   一意な値が格納されるカラムに設定する(1件に絞り込める)
   
   効果が低い例
   カラム「性別」に設定する(半分ぐらいまでしか絞り込めない)
   
  ・BETWEEN句等の範囲検索で後述のビットマップインデックスより優れる。
  ・ORDER BY等のソートが必要な場合に優れる。
  ・NULL検索やNOT検索では効果を発揮できない。
  
 ②ビットマップインデックス
  ・検索キー値が持ち得る値をビットで保持し、それをレコード毎に保持する。
  ・持ち得る値が少ないカラムに設定するとB+木インデックスより効果が高い。
   (例えばカラム「性別」等。)
  ・AND/OR条件のみの検索ではB+木インデックスより効果が高い。
  ・NOT検索ではB+木インデックスより効果が高い。
  
 ③ハッシュインデックス
  ・検索キー値をハッシュ化したものをインデックスとして用いる。
   (ハッシュ化…特定のアルゴリズムにより不可逆の固定長の値にすること)
  ・一意な値を検索するのに向いている。
  ・データ量が増えても検索にかかる時間が変わらないと言われている。
   (正確には、データ量が増えるとハッシュ値の衝突が増え、時間が微増する)
  ・ワイルドカードを用いて検索する場合はB+木インデックスの方が良い。
  ・BETWEEN句等の範囲検索には適用できない。
  ・ORDER BY等のソートが必要な場合には適用できない。
 
----------------------
 
目次

https://1drv.ms/b/s!AivF3bzWXOzuhG1Xk5hscKYqkLkM

プログラムによる小数点以下の計算で誤差が生じる原因&対処法2選

プログラムで小数点以下の計算を行う際、誤差が生じることがあります。
金額計算を行う時はこの誤差が即障害に繋がるので、誤差が生じないように実装する必要があります。
 
今回の記事では、誤差が生じる原因とその対処法を2つ挙げていきたいと思います。
 
1.浮動小数点型の丸め誤差
丸め誤差とは、小数点以下の数を2進数で表現できない(近似値を使わざるを得ない)ことにより発生する誤差です。
浮動小数点型(javaで言うとfloat型やdouble型)の変数を使用する際に、この問題が発生することがあります。
浮動小数点型を使用すると、例えば「0.3」が「0.29999999…」になったりするので、小数点以下の誤差が許されない場合には浮動小数点型を使用するべきではありません。
 
最も簡単な対処法は、整数型(javaで言うとint型等)で計算できるように、ファイルやテーブルの数値項目の単位を変えるという対処法です。
例えば、「金額項目は0.01円単位とする」という設計とすれば、「1.01円」を「101」と表すことが可能となり、整数型で計算できるようになります。
 
また、プログラム言語が任意精度型(javaで言うとBigDecimal型)の変数を用意している場合は、その型を使用することで丸め誤差の発生を防ぐことができます。
なお、COBOLの場合は丸め誤差が発生しないので、COBOLで小数点以下の項目を定義する場合は任意精度型であると考えて良いです。
 
2.中間結果を格納する領域の桁数不足による切り捨て発生
複数の四則演算を行って最終的な結果を得る際、中間結果を格納するための領域が必要になります。
もちろん、その領域が自分で定義した整数型の変数だったりすると、その時点で切り捨てが発生し、誤差が発生してしまいます。
 
このようなケースはレビューをすれば一目瞭然なのであまり心配はいらないのですが、問題なのはその中間結果がコンパイラにより暗黙的に用意される場合です。
具体的に言えば、COBOLでCOMPUTE文を使うような場合に問題になります。
基本的には、乗算を除算よりも先に行う、というのが誤差を防ぐための方法になりますが、中間結果の桁数の仕様を把握した上でそのような対処法を採用するのが望ましいです。
中間結果の桁数はコンパイラを用意しているベンダー毎で異なるので、詳しくはベンダーが用意しているマニュアル等で調べる必要があります。
 
例えば、COMPUTE文の中間領域が小数点以下0桁(コンパイラが暗黙的に設定)、最終結果を格納する領域が小数点以下0桁(プログラマが明示的に定義)である場合、以下のような計算結果になります。
 
・正しい計算
  123456円の消費税8%
 →123456円 * 1.08
 →133332.48円
 →133332円
  (小数点以下切り捨て)
 
・COMPUTE文で問題のある計算順
  123456 / 100 * 108
 →1234 * 108
  (123456 / 100 の結果を中間領域に格納する際、下2桁が意図せず失われる)
 →133272
 
・COMPUTE文で問題のない計算順
  123456 * 108 / 100
 →13333248 / 100
 →133332
  (13333248 / 100 の結果を最終結果の領域に格納する際、下2ケタを切り捨てる)

システム開発現場の間違い探し

システム開発現場の間違い探しを表現するとこんな感じです。
 
問:左右の絵に違いはありません。

f:id:akira2kun:20190804045732j:plain

ペーパーの試験や既にバグが顕在化したシステムとは異なり、システム開発現場では間違いがあることが明らかではありません。
むしろ、「○○さんのレビューを通したからこの仕様は正しい」「カバレージを100%通したのでテスト結果は正しい」といった、先ほどの問題のような「左右の絵に違いはありません。」という情報で溢れています。
(ちなみに、先ほどの問題は、左手にデバイスを持っているかどうかの違いがあります。)
 
この業界では「動かなければバグ、動けば仕様」という言葉がありますが、異常終了で止まらない限り、「仕様なのかバグなのか」は誰かが判断する必要があります。
お客様から指摘される前にこれを判断するためには、「左右の絵に違いはありません。」という類の情報にも自分なりの視点で疑いの目を向ける必要があります。
このようにして開発者がバグを見つける気がなければ、お客様から指摘されるまでバグは発見されないことになります。
 
三者から見ると「なぜこんな単純なバグが見つからないのか」と思えることが良くあります。
直近では、7payの件がそうでしょうか。
しかし、当事者にとっては決して「こんな単純なバグ」ではありません。お客様に実際に迷惑をかけるまで、「こんな単純なバグ」は「左右の絵に違いはありません。」です。
これを事前にバグとして見つけるためには、知識や経験が豊富で何が正しくて何が間違っているのかを自分で判断できる開発者が、システム全体を見渡せるポジションに立ち、チェック(レビューやテスト)を何重にも重ねて見つけるしかありません。
そのバグを上位者に伝え、修正する方向に持っていくコミュニケーション能力も必要になります。
 
私もいち開発者として、何が正しくて何が間違っているのかを自分で判断できるように、日々研鑽を重ねていきたいです。
 
---------------------
 
イラストはいらすとやの以下のページより引用しました。

https://www.irasutoya.com/2019/04/blog-post_553.html

エラー解消系記事の必要性について

このブログでは、出てくるエラーとその解消法を、エラー解消系の記事として記事化することがあります。
(マニュアルのエラーメッセージ一覧の一項目をそのまま記事にするような形です)
直近では、TeraTermの文字化けやjspのforEachについて記事化しました。
 
あまりの中身の無さにこれを記事にして良いのかと自問自答することもありますが、それでも記事にする意味はあると思っているので、そのことを以下で書いていきます。
 
-------------------------
 
実務や学習でプログラミングをしていると、エラーはつきものです。
勝手知ったる言語であれば「ああ、あれね」って感じですぐに対処できるのですが、経験の浅い言語を触っている時はエラーの解消に時間がかかります。
特にプログラミング全般の知識や経験が浅い場合は顕著です。
 
エラーの原因と解消法がすぐにわからない時は、藁にもすがる思いでグーグル等でWeb検索すると思います。
その結果、エラーとその解消法がそのままズバリ書いてあるページにヒットすれば、その解消法を試して次に進むことができます。
しかし、そのようなページにヒットしないと、初心者向けの解説やマニュアル等を読んで、何が足りないのかを調べて…という作業が必要になってしまいます。
自力でエラー調査を始めると、プログラム経験者でも半日かかることは珍しくありません。
 
正直、プログラムのエラー解消はあまり生産的な作業ではありません。
実務でエラー解消に時間を使いたくないのはもちろんのこと、学習でも本来学びたいことに時間を割けなくなりますし、プログラミング初心者の場合は挫折の原因にもなりかねません。
(エラーの原因と解決法を探る中で、言語仕様の理解が深まることもあるので、全くの無駄とまでは言いませんが)
 
上記のような背景があるので、私個人の基準としては、自分が出くわしたエラーメッセージや事象をgoogleで調べて、解消法をズバリ掲載したページがトップ10に出てこなければ、記事化することにしています。
 
-------------------------
 
少しいやらしい話をすると、私のブログでは、エラー解消系の記事は1記事あたり1日2~3アクセス程度あります。
アクセス数としては微々たるものですが、エラー解消系の記事は300~500文字もあれば1記事書けてしまいますし、知識や経験が浅い方でもエラー解消系の記事であればとっつきやすいと思うので、その割には悪くないアクセス数だと思います。
100記事も書けば1日あたり200~300アクセスになるので、アクセス数を稼ぎたい方にとってはなかなか馬鹿にできない数字だと思います。
また、単にアクセス数を稼げるだけでなく、確実に誰かの役に立つので、その意味でも悪くないと思います。

jsp:foreachが上手く動かない(表示できない)時の対処法→ライブラリ定義忘れかも

ググってもそのままズバリの答えが出てこなかったので、記事にします。
--------------------------
JSTLのcoreタグライブラリを使用している時に、
 
<tr>
    <th> </th>
    <c:forEach var="obj1" items="${time}">
        <th><c:out value="${obj1.time}"/></th>
    </c:forEach>
</tr>
 
こんな感じでforEach文を使っても何も表示されないことがありました。
 
理由は単純で、jspの先頭に
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
を入れていなかったためでした。
 
taglib文はライブラリの定義であり、この定義でライブラリを取り込まないとJSTLのcoreタグライブラリは使用できません。
また、「prefix="c"」で指定した文字cは、「c:forEach」の文字cに対応しています。
 
詳しくは以下のページが参考になります。
 
JSTL coreタグライブラリの利用

https://qiita.com/sculptcat/items/53d1a3a2d3b973354085