共通部分を一つにするのは楽しい
前回の記事では、if...else 文の中を共通化することができませんでした。しかし、関数型プログラミングの考えを取り入れることで、共通化することができました。
function move(com, i) { _.each(rangeOne(i, com === 0 ? 100 : 0), function(i) { /* 共通部分 */ }); } // 1ずつ増減する要素の配列を返す関数 function rangeOne(start, end) { return _.range(start, end, start < end ? 1 : -1); }
前回のコードです。
function move(com, i) { function common() { // 共通部分 } if (com === 0) { while (i < 100) { common(); i += 1; } } else { while (i > 0) { common(); i -= 1; } } }
_.each メソッドや _.range メソッドは、Underscore.js*1 で定義されているメソッドです。Underscore.js は、JavaScript で関数型プログラミングをするために作られた JavaScript ライブラリです。各メソッドの説明をしてみます。
_.each メソッド
第一引数に配列を、第二引数に関数を渡します。
このメソッドの仕事は、配列の各要素を関数に渡すことです。
_.each([1, 3, 5], function(n) { console.log(n * n) }) // => コンソール画面に「1 9 25」と表示される
_.range メソッド
全ての引数は数値です。
このメソッドの機能は、新たな配列を作成することです。
_.range(10, 15, 1) // => [10, 11, 12, 13, 14] _.range(10, 0, -2) // => [10, 8, 6, 4, 2] _.range(10, 5, 1) // => []
_.range メソッドの第三引数を省略すると 1 が入ります。第二引数が第一引数よりも小さいか、等しいとき、空の配列が作成されます(例の 3 番目)。
この 2 つのメソッドを使用することにより、前回成し遂げられなかった共通化ができました。とても嬉しかったです。副産物として rangeOne 関数という汎用性の高い関数もできました。これからも、共通部分を一つにすることを意識したいと思います。
今回のコードにはバグが潜んでいました。
例えば i 変数が 200 で com 変数が 0 のとき、前回のコードではループが一度も実行されないにも関わらず、今回のコードでは 100 回のループが実行されます。com 変数ではなく rangeOne 関数が増減の方向を決めているからです。以下のように訂正しました。
function move(com, i) { _.each(rangeMake(i, com===0?[100,1]:[0,-1]), function(i) { /* 共通部分 */ }); } function rangeMake(start, arr) { return funcApply(_.range, [start].concat(arr)); } function funcApply(func, args) { return func.apply(null, args); }
funcApply 関数は汎用性が高いですが、rangeMake 関数は汎用性が低いかもしれません。コードが横に長くなり過ぎないように rangeMake 関数を作ったのですが、はたしてこれは正しいのか…。そのような動機で関数を作るなら、頑張って整形したほうがいいのではないか。でも、うまいこと整形する方法がわからなかったので、rangeMake 関数という汎用性の低そうな関数を作成してしまったわけです。
とりあえず、今回はこれを完成形としておきます。ではまた。
ちょっと気になることがあったのでまたまた追記。
funcApply 関数の必要性があまりないように感じられました。
function move(com, i) { _.each(rangeMake(i, com===0?[100,1]:[0,-1]), function(i) { /* 共通部分 */ }); } function rangeMake(start, arr) { return _.range.apply(null, [start].concat(arr)); }
こちらのほうがいいかもしれません。
*1:公式サイト http://underscorejs.org/