関数合成の書きにくさ

ここまでに随分と紙幅をとってしまったが、もともとここから先を書きたかった。

先に 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. 最初の要素の値は 1、このインデックスは 0、配列は [1, 3, 2, 1, 3]。
  2. 次の要素の値は 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;
     };

こんなことしてるから、時間がいくらあっても足りないわけだけれど。

わたしが書き直す前とあととで、どちらがプログラマー諸君にとってより読みやすいか、というのは、ほんと聞いてみたい。
いや、聞いたら、自信を失うかもしれない。

Vue JS でのハマりポイント

  • template で置き換えられるトップレベルは一要素だけ
  • template に内で参照する props や data は、テキストと属性とで指定方法が違う
    • テキスト部分では {{ value-name }}
    • 属性では v-bind:="value-name"

カプセル化

さすがに同じレビューを繰り返すのに心が疲れ切ったので、あった…。