関数合成の書きにくさ
ここまでに随分と紙幅をとってしまったが、もともとここから先を書きたかった。
先に unique を示したが、重複を取り除く処理とともに、取り除かれる要素を取り出す処理を並べて書きたくなった。
ナイーブに書けば、こうなるだろう。
function hoge(array) { const unis = array.filter((val, idx, arr) => arr.indexOf(val) === idx); const dups = array.filter((val, idx, arr) => arr.indexOf(val) !== idx); // continues calculation on unis and dups...
お気づきのとおり違いは `===` と `!==`、比較の結果のみである。
こういう場合に、以下のようにサクッと関数合成が使えたら楽なのになあ、という話。:
function hoge(array) { const unique = (value, index, array) => array.indexOf(value) === index; const unis = array.filter(unique); const dups = array.filter(not <of> unique); // continues calculation on unis and dups...
Haskell 使っておけよタコ助、という話でもある。
無理やり JavaScript 文法に落とすと、次のようになる。:
function hoge(array) { const unique = (value, index, array) => array.indexOf(value) === index; const not = f => function() { return !f.apply(null, arguments); }; const unis = array.filter(unique); const dups = array.filter(not(unique)); // continues calculation on unis and dups...
C++ でテンプレート使った functor と同じなんだけれど、丸括弧が並ぶのは読みづらい。(この Functor と Haskell で言うところの関手は、どこが同じでどこが違うのだろう?)
エディターで、関数はイタリック、値はノーマル、みたくフォントを使い分けられたら違うのだろうか?
関数だって「値」で、値に名前をつけているだけ。だから特別扱いするほうがおかしい、という見方もあるだろう。
とはいいながら、別の値に適用可能な値と、そもそも適用できな値とは、表現が変えられたほうが理解はたやすい、のではなかろうか。
結論だせず、グダグダで終わる。
配列からの重複削除
JavaScript で、配列から重複した要素をとりのぞく処理は以下のように書ける(検索すると、たくさんヒットする)。
const unique = array => array.filter((value, index, self) => self.indexOf(value) === index);
これは次のように使う:
> unique([1, 3, 2, 1, 3]) => [1, 3, 2]
手続き的な処理の解説
unique に渡す引数配列 array を、関数 `(value, index, array) => array.indexOf(value) === index` で filter する。
つまり value, index, array の三引数をとり、これで array.indexOf(value) === index を評価して、 true になる要素 value を残して配列として返す。
先の例につかった配列 `[1, 3, 2, 1, 3]` を例につかう。
filter は配列の先頭から順に値を渡してくれる。
- 最初の要素の値は 1、このインデックスは 0、配列は [1, 3, 2, 1, 3]。
- 次の要素の値は 3、インデックス 1、配列は [1, 3, 2, 1, 3]。
この調子で、入力配列を filter に順次渡される三つ組の配列として表現すると、以下のようになる。:
[(1, 0, [1,3,2,1,3]), (3, 1, [1,3,2,1,3]), (2, 2, [1,3,2,1,3]), (1, 3, [1,3,2,1,3]), (3, 4, [1,3,2,1,3])]
これを順次 `array.indexOf(value) === index` で評価すると、:
[true, true, true, false, false]
というのは、最初の要素を例にとれば以下のように評価が進むため。:
((value, index, array) => array.indexOf(value) === index))(1, 0, [1,3,2,1,3]); => [1,3,2,1,3].indexOf(1) === 0 => 0 === 0 => true
結果、先頭から順に `[1, 3, 2]` が取り出されて終わる。
オーダー
array に対する二重ループだから array の長さを n として O(n^2)。
filter に渡す関数の、その引数に array たる self がバインドされること、そして関数本体で array を self.indexOf() として使っているため。(ワーストケースは、重複がない配列への適用)
ファーストクラス(第一級の〜)
関数がファーストクラスのオブジェクトである、などとスカした(かっこいい)言い方がある。
言ってしまえば、関数が、関数の戻り値や引数として渡せる程度に特別扱いされている(逆か? 特別扱いされていない?)ということだ。
WinAuth 認証情報を別 PC に移植する
バッドノウハウ。
WinAuth の認証情報を複数 PC で共有したくなることがあるよね?
1. エクスプローラーを開く
2. アドレスバーに %APPDATA% を入力して開く
3. WinAuth フォルダーをコピーする
-
- ネットワークに置くとか、 USB スティックに入れるとか、お好みで
- ただしアクセス権設定にはご注意! 人に持っていかれないように!
4. 別 PC の %APPDATA% に WinAuth フォルダーをペーストする
※リカバリーしたりする前に、保存しておくこと! っていう見出しにすりゃノウハウなのにね。
-
-
- -
-
なお、 WinAuth 実行バイナリーは %LOCALAPPDATA%\Programs\WinAuth-3.x.x に置いています。
そしてスタートメニューとタスクバーにピン留め。
リファクタリング
レビューしていると、処理がどう流れているからわからんから、ちょっと整理させてね…… ってことがよくある。
@@ -22,22 +22,17 @@ var ResultsUtils = (() => { return active_status.includes(job.status); }; var _parseMonitoringReport = function(result) { - if(result.train_status || result.monitoring_report) { - var monitoring_report = result.train_status ? result.train_status.monitoring_report : result.monitoring_report; - result.costs = []; - result.training_errors = []; - result.validation_errors = []; - if(monitoring_report) { - Object.keys(monitoring_report).forEach((key) => { - result.costs.push(monitoring_report[key].cost); - result.training_errors.push(monitoring_report[key].train_error ? monitoring_report[key].train_error : 0); - result.validation_errors.push(monitoring_report[key].valid_error ? monitoring_report[key].valid_error : 0); - }); - result.current_epoch = Object.keys(monitoring_report).length; - } - result.train_status - ? delete result.train_status.monitoring_report - : delete result.monitoring_report + if (result.train_status || result.monitoring_report) { + var report = (result.train_status || result).monitoring_report || {}; + var indices = Object.keys(report).sort((a, b) => a - b); + var valueOf = key => index => report[index][key] || 0; + Object.assign(result, { + current_epoch: indices.length, + costs: indices.map(valueOf('cost')), + training_errors: indices.map(valueOf('train_error')), + validation_errors: indices.map(valueOf('valid_error')), + }); + delete (result.train_status || result).monitoring_report; } return result; };
こんなことしてるから、時間がいくらあっても足りないわけだけれど。
ハイレベルのプログラムかと聞かれると、これがー? っていう気持ちにはなる。 なるが。 メンバーがついてこれないことを見ると、ひょっとするとハイレベルなコードを書いてい るのかもしれない。
— いけべ (@ikb) September 27, 2017
わたしが書き直す前とあととで、どちらがプログラマー諸君にとってより読みやすいか、というのは、ほんと聞いてみたい。
いや、聞いたら、自信を失うかもしれない。
カプセル化
JavaScript でも Python でも '_' で始まるプロパティやメソッドは、そのファイルスコープ、あるいはオブジェクトのスコープだけで参照してください。
— いけべ (@ikb) July 21, 2017
これはキーワードで明示的アクセス制限できない言語で紳士協定として導入された名前規則です。尊重してください。
具体的にいうと this._hoge や self._foo() は良くて、 piyo._fuga や poyo._foo() はアウトです。
— いけべ (@ikb) July 21, 2017
ちょっとわかりにくいかもしれませんが this._foo._bar もアウトです(_foo._bar がルールに抵触するため)。
このルールを回避するために this._foo を foo に変名する、あるいは this.getFoo = function() { return this._foo; } のようなアクセッサーを追加する、ということも禁止です。
— いけべ (@ikb) July 21, 2017
オブジェクトの境界は、ある関連をもった複数のプロパティをまとめて管理するために使います。
— いけべ (@ikb) July 21, 2017
オブジェクトの関数は、関連を持つ複数のプロパティが、同時に満たすべき制約条件を壊さないように導入するものです。
プライベートなプロパティは、制約を満たし続けるよう管理するために使います。
— いけべ (@ikb) July 21, 2017
これをひとつ取り出して変更できるアクセッサーを追加するということは、オブジェクトの管理責任放棄です。
プライベートなプロパティを、そのオブジェクトの外から変更することは、オブジェクトの管理責任侵害です。
このアクセス制約は「カプセル化」として知られており、プログラムの品質担保に大きな役割を果たします。
— いけべ (@ikb) July 21, 2017
知っていた人は改めて、知らなかった人はこれから、注意するようお願いします。
さすがに同じレビューを繰り返すのに心が疲れ切ったので、あった…。