プログラミング奮闘記

難しいと評判!初心者がN予備校でプログラミングを学んだ感想と備忘録!
No.20:ボット作成

N予備校 プログラミングコース 第20回

あなたは2019年の目標って立てましたか?
どんな目標を立てたか覚えていますか?

私は2018年末に2019年の目標を立てました。
その一つにプログラミングを学び仕事として使えるようにすることをあげました。
そして、思い立ったが吉日と「N予備校のプログラミングコース」を申し込みました。

そして受講と同時に自身の振替と、これから始めようと考えている人の参考になればとブログを書き始めました。
今回ブログの移設にともない、再度そのブログを読み返しながら、今思うことを加えて記載していきたいと思います。

また私が受講したのは【2018年度】で、現在公開されている【2019年度】との違いについても書いていきたいと思います。

このブログは、初心者が学びながら書いているため、間違っている場合があります。
分かり次第修正していくつもりです。

そのあたりも含め楽しんでいただければと思います。 

最後に進めていてわからなかったところや気になったところをまとめています。

プログラミング入門 webアプリコース 第20回

 今回は、チャットコミュニケーションツールであるSlackで動かすことのできるボットをHubotを使って作成します。

  • 今回の内容まとめ(第3章 07)
    ・モジュールについて
    ・CRUDについて
    ・ボットのプログラム作成
    ・ボットのテストプログラム作成

  • 今回の目標
    プログラム作成に必要なCRUDの概念を学び、ボット処理をnpmのモジュールとして作成する

  • 今回新しく扱っているコマンド等
    ・Array.from
    ・filter
    ・assert
    ・equal
    ・deepEqual

モジュール(module)について 

モジュールとは、全体が交換できるようになっている部品などの集まりの事です。

たとえば、スマホであれば「画面」「電池」「基盤」等がモジュールにあたります。
画面が割れたときにスマホ全体ではなく画面を交換します。
ソフトウェア開発でも、1つのモノとして作成するのではなく、それぞれの機能ごとに作成することで改善が容易になります。
不具合が生じた時もすべてを変更するのではなく、不具合を生じた機能だけを解変更することが可能です。
このそれぞれの機能に分けることをモジュール化と呼んでいます。

ボットも、それぞれの機能ごとに作成します
その際に、要件に抜け漏れがないかをチェックすることが需要になります。
その時に利用するのが、CRUDという概念です。

ボットの要件の抜け漏れをチェック(CRUDの概念)

CRUD(クラッド)は、Create(生成)・Read(読み取り)・Update(更新)・Delete(削除)の頭文字をとったものです。
ソフトウェア開発の際、CRUDが揃っているかをチェックすることが重要になります。

今回作成するソフトウェアに必要な要件をCRUDに当てはめると以下になります。

  • Create :タスクの作成(todo)
  • Update :タスク状態の更新(done)
  • Read   :未完了のタスク状態の読み込み(list)
  • Read   :完了のタスク状態の読み込み(donelist)
  • Delete :タスクの削除(del) 

このCRUDに漏れがないことを確認しながら開発を進めます。

このCRUDに要件漏れがないかをチェックする方法として、「オブジェクトのライフサイクル」のチェックがあります。
 

オブジェクトのライフサイクル

ここでは、データのまとまりのことをオブジェクトと言い、そのデータがどのように処理され、どう変化するのかを表したものをライフサイクルと呼びます。
このオブジェクトのライフサイクルを確認することで、開発するプログラムの一連の動きを確認することが出来ます。
この一連の動きを図で表したものをステートステートチャート図と言います。

<ステートチャート図>

 

上記を確認すると、一連の流れが満たされているため要件がそろっていることが確認できます。
今回は、完了から未完了に戻す操作がありません。
それは削除してから再度作成する過程を踏むことで対応しています。

ボットのプログラム作成

CRUDの要件を満たしながらボットのプログラムを作成します。

todoタスクの設定(C:Create)

CRUDのC(Create)にあたるtodoコマンドにより処理する内容をプログラムします。

let tasks = new Map();
function todo(task){
tasks.set(task, false);
}
module.exports = {
todo: todo,
};
1行目で、連想配列のMapをtasksという変数に設定し、
2~3行目で、tasksに[task, false]という配列を組み込みます。tasksの配列のkeyには変数taskの内容、valueにはタスクが完了しているかの真偽値(完了していればtrue)を設定しています。
5~6行目で、todoという関数をパッケージの関数として公開しています。

listコマンドの実装(R:Read)

CRUDのR(Read)にあたる未完了のタスクの一覧を表示するlistコマンドをプログラムします。

今回作成するlistコマンドには、Array.from関数とfilterを使用します。

Array.from関数

Array.from関数は、連想配列のMapをkeyと値で構成される要素2つの配列に変換します。

var D = new Map();
D.set(‘あ’, ‘apple’);
return Array.from (D)
1行目で、連想配列のMapをDという変数に設定し、
2行目で、Mapのキーが「あ」、値が「apple」と設定し
3行目で、[ [ あ’, ‘apple’] ]という配列に変換されます。

filter

filterでは、与えられた関数の結果がtureである時だけ、その配列の要素を選別した配列を作成します。

.filter(function(n) {return n %2 === 0});
2で割った時の余りが0になる要素だけを選別することになるので結果は、

[2, 4]

と表示されます。

尚、アロー関数で書くと

.filter(n => n %2 === 0)
上記でも同じ結果が得られるようです。

 
このArray.from関数とfilterを使用してlistコマンドを作成します。

function isDone(taskAndIsDonePair){
  return taskAndIsDonePair;
}
function isNotDone (taskAndIsDonePair){
  return !isDone(taskAndIsDonePair);
}
function list() {
  return Array.from(tasks)
     .filter(isNotDone)
     .map(t => t[0]);
}
module.exports = {
  list:list
};
1~2行目で、配列を受けとりその二番目([0]が一番目)の値(今回であればtrueかfalse)を取得しています。その動作を、isDone(taskAndIsDonePair)と名付けています。

4~5行目で、isDone(taskAndIsDonePair)で取得されたもので結果を入れ替えたもの(trueはfalseに、falseはtrueに置き換えられます)を取得しています。その動作を、isNotDone(taskAndIsDonePair)と名付けています。

!は否定を意味するので、trueはfalseに、falseはtrueに置き換えられます。

8行目で、連想配列のkeyとvalueを配列に変換し

9行目で、isNotDone(taskAndIsDonePair)の結果がtrueである配列(isDone(taskAndIsDonePair)の値がFalseであった配列)だけを取得します。つまり、isDone(taskAndIsDonePair)の値がFalseだった配列が、isNotDone(taskAndIsDonePair)でtrueに変換され、そのtrueに変換された配列だけを取得します

この部分はES6のアロー関数で書かれているので混乱しやすいと思います。この部分をES5以前の書き方にすると、

.map( function(t) {
return t[0]
});

これをES6のアロー関数の書き方にすると、

.map( (t) => {
return t[0]
});

さらに、return文を省略すると最後の値が自動的に戻り値になるという決まりに従うと、

.map( (t) => t[0]);

加えて、引数の()も省略して最終的に
.map(t => t[0]);

最後に12~13行目で、listという関数をパッケージの関数として公開しています。

ここで、todoコマンドとlistコマンドをまとめたものが以下になります。 

const tasks = new Map();
function todo(task){
  tasks.set(task, false);
}
function isDone(taskAndIsDonePair){
  return taskAndIsDonePair;
}
function isNotDone (taskAndIsDonePair){
return !isDone(taskAndIsDonePair);
}
function list() {
  return Array.from(tasks)
     .filter(isNotDone)
     .map(t => t[0]);
}
module.exports = {
  todo:todo,
  list:list
};
 

doneコマンドの設定(U:Update)

CRUDのU(Update)にあたる完了状態にするdoneコマンドを作成します。

function done(task){
  if (tasks.has(task)){
    tasks.set(task, true);
  }
}
module.exports = {
done: done,
};
1-3行目で、連想配列にtaskがキーとして登録されていれば、完了状態をtrueに変更します。
6-7行目で、doneという関数をパッケージの関数として公開しています。

donelistコマンドの設定

こちらはlist関数のfilter条件が真偽で反転したものです

function donelist(){
return Array.from(tasks)
.filter(isDone)
.map(t => t[0]);
}
module.exports = {
donelist: donelist,
};
2行目で、連想配列のkeyとvalueを配列に変換します。

3-4行目で、isDone(taskAndIsDonePair)の結果がtrueである配列を取得します。

6-7行目で、donelistという関数をパッケージの関数として公開しています。

delコマンドの実装(D:Delete)

CRUDのD(Delete) にあたるdelコマンドを作成します。

function del(task) {
tasks.delete(task);
}
module.exports = {
del: del
};
2行目で、連想配列からtaskの文字列のキー情報を削除します。

5行目で、delという関数をパッケージの関数として公開しています。

 

ボットプログラムのテストの実施

作成したボットのプログラムを動作させるためのプログラムを作成します。

今回プログラムを作成する際に使用するのが、Node.jsのモジュールassertです。モジュールassert内のequalを今回は使用します。 

assert

const assert = require(‘assert’);
モジュールassertを呼び出して、その変数をassertとしています。

equal

assert.equal(1,1);
assert.equal(, );
1行目は、1と1は同じ数字の値であるのでエラーは出ません

JavaScriptでは、==演算子での比較は同じオブジェクト自身でないと等しいとは判断されません。ですので2行目は、1つ目のと2つ目のでは同じオブジェクト自身とみなされずエラーが出てしまいます。

そこで、利用されるコマンドが次です。

deepEqual

deepEqualは、与えられたオブジェクトの配列や中身まで比較するequal関数です。

ですので、

assert.deepEqual(, );
このコマンドだと、エラーが発生しません。

 
以上のコマンドを使用して、ボットのプログラムを動作させるためのプログラムが以下になります。

const todo = require(‘○○’);
const assert = require(‘assert’);

//todoとlistのテスト
todo.todo(‘ノートを買う’);
todo.todo(‘鉛筆を買う’);
assert.deepEqual(todo.list(), [‘ノートを買う’, ‘鉛筆を買う’]);

// doneとdonelistのテスト
todo.done(‘鉛筆を買う’);
assert.deepEqual(todo.list(), [‘ノートを買う’]);
assert.deepEqual(todo.donelist(), [‘鉛筆を買う’]);

//delのテスト
todo.del(‘ノートを買う’);
todo.del(‘鉛筆を買う’);
assert.deepEqual(todo.list(), []);
assert.deepEqual(todo.donelist(), []);

console.log(‘テストが完了しました’);
 

1行目で、ボットのプログラムを呼び出します。○○には上記で作成したボットのプログラムファイルを指定します。

2行目で、モジュールassertを呼び出して、その変数をassertとしています。

5-6行目で、ボットプログラムのtodo関数のtaskに「ノートを買う」「鉛筆を買う」の2つを追加します。つまり、[ノートを買う, false] [鉛筆を買う, false]という配列が作成されます。その後、isNotDone関数で[ノートを買う, true] [鉛筆を買う, true]へと変換され、list関数で[ノートを買う, 鉛筆を買う]という配列が作成されます。

7行目で、ボットプログラムのlist関数の中身(配列)と、[ノートを買う, 鉛筆を買う]が同じであるかを確認しています。

10行目で、ボットプログラムのdone関数のtaskに「鉛筆を買う」を追加します。これにより、連想配列にキーとして「鉛筆を買う」が含まれているので、[鉛筆を買う, true]へと変換されます。そのため、donelistには[鉛筆を買う]、listには[ノートを買う]という配列がそれぞれ作成されます。

11行目で、donelist関数の中身(配列)と[鉛筆を買う]が同じであるかを確認し、

12行目で、list関数の中身(配列)と[ノートを買う]が等しいかを確認しています。

15-16行目で、del関数のtaskに「ノートを買う」「鉛筆を買う」の2つを追加します。これにより、連想配列にキーとして「ノートを買う」「鉛筆を買う」が含まれているので、配列のキーが削除されます。それにより、donelist関数の中身も、list関数の中身もそれぞれ[ ]という空欄になります。

17行目で、donelist関数の中身(配列)と[ ]が同じであるかを確認し、

18行目で、list関数の中身(配列)と[ ]が等しいかを確認しています。

20行目で、すべてのプログラムが問題なく完了すれば「テストが完了しました」と表示されます。

 

以上が、ボットのプログラムになります。次回、今回作成したボットをSlack上で動かします。

  

ABOUT ME
GoodAmbition
オンライン塾経営者(大阪大学工学部出身の元開発技術者) 自身も家庭教師や塾講師として働きつつ、後輩の育成やオンライン塾を経営しています。 私自身も約10年にわたり家庭教師や塾講師として100人以上の受験生と向き合ってきました。 色々な学生、保護者の方とかかわる中でよく質問される内容や、受験に必要な内容について書いています。 独学で頑張っている人たちへ勉強計画や悩み相談なども受け付けていますので気軽にお問い合わせください。 就職活動や資格の勉強などで悩んでいる方もご連絡ください。 教育・就活、書籍、食べ歩きに関するお話がメインです。 最近取り組んでいること プログラミング、筋トレ、マラソン、ライティングスキル向上etc. 苦手なことを克服しようと頑張っています。