技術とか戦略とか

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

Excel:漢字項目の昇順検索で失敗する場合の対応方法(文字コード順にソート)

前に「ExcelのVLOOKUP関数の高速化」という記事を執筆しました。
検索のキー項目を昇順に並び替え、VLOOKUP関数のあいまい検索(二分検索)を使用することで、VLOOKUP関数を高速化できるという内容です。
 
以下では、漢字項目をキー項目として検索する場合の注意点を追記します。
 
-----------------------------
 
注意点としては、文字コード(SJIS)の昇順に並び替える必要があるということがあります。
通常はフィルタから並び替えれば文字コードの昇順になるのですが、漢字の場合は読み仮名の昇順に並んでしまいます。
そのため、漢字項目をフィルタから並び替え、その項目をキー項目として検索すると、検索に失敗(近似値を取得)してしまいます。
 
漢字を文字コードの昇順に並び替えて上手く検索できるようにするためには、以下の手順を踏む必要があります。
 
1.「データ」タブ→「並び替え」を選択
f:id:akira2kun:20190916173214j:plain
2.「オプション」を選択
f:id:akira2kun:20190916173244j:plain
3.「ふりがなを使わない」を選択

f:id:akira2kun:20190916173313j:plain
4.「列」を当該列、「並び替えのキー」を「値」、「順序」を「昇順」とする
f:id:akira2kun:20190916173338j:plain
5.「OK」を押下すると文字コードの昇順に並び替えられる
f:id:akira2kun:20190916173408j:plain

ソースコードにおける一貫したポリシーの重要性

ソースコードを読みやすくし保守性を高めるために、「GOTOは使わない方が良い」「クラスは小さく分けた方が良い」といったポリシーを良く聞きます。
しかし、これらのポリシーによりソースコードが読みやすくなるとは限りません。
例えば、「GOTOは使わない方が良い」というポリシーについては、制御構造を実現するためのフラグが増えてかえってソースコードが読みにくくなることがあります。「関数・パラグラフの最初と最後へのGOTOだけ許可する」という形で若干緩いポリシーにした方が個人的には好きです。
また、「クラスは小さく分けた方が良い」というポリシーについてもやりすぎは禁物で、あまりにクラスを細かく分けすぎるとソースコードの管理が大変になったり全体の構造を把握しにくくなったりします。
 
ソースコードを読みやすくする上で最も重要なのは、上記のような細かい話ではなく、ソースコードに一貫したポリシーがあるか、だと個人的には思っています。
ソースコードに一貫したポリシーがあれば、たとえ癖の強い(一般的なポリシーに則っていない)ソースコードであったとしてもそれなりに読みやすかったりします。
一貫したポリシーがあるソースコードには「予測可能性」のようなものがあると感じます。ソースコードを読んでいて「ああ、ビジネスロジックはきっとここにあるな」「インプット・アウトプットの定義はきっとここでしていな」といった具合で予測を立てながらスムーズにソースコードを理解することができます。
(これができていないソースコードは、たとえ自分が作ったソースコードであっても追うのに苦労します…)
 
例として、文章の書き方について考えるとわかりやすいと思います。
ある機関が発行している論文について、どの論文も「1.序論」「2.本論」「3.結論」となっていれば、「結論をかいつまんで把握したい場合は先に3(一番後ろのパラグラフ)を読む」といった形で予測を立てながら読むことができます。
しかし、論文によってポリシーがバラバラで、「1.序論」「2.本論」「3.余談」「4.結論」「5.後日談」のようになっている論文が混ざっていたら読みにくくてしょうがありません。結論が欲しい場合に、3を見ても一番後ろのパラグラフを見ても結論が書いてなく、わざわざ全体的に目を通す必要が出てきてしまいます。
 
一貫したポリシーを保つ上で、ネックになるのは複数人で開発する場合です。
複数人で開発する場合は、開発者毎でポリシーが異なるので、コーディング規約のような形でポリシーを定める必要があります。
コーディング規約がない、若しくは不十分な場合は、一般的に広まっているポリシー(例えば前述のgotoless文)に従ったり、既存のソースコードの書き方を真似ることが望ましいです。
オブジェクト指向のプログラム言語を使用しているのであれば、カプセル化や継承といった機能を使うことでポリシーに従っていないソースコードコンパイルエラーを起こさせることもできるので、一貫したポリシーに基づいたプログラミングが容易になります。

java8:関数型インターフェースの背景にある考え方

【前置き】
Java8から関数型インターフェースが使用可能になりました。
具体的に「ラムダ式」「Stream」「Optional」「Files」と言った方がわかりやすいでしょうか。
 
関数型インターフェースは関数型プログラミングをサポートするものであるため、従来からJavaでサポートされていたオブジェクト指向プログラミングとは発想が異なります。
Javaを使っている人は従来のオブジェクト指向プログラミング的な書き方には慣れていると思うのですが、関数型インターフェースには正直抵抗感を覚えると思います(私も慣れていないので抵抗感があります)。何をしているのかわからないという次元かもしれません。
しかし、関数型インターフェースの使用を半ば強制されるフレームワークが登場していたり(例:Apache Spark)、関数型インターフェースでJavaを書く開発者も見かけるようになってきたため、いつまでも関数型インターフェースから逃げるわけにもいかないというのが実情だと思います。
 
関数型インターフェースに抵抗感を覚えるのは、その背景にある関数型プログラミングの発想を知らないからだと思いました。
そこで、今回は、自分なりに理解した関数型プログラミングの考え方を紹介したいと思います。
 
【サンプルコード】
言葉で説明するよりも先にサンプルコードを見た方がわかりやすいと思うので、サンプルコードを先に紹介します。
年齢のリストから30代の人数を数える、というプログラムです。
ごく短いプログラムですので、お付き合いください。
 
・FunctionTest.java

import java.util.Arrays;
import java.util.List;

public class FunctionalTest {

    public static void main(String args) {
        Integer
age = {38, 26, 40, 32, 36};
        List<Integer> list = Arrays.asList(age);

        System.out.println("手続き型として記述");
        int count = 0;
        for (int i = 0; i < list.size(); i++) {
            int individualAge = list.get(i);
            if (individualAge >= 30 && individualAge <= 39) {
                count++;
            }
        }
        System.out.println(count);

        System.out.println("関数型インターフェースを使用");
        System.out.println(list.stream()
                               .filter(x -> x >= 30 && x <= 39)
                               .count());

    }

}
 
・実行結果
手続き型として記述
3
関数型インターフェースを使用
3
 
【関数型プログラミングの考え方】
関数型プログラミングでは、以下のことを実現しようとしています。
色々難しい用語(例えば「副作用」等)はあるのですが、今回は用語を使わずに簡潔にまとめます。
 
・内部状態(State)を排除する
 最も本質的な考え方です。
 
 関数型プログラミングでは、内部状態を排除することを目的としています。
 「内部状態」とは、上記のコードで言うと「count」や「i」を指します。
 
 内部状態が入りこんでしまうと、
 内部状態により関数の結果が変わってしまうため、
 内部状態を把握する必要が出てきてしまい、可読性が悪化します。
 (把握のために「count」や「i」をトレースする必要が出てきてしまう)
 把握しきれずに意図しないバグを出してしまうことも珍しくありません。
 内部状態を排除して、品質を上げよう、という発想です。
 
 また、コンピュータにとっては内部状態は重要ですが、
 人間にとってはやりたいことを実現できれば良く、
 内部状態は重要ではありません。
 重要ではない記述を削減することでコードを完結にしたい、
 という発想もあります。
 
 Java8のラムダ式では、ラムダ式の外部で定義された変数の値を
 ラムダ式の内部で変更することを禁止されています(コンパイルエラーになる)。
 その背景には、内部状態の排除があると思っています。
 
・自然言語に近い形で処理を記述する
 これは、コードが簡潔になった結果生じた副次的な考え方かもしれません。
 
 関数型プログラミングでは、関数を組み合わせることにより処理を実現します。
 関数を次々とつなぎ合わせるように記述することで、
 ソースコードが自然言語に近い形になります。
 わかりやすく言えば、ソースコード自体がコメントのようになります。
 
 例えば、サンプルコードでは
 「年齢のリストから30代の人数を数える」
 という処理を行おうとしています。
 従来のプログラミングでは、
 これを実現するためにforループとかカウント用の変数を使用しており、
 何をしているのか把握するためには、
 内部状態をトレースして意図を汲み取る必要があります。
 しかし、関数型プログラミングでは、
 「list.stream().filter(x -> x >= 30 && x <= 39).count()」→
 「listを30<=x<=39でfilterしてcountする」
 と読めるため、
 「年齢のリストから30代の人数を数える」
 という処理であることを自然に把握することができます。

情報処理技術者試験対策「稼働率の計算」

今回は、「稼働率の計算」について書きます。
情報処理技術者試験では頻出のテーマであり、また実務でもインフラ構成を考えるのに役に立つ考え方です。
 
いくつかの例を挙げて説明していきますが、便宜上、各機器の稼働率は0.9とします。
稼働率…機器やシステムが稼働している時間の割合。0なら全く稼働していない。1なら常に稼働している。)
 
・直列構成の場合
 各々の機器が全て動いている場合に全体として稼働します。
 各機器の稼働率を掛け合わせることで、システム全体の稼働率を計算可能です。
f:id:akira2kun:20190902233130j:plain

 この例では、以下のようになります。
  機器Aが稼働、機器Bが稼働→全体として稼働
  機器Aが稼働、機器Bが非稼働→全体として非稼働(機器Bで止まる)
  機器Aが非稼働、機器Bが稼働→全体として非稼働(機器Aで止まる)
  機器Aが非稼働、機器Bが非稼働→全体として非稼働
  
 機器Aが稼働かつ機器Bが稼働している確率は、
  0.9 * 0.9 = 0.81
 となります。
 
・並列構成
 何れかの機器が動いている場合に全体として稼働します。
 各機器の非稼働率と掛け合わせた値を求め、それを1から減算することで、
 システム全体の稼働率を計算可能です。
f:id:akira2kun:20190902233200j:plain

 この例では、以下のようになります。
  機器Aが稼働、機器Bが稼働→全体として稼働
  機器Aが稼働、機器Bが非稼働→全体として稼働(機器Aのみで稼働)
  機器Aが非稼働、機器Bが稼働→全体として稼働(機器Bのみで稼働)
  機器Aが非稼働、機器Bが非稼働→全体として非稼働(機器Aでも機器Bでも止まる)
 
 稼働率が0.9なので、非稼働率は 1 - 0.9 で 0.1 になります。
 機器Aが非稼働かつ機器Bが非稼働の確率は
  0.1 * 0.1 = 0.01
 となります。
 機器Aが非稼働かつ機器Bが非稼働以外であれば稼働状態なので、
  1 - 0.01 = 0.99
 がシステム全体の稼働率となります。
 
・混在している場合
 細かい部分から計算していくことで、システム全体の稼働率が求まります。
f:id:akira2kun:20190902233228j:plain

 この例では、点線部分の稼働率は前述の通り0.81です。
 点線部分と機器Cは並列構成のため、システム全体の稼働率
  1 - ( (1 - 0.81) * (1 - 0.9) ) = 0.981
 となります。
f:id:akira2kun:20190902233609j:plain

 また、この例では、点線部分の稼働率は前述の通り0.99です。
 点線部分と機器Cは直列構成のため、システム全体の稼働率
  0.99 * 0.9 = 0.891
 となります。
 
----------------------
 
目次

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

情報処理技術者試験対策「磁気ディスクの構成とアクセス」

今回は、「磁気ディスクの構成とアクセス」について書きます。
文章ではわかりづらいので図解します。
 
ITパスポートや基本情報処理技術者では頻出です。
 
また、フラグメンテーションデフラグは普通にPCを使っていても知っておいた方が良いことです。
それ以外で実務ではあまり意識する必要がないことだとは思いますが、メインフレームを扱う場合は磁気ディスクの構成を把握していないと容量計算で困ることはあります。
 

f:id:akira2kun:20190902232426j:plain
f:id:akira2kun:20190902232452j:plain

----------------------
 
目次

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

保守性の高いコードを作成するために心がけること

ソースコードは作って終わりではなく、その後何年、何十年にもわたって保守開発が行われます。
また、保守開発を行う開発者もその間に入れ替わります。
ソースコードを作る際は、このことを踏まえて高品質・低コストで保守開発ができるようにする必要があります。
 
難しい話をするとデザインパターンとかフレームワークとかの話になるのですが、以下のことは新人も含めて最低限心がける必要があります。
 
・適切な変数名やメソッド名を与える
 変数名・メソッド名を与える際は、
 その変数やメソッドが何をするのかわかるような名前にする必要があります。
 例えば、「a」や「hoge」といった変数名は不可で、
 「loopEndFlag」や「commodityCode」といった変数名にする必要があります。
 
 また、現場毎で命名規則が決められていることも多いので、
 それに倣った命名をする必要があります。
 命名規則を無視すると、
 他のソースコードと統一性が取れなくなり読みにくいソースコードになったり、
 影響分析等のためにキーワードで検索する時に引っかからなくなったりします。
 
・適切にコメントを記述する
 コメントを入れることで、その箇所で何をしているのかがわかりやすくなります。
 しかし、コメントを入れれば良いというものではなく、
 意味のあるコメントである必要があります。
 例えば、
 
 /* iが0~11の間処理する */
 for (int i=0;i<12;i++){
 …
 }
 
 といったソースをそのまま日本語にしただけのようなコメントは不可で、
 
 /* 1月~12月の売上高を計算する */
 for (int i=0;i<12;i++){
 …
 }
 
 といった業務的な意味を書く必要があります。
 
 また、ソースコードの先頭には
 「ソース名」「処理概要」「変更日」「変更概要」「変更者」
 といった情報を記述するのが一般的です。
 メソッドの先頭には
 「メソッド名」「処理概要」「引数」「戻り値」「出力され得る例外」
 といった情報を記述するのが一般的です。
 
 コメントの書き方も、現場毎で決まっていることが多いです。
 
・分かりやすいロジックを心がける
 保守開発の際にソースコードのロジックを読み解くことも多いので、
 if文のネスト(if文の中のif文)が多すぎる、
 goto文で制御があちこちに飛ぶ、
 といったロジックが分かりにくくなるような書き方も避けた方が良いです。
 また、if文やfor文等を使う際は、
 インデント(左側のスペース)を適切に入れて、
 構造が分かりやすくなるようにした方が良いです。
 (インデントの入れ方は各言語の入門書を真似れば良いです)
 
 新人の内はあまり気にする必要はありませんが、
 使用する文法も入門書に載っているものを中心にした方が無難です。
 自分の現場で広まっているなら良いのですが、
 そうでないのに新しい文法やマイナーな文法を使うと、
 他の開発者(特に新人)から見て理解しにくくなることがあります。
 
・ハードコーディングは原則禁止
 ハードコーディングとは、
 マスタデータをソースコードの中に持たせることです。
 例えば、
  
 /* 商品コード(野菜) */
 int syohinCodeVegetable = 1;
 
 /* 商品コード(調味料) */
 int syohinCodeSpices = 2;
 :
 :
 :
 
 のような書き方は不可で、商品コードの一覧を持たせたいなら、
 データベースやファイルに持たせてそこから取得するべきです。
 
 マスタデータは、追加や修正や削除が行われることがあります。
 マスタデータをデータベースやファイルに持たせていない場合、
 追加・修正・削除が行われる度に、
 ソースコードを修正しコンパイルする必要が出てきて保守工数が増加します。
 
 更に言うと、マスタコード読み込みのような色々なソースコードで使われる処理は、
 共通処理として別のソースコードに切り出すべきです。
 これをしないと、修正する際に修正漏れが発生する可能性が高くなります。
 大抵の場合、使うべき共通処理は現場毎やプロジェクト毎で決められているので、
 それに従う必要があります。
 
・仕様書とソースコードを合わせる
 仕様書には、ソースコードがどのような意図で作成されているのか、
 他のソースコードとどのような連携をしているのか、
 等の設計思想が記載されています。
 しかし、仕様書がソースコードと乖離していた場合、
 仕様書から調査する時に実際の実装を誤って理解してしまいます。
 そのことにより、保守開発でバグが生まれる原因になります。
 それを防ぐために、ソースコードが仕様書から乖離した時は、
 仕様書もソースコードに合わせて修正するべきです。

モチベーションを高める目標を作るのに必要な三者に対する視点

あくまでも私の経験に基づく持論ですが、モチベーションを高める目標を作るには、「自分」と「身近な人」と「社会」の三者に目を向ける必要があると思っています。
巷では目標管理制度や三年目研修のようなものが流行っていますが、この三者に目を向けようとしない限り、上から目標を押し付けられて終わり、モチベーションは上がるどころか下手したら下がる、という結果に終わってしまうのではないかと思っています。
 
--------------------------
 
三者に目を向ける」とは、具体的に言うと以下のようなことです。
 
1.その目標は自分にとってちょうど良い難易度か
例え、自社や社会から望まれることだとしても、○○さん(とても追いつけそうにないスーパーマン)のようになろうとしたら、現実味がわかずモチベーションは上がりません。これを他の人から望まれた場合は、「○○さんまで行かないと評価されないのか」と自信を失う原因にもなります。
かと言って、昔の自分でもできていた簡単なことをやろうとしても、頑張らなくても目標達成できるためモチベーションは上がりません。
今の自分でも少し頑張れば達成できそうな目標を打ち立てることが大事です。その目標こそが、自分に取ってチャレンジングに思えることのはずです。
仮に大きな目標があるとしても、できそうなことから一つ一つ積み上げていくことが大事です。
 
2.その目標は身近な人から評価されることか
「身近な人」とは主に職場の上司や同僚のことですが、家族や友人関係も含まれます。
要は「身近にサポートしてくれる人がいるか」ということです。
仮に「立派な技術者になりたい」と思っていたとしても、自分の職場が管理スキルばかり求められる職場で、家族や友人も「大企業で昇進できる人が偉い」という価値観なのであれば、誰からも評価されず、モチベーションの維持が困難になります。
(日本のSIerではこのようなことが起こりがちです)
このような状況はお互いに不幸なので、職場を変えるなり交友関係を見直すなりした方が良いでしょう。
(目標を周りに合わせて変える方法もありますが、それでは自分というものがなくなりモチベーションの維持が別の意味で困難になります。また、他人を変えるのも難しいので、説得に回るのも徒労に終わることが多いです。)
 
3.その目標は社会を幸せにするものか
仮に、自分にとってチャレンジングで、周りからも望まれている目標だとしても、その目標達成の過程で社会を幸せにできないなら、良心の呵責でモチベーションを維持できません。
目標を立てるのであれば、「この目標を達成することで社会も幸せにできる」という確信のようなものも必要です。
 
-------------------------
 
以下は私の例です。
 
私は、技術者としてシステムの安定稼働に貢献したいと思っており、その思いに即した目標を立てるようにしています。
そのための勉強は欠かさず行いたいと思いますし、ノウハウのようなものがあればそれを他の方にも伝えたいと思っています。
また、どうすればシステムを安定稼働させられるのか、一緒に考えていきたいと思っています。
 
正直な話、「システムの安定稼働」というのは地味で目立たない仕事ですし、「技術者」というのも上を見たらキリがありません。
もっと簡単に評価を得る方法はいくらでもあるので、昔はモチベーションを保つことができませんでした。
 
しかし、社会を幸せにすることを考えると簡単に評価を得るだけでは空しいと思い、結局「技術者としてシステムの安定稼働に貢献する」という所にたどり着きました。
また、今の会社で仕事をし、社長や上司や同僚と会話している内に、「技術者」であることや「システムの安定稼働」という仕事に対して誇りを持てるようになりました。システムを安定稼働させるのも立派な技術であると思えるようになりました。
 
今の私は、三者に目を向けた目標を立てることができているので、高いモチベーションで仕事をすることができていると自分でも感じます。
 
1.その目標は自分にとってちょうど良い難易度か
私が勤めている会社はまだまだ小さく、安定して仕事を回していくために色々とやらなくてはいけないことがある。
技術を売る仕事をしている以上、特に技術者の育成には力を入れなくてはならない。
昔勤めていた大手SIerのような整った研修制度を整備することはまだできないので、その制約の中で如何に技術者を育成するか、知恵を絞る必要がある。業界経験のある私だからこそできることがあると感じている。
 
2.その目標は身近な人から評価されることか
今の目標は、会社の社長・上司・同僚、家族、友人から評価してもらえることである。
また、私や一緒に仕事をする方々(同じ会社の人だけでなく、一緒に仕事をする他社の方々も含めて)がシステムの安定稼働に貢献することができれば、会社の売り上げも増やすことができる。
 
3.その目標は社会を幸せにするものか
システムの安定は社会の基盤を支えるものであり、この問いに対しては胸を張ってYESと言えるものである。