2018/04/22 - キー配列変換を関数型プログラミングで

マルチプラットフォーム版WTの開発は,Windows版のテストが完了した。次はAndroid版をテストしているのだが,Bluetoothキーボードがどうしても101キーボードとして認識されている。どうもキー入力を取得すると101のキーコードが来て,通常はIME側で106に変換しているっぽい。携帯だけならいいけど,raspberry pi版もあるのでどうしてもBluetoothキーボードに対応したい。ということで101->106変換を実装。

現状のWTでは101/106の2次元キー配列(1列分のキー×5段)を既に持っている。これを変換するマップを作ればいいわけで,普通に作ると2重のループで2次元配列のそれぞれを対応づけて,Dictionary<KeyCode, KeyCode>を作る感じ。こういう手続き型のコードはささっと書ける。

ただ,最近関数型言語を勉強していて,写像とか圏論に興味がある。なので関数型で書けないかな,って考えると,101キー集合から106キー集合への写像を作ればいいということなので,あえてLinqで書いてみた。んだけど慣れてなくてすごく分かりづらいものになってしまった。関数型に慣れてる人は簡単に書けるのかなあ。理解しづらいのは置いておいて,コンパイルできるようにするまでは大変なんだけど,書き終わって実行すると一発で動いた。このあたりは手続き型に対する関数型の強みになるのかな。

// KeyLine1をインデックス付きにする
x.KeyLines.Select((xLine, i) => new { index = i, line = xLine.KeyTops })
    // KeyLine2をインデックス付きにする
    .Join(y.KeyLines.Select((yLine, i) => new { index = i, line = yLine.KeyTops }),
        xLine => xLine.index,
        yLine => yLine.index,
        // indexが同じキー行でInner Join
        (xLine, yLine) =>
            // KeyTops1をインデックス付きにする
            xLine.line.Select((xKey, i) => new { index = i, key = xKey.KeyCode })
                // KeyTops2をインデックス付きにする
                .Join(yLine.line.Select((yKey, i) => new { index = i, key = yKey.KeyCode }),
                    xKey => xKey.index,
                    yKey => yKey.index,
                    // indexが同じKeyTopでInner Join
                    (xKey, yKey) => new { xKey = xKey.key, yKey = yKey.key }))
     // key1->key2のmapがキー行数分得られたので1次元配列にする
     .SelectMany(xLine => xLine)
     // キーとキーのDictionaryを作る
     .ToDictionary(item => item.xKey, item => item.yKey);

関数型言語だけど,元々Cマガの千言万語で興味があったのだが,最近いろんな言語で関数型言語的な書き方ができてきているので,Haskelを勉強してみた。で,やっぱりモナドで引っかかるので圏論を勉強し始めたのだが,むずい。群論とか線形空間とかの例はなんとなく分かるんだけど,位相空間とかになると分からない。と思っていたら丁度数学ガールの新刊で位相空間を説明していて助かった。


コメントする