30 31 1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31 1 2

ここは Web や Android アプリのプログラマでありチェスやバイク、株式投資を趣味とするコジオンこと Hideyuki Kojima の日記です。 毎日何かしら欠かさず書いています。

この Blog の他に Qiita にもいくつか技術系の記事を投稿しています。 YouTube のチェス実況チャンネル に毎日 lichessChess.com の 10 分レート戦の実況動画を投稿しています。

連絡はメールでお願いします。kojionilk あっとまーく gmail どっと com です。

最近のコメント

たまの客先訪問

本日は客先訪問だったためスーツ姿で遅めの電車に乗った。 ちょっと頭痛がしたので薬を飲みたいのと喉が渇いたので到着駅で珍しく自動販売機で飲み物を購入した。 いつかも書いた気がするが、私は自動販売機で飲み物を買うという行為はツーリング以外ではほぼ行わないため、買ってみると何となくツーリングに来たような自由な気分になる。 「2つの食感ソーダゼリーメロン」という子どもが好きそうなものを買ったが、なかなか面白い食感でたまにはいい感じだ。

久々のクアアイナ

クアアイナ 厚切りアボカドチェダーチーズバーガー

訪問が終わりちょうどお昼時になった。 帰り道にクアアイナがあったので寄ってみた。 クアアイナはハワイ発の比較的有名なグルメバーガーチェーンだが、本当に久々に来た。 1 年半ぶりだろうか。

「厚切りチェダーアボカドバーガー」で、要するにアボカドチーズバーガー (厚切りなのはチーズ) である。 店員が「アボガド」と発音しているのがどうも気になった。 スペルが AVOCADO なのだから「ガ」の要素などどこにもない。 ……などと思ったら文法ミスを指摘する人は“圧倒的に性格が悪い”を思い出した。

性別、年齢にかかわらず結果は期待通りのものだった。より外向的な人物の方がミスを見逃す傾向があり、感じが悪く内向的な人物の方が「ミスの多い文章を書く人」を否定的に評価していたという。

……なかなか手厳しい。 勿論店員にわざわざ指摘はしていないが、思ったのは確かだ。 ちなみにこの Blog も意識して誤字は少なめに保っており、定期的にひと通りチェックし誤字や文法ミスを排除するようにはしている。 ……まぁ感じが悪いかどうかは置いておいて「内向的」は合っている。

話がそれたが、クアアイナのハンバーガーは相変わらずバンズやパティがパサパサした感じのいかにもこの店ならではといった感じのものだ。 一般的なグルメバーガー及びハンバーガーのソースや肉汁でジューシーな感じをイメージしていると真逆である。 クアアイナのハンバーガーを最初食べたときは「パサパサしている」と否定的な印象を持ったのだが、やはりこうやって何軒も食べ歩いてみるとしっかりとグルメバーガーの風格を保っていると感じる。 シェイクシャックで感じたようなジャンクな印象は全くしない。

写真のセットで税込 1,500 円近くする。 私はグルメバーガーを食べ慣れているのでそこまで高いとは感じないが、人によってはとても高いと思うことだろう。 ちなみに私は普段のランチはワンコイン (500 円) 近辺なので 3 食ぶんである。 ただ、この店はグルメバーガーでは珍しくジェフグルメカードが使えるのは大きいし、ショッピングモールに入っている場合はクレジットカードも使えるのもいい。

ツインエンジェル BREAK は観終わった

先日から久々に d アニメで視聴しているわけだが、ようやくツインエンジェル BREAK が観終わった。 話の大筋が分かりやすいのでとても観やすいアニメだったのだが、少しネタバレになってしまうが、死んだと思った登場人物がまた出てきてまた死んだような演出があったがまた生きているような演出があり……というのは感動の安売りをしているようで正直好きではない。 キャラクターが可愛いのはいいが、変身するシーンを観ていると自分でも恥ずかしくなるのがなかなか辛い。 大きいお兄さん向けなのは理解するところだが、変身でわざわざ胸が揺れる演出を入れる必要はないと思う……。

蒼穹のファフナー HEAVEN AND EARTH

続いて「蒼穹のファフナー RIGHT OF LEFT」と「蒼穹のファフナー HEAVEN AND EARTH」を観た。 こちらはうって変わってとても分かりにくい話だ。 正直よく分からないのだが、観ている人に自由に考える余地を残すという意味があるのだろう。

ただ一番の難点は登場人物……特に少年少女たちの顔が全部同じような顔に見えることだった。 どれが誰だか良く分からない時があるし、覚えられない。 全体的に重い話で世界観は好きではある。 が、前述の通り良く分からないので、これを好きと言っていいのかに少し疑問が残る。

最新版に差し替え

私が今使用している月配列 K の最新版と紹介サイト: typing.kojion.com に載せている版で差異が生じてしまっていたので、配列図、DvorakJ の定義ファイル、シミュレータの挙動を最新の月配列 K に差し替えを行った。 具体的に言うと月配列 K 20170313 版から月配列 K 20170804 版への変更を行ったわけだが、月配列 K 結局以前の安定版とそう変わらなくなるにも書いている通り差異は「ち」の位置のみである。

月配列 K は私のポリシーで最新の安定版のみを「月配列 K」と呼称することとしているので月配列 T のようなバージョン番号表記はない。 それなりの頻度で配列の修正を行うことを前提とするならばバージョン番号表記があったほうが有利なのは間違いないが、私としてはもう配列をいじる気は無い (もっとも 2017/03/13 時点でもそう思っていたに違いないが……)。

折角なので月配列 T3.1 もシミュレータに導入

ついでに紹介サイトに置いてある月配列シミュレータで選択できた月配列 T3.0 を T3.1 にアップデートしておいた。 試しに T3.1 を打ってみたが、月配列 2-263 式とは大分配置が変わってしまっているものの、元としているだけのことはあるのかキーボードガイドを見ながらまずまずスムーズに入力できた。

月配列 T の作者である tken さんの Blog には今回の T3.1 (多くは T3.0) において「何故そのキーをそこに配置したのか」が詳細に記されている。 これは大変興味深く拝見させて頂いた。 また、このような考察が記載されていると今後同様に月配列 2-263 式を改造しようという人が出てきた時にこの情報を見て判断材料に加えることができる。 これはとても有意義だと私も考えるところであり、わざわざ紹介サイトなどを拵えているのも月配列 K をそのまま使って欲しいというよりも「このような方法で改造を加えるとこのような効果が見込める」ということを同様の悩みを持つ方々に選択肢として示したいがためなのである。 これは技術者として Blog や Qiita にわざわざ頼まれもしない技術情報をまとめることに似ている。 勿論ボランティア精神だけでなく、記事を書きながら自分の考えをまとめるという役割もあるので聞こえがいいように言っているようで恐縮ではあるが「皆でハッピーになろう」ということである。

技術情報に関して言えば私もよくググって問題解決しているので会ったこともない技術者同士で助け合っているとも言える。 私はこの見えない助け合いの精神が大好きだ。

そんなに強い人ばかりじゃない

思うところがあり Twitter を止めてから 1 週間が経ったが、フォローしていた人のツイートはまだたまに見ている (鍵の人は見れないが仕方がない)。 そんな中、一般的な観点から言うとタイピングが十分に速いにも関わらず大会で全く活躍できずに嘆いている人を見つけた。

「自分なりの努力はしたのだが思うような結果は出せないし今後もその見込みがないだろう」という事で正直気持ちが分かりすぎるだけに印象に残った。 タイピングは実力が如実にスコアに反映され格付けされてしまうハードでストイックな世界なのである。 「それなりのスコアでそれなりに頑張る」ような人には居心地がいいようには見えない。 この点に関してはどちらかというと「自分で居心地の良いポジションを作っていく」方がいいような気がする。

私もそんな事を思って「タイピングは他人を見ずに自分だけを見てコツコツとやった方が長続きするのではないか」といった趣旨のツイートをしたことがあった。 ただ、その前の「才能が大きく作用する」は思っていても言うべきではなかったとは思う。 「才能」という言葉を出してしまうとまるで現時点で遅い人は才能が無いような印象を受けてしまう。 その人の環境や経験にもよるだろうから一概に言ってしまっていいような事には思えない。

何のためにやるのか、自分が何を求めているのか

私は上 (タイプウェルでいうところの Machine 以上) を目指そうなどということは思ったことはないし、「自分にはそれだけの才能はない」と思っている。 「自分で自分にフタをしている」と言われても、確かにその通りとしか言いようがない。 自分はそこには重きを置いていないだけの話である。

タイピングは現時点の実力やそれに対する成長率など個人差が大きいのは確かであるが、コツコツと続けていけば確実にレベルアップできる趣味であり「確実にレベルアップできる点のみを楽しむ」方向に持っていくのがいいと思う。 私は趣味の一つとして自分の無理のない範囲内で空いた時間にやる位が丁度いいと思っている。 しかしいまのネット社会では嫌でも他人の情報が入ってきてしまうのでなかなか難しい話ではあるのだが、Twitter のような SNS を控えると少しは改善されなくはない。

勿論上を目指したい人はそうすればいいだろうし、そんなに (いろいろあるだろうが、多くはメンタルが) 強くない人は別に上記のようなユルイ楽しみ方でやっていってもいいのではないかと思う。 将棋プロやアマ強豪の華麗な差し回しは勿論素晴らしいものだが、だからといって町の将棋道場のそこそこ強い人に存在価値が無いかというとそんなことはないはずである。

家庭持ちの中年である自分が人生の中で何を優先すべきかというところでいうと、生きるための手段、仕事である。 趣味などは仕事に支障の出ない範囲内で楽しむのが良いと思うが、上記のようにまったり楽しみたい人が素直に楽しめる方向に持っていくには多少の気構えというか工夫が必要なところにタイピングという趣味の息苦しさを少し感じる。

朝食バイキング

例によって 5 時頃起床しひとっ風呂浴びてからホテル朝食バイキングを頂いた。 バイキングに関しては可もなく不可もなくといった内容だった。

そんなにゆっくりしているのも何だということでそのままホテルを後にした。

女神湖

すぐ近くいうことで女神湖に行ってみた。 白樺湖と違って周りにこのホテル以外何もない。 まぁ逆にそこがいいのかもしれない。 バイクで来たとしても畔に座ってしばらくまったりしたいかもしれない風景だった。

長門牧場

その後ソフトクリームでも食べようということで牧場に行くことにした。 この辺りは牧場が多いのだが、その中で事前に調べておいたソフトクリームが美味しいらしい「長門牧場」にした。

入口あたりにアルパカがいたり羊がいたりしたが、特に他に見るところもなく正直時間つぶしに困るところではあった。 予定通りソフトクリームを頂いた。 確かに濃厚でとても美味しい。 ……が、他で食べても大して変わらない感想だったかもしれない……。

三峰展望台

三峰展望台

最終目標は日本一標高が高いらしい道の駅「美ヶ原高原美術館」に決めていたのでそこを向かって走り出した。 ビーナスラインを走り終えるところで三峰展望台 (峠の茶屋) があったので寄ってみた。

ここは茶屋から少し上がった展望ポイントから 360 度見渡せる絶景がとてもいい。 今日は割と遠くの山々がクリアに見えたので良かった。 やはり皆で絶景を背景に写真撮影をした。 ビーナスライン特有の「標高が高すぎて木々が生えていない山肌」がとても美しいと思う。

そのまま三峰展望台を後にして美ヶ原高原美術館に行った。 美術館は皆で入り、パンフレットに書いてある「40 分コース」を目標に回ってみた。 コースから外れるところでもかなり多くの芸術品があり普通に回っていたらとても全部見きれない感じであることは明らかだった。 最後の方は皆飽きていたようだったし正直展示品は理解に苦しむものばかりだった……が、何となく形は面白いので子どもを連れて散策する分にはいいのかもしれない。

北八ヶ岳ロープウェイ

蓼科高原に家族旅行に出かけた。 私の運転だが車は自宅のはパワーが足りない軽なので父のものを借りた。 まず佐久市の方まで高速で走り切り北八ヶ岳ロープウェイを目指す。

北八ヶ岳ロープウェイは標高 2,240 m 程度まで上がることが出来るというものだった。 真夏だがロープウェイを登った先は半袖だと肌寒いくらいだった。 事前の調査どおり山頂駅から 30 分ほどの周遊道があるのでそこを皆で周る。 天気が微妙だったのか霧がかかっており、若干展望に難があったのが残念だった。

ロープウェイを終えて山麓駅のレストランで食事。 カツレツ定食を頂いた。 ここの食事は古き良き昭和の「日本の洋食」といった雰囲気を醸し出しており、とても趣深かった。 コーヒーもとても美味しかった。

蓼科高原 バラクライングリッシュガーデン

ここで私が事前準備したプランを幾つか提示したところで「ジャズがいい」と言われたので次の目的地はバラクライングリッシュガーデンに決まった。 ロープウェイから 20 分ほどで到着したのでそれほど遠くない。 ここは英国式庭園なのだが個人的にそんなに庭園に興味が無いので何となく流し見で終わった。 「ダリアが綺麗」ぐらいしか言えなかった。

肝心のジャズだが 13:30 の回に間に合ったのでそこを鑑賞した。 プロの歌手の方の歌唱がとても表情豊かでクオリティの高いものだった。 ……のだが会場が庭園の横にあるからか室内をハチが飛び回っているような状態で環境はあまり良くなかった感じはあった……。 ドリンク付きのコンサートチケットだったので聞き終わってからレストランでドリンクバーを頂いた。

白樺湖

白樺湖

まだ少し時間があるということで、ホテルに向かう道にある白樺湖に向かった。 白樺湖はこのあたりでは一番広い人工の湖であり周辺に観光用の施設があった。 とはいえそこまで繁盛しているわけではなさそうだった。 赤城山などと同じような感じだろうか。 というと失礼だが……。

特にこれをやろうとも決めていなかったので白樺湖の一部分だけを適当に散策した。 石碑を見つけたので写真を撮っておいた。

明日は天気がいいことを確認してはいたのだが、時間があったので念のためビーナスラインの絶景ポイント (白樺湖 ~ 車山高原) を走っておいた。 それぞれの展望ポイントで皆で写真を撮って楽しんだ。 今日も多くのバイクを見かけた。 本音を言うと自分もバイクで来たかった……。

ビーナスラインを堪能したところで 16 時を過ぎたのでホテルに向かった。

ホテルアンビエント蓼科

ホテルアンビエントは何箇所かにあるようだがその中の蓼科 (女神湖) にあるホテルアンビエント蓼科に宿泊した。 食事は和会席としたが、フランスコース料理のように時間をかけて出てくる感じではあったが 1 時間ほどでデザートまで行ったのでそこまで待たされている感はなかった。 ビールはブラウマイスターの他にオラホビールも選択できるのが好印象だった。 勿論和会席もとても美味しかった。

その後大浴場に行ったのだが、どうもホテルの規模に対して脱衣所とお風呂が小さすぎるような印象は受けた。 このホテルは別館が何棟もあったので恐らく大浴場の規模感が追従できていないように見受けられた。 ただ露天風呂やサウナなど基本の部分は抑えてあった。

起床も早かったので、夜のコンサートを聴いてからすぐ寝てしまった。

明日から蓼科・車山方面に家族旅行に出かける予定なので、天気を確認し計画を練っている。 旅行計画・車の運転はいつもの私の仕事である。 土曜は雨の予報のようだったが幸いにも午前中のみのようで午後は晴れ間が見えるようだ。 日曜は晴れるらしい。 蓼科方面は屋内で楽しめるレジャー施設が少ないので晴れてくれないとなかなか厳しいものがあった。 雨の場合は途中の軽井沢あたりで観光してから来るというのもアリかと思った。

12:00 前あたりに立科町に到着するものとすると 2 箇所ほど回るのが妥当な線だろう。 メイン 1 箇所を堪能しサブ 1 箇所を軽く寄ってから旅館に向かう感じだ。

北八ヶ岳ロープウェイ

公式ページに以下のように記載があった:

北八ヶ岳ロープウエイは、八ヶ岳の北端に位置する北横岳と縞枯山の間に架かり、山麓駅(標高1,771m)から山頂駅(同2,237m)までの高低差466mを約7分で駆け上がります。 山頂駅には自然が造り出した芸術『坪庭』が広がり、1周30分~40分ほどの散策路には、自然そのままの高山植物が季節毎に咲き、訪れる方々を魅了します。

1 周 30 ~ 40 分ほどの散策路があるというのはちょうどいい気がする。 1 時間くらいかかるのは少し身構えてしまうが 30 分程度ならば年配者でも問題はないだろう。 ロープウェイに乗っている時間と待ち時間を加味しても 1 時間半程度だろう。

蓼科高原 バラクライングリッシュガーデン

英国式庭園……らしい。 正直これだけだと辛いものがあるが、ジャズのイベントがあるようなのでそれ目当てで行くのもアリかもしれない。 イベント時間は 11:00, 13:30, 15:30 なので行くつもりならばそれを頭に入れて行動しておくのがいいだろう。

蓼科山聖光寺

諏訪大社はちょっと遠い……。 少しお寺に寄りたいならアリか。

白樺湖

軽く散策したり店を見たり。 規模が小さくなるが女神湖もアリ。

長門牧場

牧場ならここで。

2 日目

2 日目はビーナスラインで絶景を楽しみつつ北上し美ヶ原に到着、美ヶ原美術館を堪能し、日本一標高が高いと称される道の駅美ヶ原高原に行きお土産を買い帰路につく、といった感じがいいだろう。

Android Studio に warning を出されるようになった

Kotlin で以下のような構文を書くと Android Studio に warning を出されるようになった:

if (value?.isEmpty() ?: false)  // Equality check can be used instead of elvis
if (value?.isEmpty() == true)  // これなら OK

この場合は elvis 演算子 ?: ではなく == で代用できるというわけである。 何故私が上の elvis 演算子 を使った構文を使ってしまっていたかというと、紛れもなく Java では false は基本型 boolean であるという事実が頭にあったからであり Java の概念に引きずられて if (value?.isEmpty() == true) の方の例が些か直感的でないように感じてしまったからだ。 以下どういうことなのか考察する。

Java では

Java では以下のような比較は syntax error となる:

if (null == false)  // syntax error. false はオブジェクトではない!!
if (null == Boolean.FALSE)  // これなら OK

Java は歴史的な経緯にて全てのフィールドやメソッドがオブジェクトというわけではなく基本型 (プリミティブ型) とオブジェクト型に分類される。 そもそも基本型が null になることはなく基本型に対する == 比較は基本型且つ型が一致していないと行えない。

基本型はオブジェクトではないので基本型に対するメソッド呼び出しを行うことはできない。 メソッド呼び出しを行いたい場合は対応するラッパー型 (オブジェクト) に変換した上でメソッド呼び出しを行う。 上記の例の場合 boolean のラッパー型は Boolean なので false に対しメソッド呼び出しを行いたいならば Boolean.FALSE を使えば良い。

Boolean.TRUEBoolean.FALSE はそれぞれ java.lang.Boolean クラスに対するインスタンスである。 一方 Java における == 演算子の役割はオブジェクトの参照が等しいことの比較なので以下のような null との比較は false となる:

System.out.println(null == Boolean.TRUE);  // false
System.out.println(null == Boolean.FALSE);  // false

一方 Boolean.equals() の実装も「比較対象が Boolean クラスでない場合 (null も含む) 常に false となる」となっているので以下も合点がいく:

System.out.println(Boolean.TRUE.equals(null));  // false
System.out.println(Boolean.FALSE.equals(null));  // false

まとめると Java では nulltrue でも false でもない第三の値である。 一方以下のように nullnull の同値比較は true となる事に注意する:

System.out.println(null == null);  // true

Kotlin に話を戻す

話を戻すが Kotlin では以下だとコンパイルが通らない:

if (value?.isEmpty())  // 駄目. value が null の場合戻り値が null となるので評価値が Boolean? となる

Kotlin は null-safety な言語なので上記のような書き方をすると valuenull だった場合に NullPointerException がスローされずに isEmpty() メソッド呼び出しが行われないという挙動になる。 よって null の場合を ?: で付け加えるのは自然に見える。

以下再掲する:

if (value?.isEmpty() == true)  // なんとなく null == true がマズそうに見える

Kotlin では全てのローカル変数はオブジェクトの為 null == true の評価が可能である。 しかし ==null オブジェクトに対する equals() のシンタックスシュガーに見えるので何となく null に対するメソッド呼び出しに失敗しそうに見えるのだが、Kotlin 公式に以下のように記載があった:

Structural equality is checked by the == operation (and its negated counterpart !=). By convention, an expression like a == b is translated to a?.equals(b) ?: (b === null)

つまり Kotlin においては前述の if 文は以下と等価である:

if (value?.isEmpty()?.equals(true) ?: (true === null))

これは比較対象の左辺が null だった場合左辺と右辺が共に null の時のみ true となる、と読み解ける。 つまり Java における「nulltrue とも false とも一致しないし null == null = true である」という原則を生かしつつ、常にそのまま第三の値として == で比較できるように言語の文法レベルで配慮が効いているということだろう。

先日より 10 インチタブレットである MediaPad M3 Lite 10 の運用を始めた。 何か映像作品を観ようかなと思い、前々から気になっていた「ツインエンジェル BREAK」を観ることにした。

何故ツインエンジェル BREAK なのかというところだが「MJ モバイルでイベントをやっていてキャラが可愛かったから」という点に尽きる。 普段こういった「萌え」を全面に押し出したアニメは観ないのだが、MJ モバイルで散々アバターや BGM など馴染んでしまったので全く違和感がない。 セガサミーの術中にハマっているとも思えるが、そこにもそんなに抵抗はない。 楽しければそれで良い (だけどパチスロは絶対に手を出さない)。

久々に d アニメストアを契約。 月 432 円で見放題なのは秀逸。 画質もいいしダウンロードして後で電車内で観ることもできる。 ただ、ツインエンジェル BREAK に関して言えば、いい年した中年が電車内で観るのはかなり恥ずかしい雰囲気ではある……。 3 話まで観てみたが、いかにもな王道ストーリーで話も分かりやすく、世界観も明るい感じだし正直悪くない。 こういう萌え系のアニメには少なからず「オタク」のイメージがあるのだが、イメージと作品の良さは関係がない。 変に色眼鏡で見てしまうのは良くない。

ちなみに私は「オタク」に関しては、言葉の定義にもよるが基本的に否定的な印象は持っていない。 「何か (多くは一般的にポジティブに受け入れられない趣味) に没頭するあまり人とのコミュニケーションに関して著しく注意を払わない者 (他人からどう見られるかに関して注意を払わない事も含む)」という意味であれば否定的な印象を持つかもしれないが、前半のみの特性……「何か (多くは一般的にポジティブに受け入れられない趣味) に没頭する人」なだけであればむしろ好ましいと思うくらいである。 今の「オタク」という言葉は一般的にどちらを指すのだろうか。 後者 (コミュニケーション能力はそれなりに有している) を指すのではないかと想像する。

年を取れば取るほど、人生においてコミュニケーション能力がとても重要であることを考えさせられる。 いついかなる時でも万能に役に立つという意味でこれ以外の能力は考えつかない。

議事録作成

私はよく会議で議事録を作成しているのだが、ノート PC を持ってくるにしても結構重量があって負担があるので Android タブレットに外部キーボードを繋いで同じように使えないかと考えた。 Happy Hacking Keyboard Professional JP は会社に置いてあるので micro USB - USB 変換アダプタさえあればいけそうだ。 変換アダプタは Amazon で格安で手に入れることができた。

少々使い勝手は慣れが必要だが悪くない

メモ帳としては普段使用している Google Keep を使用した。 ローマ字打ちしか選択できないが最近はなるべく打つようにしているのでそんなに詰まらずに打てる。 各種ショートカット (F7 ~ F10 や Ctrl + A, V など) も Windows と共通になっているようで同じ感覚でいけそうだ。

1 つ、入力メソッドの切り替えに関しては半角 / かなキーを押すか Win + スペースでないといけなかった。 私は入力メソッドの切り替えを多用する打ち方をしているので慣れが必要である。 今手元に英語キーボードが無いので試せないのだが英語キーボードの場合は Win + スペースのみだろうか。

会社で打つ場合はこれでもいけそうだが、旅行・ツーリング先で打つ場合にわざわざ比較的コンパクトとはいえそれなりに重量もある HHKB を持っていくのはどう見てもスマートではない。 また、アダプタで接続できるとはいえキーボードの線が伸びているのも不格好だ。 今後手元に届くであろう「かえうち」が使えないという難点はあるが、それと比較してもコンパクトで安価な Bluetooth キーボードがあれば最高な気がする。 ちょっと検討してみようかと思う。

早速購入した HUAWEI MediaPad M3 Lite 10 をデバッグで使用してみた。 やはり画面が広い端末だと机に置いた状態でとても見やすくていい感じだ。 ただ最初の状態では Mate 9 と同様に Logcat にアプリ側で出力しているデバッグログ (例えば Log.d("HOGE", "FUGA")) が出力されない。 幸いにも Mate 9 と同様の対処方法で出力することができた。 Ascend Mate 7 も同様の方法のようであるし、多くの HUAWEI 端末は全てこの方法でいけるのではないかと思われる。

念の為ここにその方法を書き残しておく:

  1. 端末の電話アプリを起動
  2. *#*#2846579#*#* とダイヤルする
  3. 何やら黒い画面が開くので「1. Background Settings」を選択
  4. 「3. LOG Settings」を押下
  5. 「AP Log」「Charge Log」「Sleep Log」にチェックを入れる (AP Log だけでもよい?)

何と 10 インチタブレットである MediaPad M3 Lite 10 には電話アプリが入っているのである。 ということは恐らく音声通話 SIM を挿せばちゃんと携帯電話として使用できるということなのだろう。

ちなみに上記の方法で設定しても再起動をした時など設定が解除されている時がある。 その時は面倒だがまた同様の手順でデバッグログを ON にする。 何度もやっていると*#*#2846579#*#* も記憶してしまうので今になってみるとそれほど不都合はない。 開発者でない多くの人に対してはわずかとはいえ電力消費をするであろうデバッグログは無用の長物であるので、初期状態で出力が OFF になっているという HUAWEI 端末の配慮は悪くないと思う。

県道 210 号線のつもりだったが

滝沢ダム

今日も天気はそんなに良くなさそうだが、このところ雨続きで全然行けていなかったので今日こそはとツーリングに出掛けた。 秩父の滝沢ダムに目標を定める。 本当はその先の県道 210 号線からふれあいの森の方に行って、適当なところで座ってまったりしようかと思っていたのだが、生憎ガソリン残量が心許なくなってしまい、かつ周辺にガソリンスタンドがないので仕方なく次回のお楽しみとした。

Google マップナビに進路を任せていたので、彩甲斐街道ではなくその南にある林道のような道で中津川に南から西にアプローチする形となってしまった。 途中「塩沢展望広場」があったので立ち寄ってみたが、周りが木に囲まれており展望広場といっても全く展望は良くなかった。 その展望広場よりも途中途中で道端にバイクを停めて湖側を眺めた方がよほど展望が良い。

彩甲斐街道の方に合流したのでそのまま東に引き返し、滝沢ダムに着いた。 以前行った浦山ダムと同様にエレベーターで内部に潜ってちょっとした展示を鑑賞することが出来た。 とはいっても浦山ダムの方には別の資料館や食堂があったりしたので、総合的には浦山ダムの方が楽しめるような気がした。

階段で下からダムの上まで登る

滝沢ダム横の階段

このダムの特徴として、エレベーターで降りた下から上まで登れる階段が併設されている、というのがあった。 435 段あり 108 m 上に登る。 所要時間 15 分らしい。 折角なので運動代わりに軽い気持ちで登ってみた。

小学生らしき子どもたちも同じ時に登っていっていたのだが、登るペースを考えていないようだったのでやはり途中でバテてしまっていたようだ。 かくいう私も途中でどうにも疲れてしまったのだが、レッグポーチにお茶のペットボトルを忍ばせていたので何度か取り出して水分補給をした。 結果子どもたちと同じくらいのペースで登りきることが出来たので、自分にはまだそのくらいの体力はあることが分かった。 しかし最後まで登って疲れ切ってしまいしばしベンチで休憩した。 今日は 8 月とは思えないくらい涼しかったから良かったものの、これが暑い日だったら相当きつかったに違いない (むしろ登っていなかっただろう)。

今日の天気予報で夕方から天気が悪くなるだろうということを知っていたので、ダムを堪能した後はそのまま帰宅した。 秩父にはしばらく来れていなかったのでいい気分転換になった。 あと、今日は何故かピースサインを出すライダーが多かったので自分も何度か返した。 この習慣は昔からあるが、自分からは出さないが悪い気はしないのでなるべく返すことにしている。

今日注文していた MediaPad M3 Lite 10 が届いた。 ショップの発送がとても早かった。 本来は数日使ってからインプレを書くわけであるが Nexus 9 LTE でも書いた通り私はタブレット端末に関してはこれまで何台も使ってきているので数時間触れば何となく長所・短所が見えてきたのでファーストインプレッションとして書き留めておくことにする。

10 インチ Android タブレットにしては軽い

最近ではこの程度は当たり前なのかもしれないが 10 インチで 460 g と iPad 2017 年モデル並である。 最初に持った時「軽い」と感じた。以前使っていた 10 インチタブレットは 600 g 超えだったのでそれに比べると随分軽く感じる。 勿論人によっては「やっぱりちょっと重い」と思うかもしれないが、正直技術の進歩に驚かされた。

スペックの低さはあまり気にならない

この端末は発売時期が最近なのに対し CPU が非力だと言われているが、確かに Mate 9 のようなハイエンドモデルと比べるとモタツキを感じるものの不快に思うほどの遅さは感じず必要十分だと思った。 MJ モバイルなどプレイしてみたが特に問題は感じられなかった。 勿論 YouTube 視聴や Kindle など全く問題ない。 少なくとも Nexus 9 よりはキビキビと動作する。

唯一みんゴルは元々処理が重いせいか少しコマ落ちのような形になりプレイし辛さは感じた。 ゲームを主目的にする場合は注意が必要だろう。

プリインストールアプリが少ないのがいい

HUAWEI の端末はどれもプリインストールされているアプリが少ない上簡単にアンインストールすることができるのが好印象なのだが、この端末でもしっかりそれが踏襲されていた。 また HUAWEI 独自のランチャーである EMUI は相変わらず Android デフォルトのようなシンプルな見た目で良い。

物理キーをナビゲーションボタンにすることができる

この端末は指紋認証用のセンサーが前面下部に付いているのだが、そのセンサー部分を使用して Android の「戻る」「ホーム」「履歴」のナビゲーションボタンの機能をもたせることができる。 シングルタップで「戻る」、長押しで「ホーム」、左右スワイプで「履歴」といった感じだ。 これが意外と使いやすい。 Android デフォルトだと画面下の領域にこのナビゲーションボタンが配置されてしまうのだが、それが無くなるので表示領域も少し広くなる。

勿論 Android デフォルトのボタン配置にすることもできるので使いやすい方を選べば良い。

音は良いことは良いが過度の期待をしてはいけない

この端末はスピーカーが上下 4 箇所についているのでさまざまなレビューサイトで「音がいい」と紹介されている。 確かにそれは間違いではなかったのだがあくまで「タブレットにしては音がいい」というだけであるので過度の期待は禁物である。 納得できる部分だったのでそこまで不満はない。

値段に対する満足度がそこそこ高い

最後に、この端末は Wi-Fi モデルで 3 万円を切り、LTE モデルでも 3 万 5 千円を切るというミドルレンジモデルの価格帯である。 だが私が今まで使ってきたタブレットは 5 万円若しくはそれ以上するものでも動作に突っかかりを感じたり重量があったりして少なからず不満があったのも確かだ。 このタブレットはこの価格で以前感じたような不満をほとんど感じない。

ただ iOS が許容できるのであれば Wi-Fi モデルであればもう少し出せば iPad 2017 年モデルが買えるのでそちらがいいだろう。 このタブレットはどうしても Android の 10 インチ級の端末が使いたい人向けだと思った。 ゲームなどの処理が重い用途でなければ恐らく十分満足できるだろう。

後日改めてレビューした

2 週間使った感想として後日 MediaPad M3 Lite 10 改めてレビューを書いたのでそちらも参照頂ければ幸いである。

普段であれば楽天スーパーセールまで待つところなのだが、楽天は少し高いのと、早めに買っておけば月末に行く予定の旅行でも使えそうなので結局 LTE モデルを kakaku.com の最安でポチった。 合わせて amazon の方で 1,000 円くらいのケースを購入。 もっと良さそうな 10 インチのモデルが出たら買い換えるつもりなのでケースに入れて綺麗に使っておく (ヤフオクの買取価格が上がる)。 まぁこの手のガジェットは常に処分するときの事を考えて綺麗に使うところではある。

ちなみにデータ SIM であるが LINE モバイルではなくイオンモバイルにした。 月 518 円 (税込) と 22 円安いというのは誤差程度としても、キャンペーンで入会金が 1 円だったのが大きいとみた。 また、イオンモバイルは最低契約期間がなくいつ解約しても違約金が発生しないらしいのもお手軽でいい。

SIM は一週間くらいかかるようなので旅行には間に合わないかもしれないが、別に SIM が無くても Wi-Fi モデルだと思って使えばどうということはない。 この MediaPad M3 Lite 10 は Wi-Fi モデルと LTE モデルの価格差がそれほど無いのが魅力だ。 iPad (2017) だとこうはいかない。

10 インチタブレットという誘惑

私は昔から 10 インチ若しくは 9 インチ級のタブレットを度々変えては使用してきた。 このあたりの系譜は Nexus 9 LTE に書いた通りだ。 ただ、実は Nexus 9 に関しては Mate 9 を購入した際に手放してしまい、しばらくタブレット無しの生活を送っていた。 Mate 9 は 5.9 インチとなかなかの画面サイズなのでタブレット無しでもいけるだろう、という打算もあった。

今年になって HP Spectre x360 という 2-in-1 ノート PC を購入したのだが、購入当初これが「タブレット PC のように (Android や iOS タブレットの代わりとして) 使用できるだろう」と思っていた。 2-in-1 なのでキーボード部分を 360 度折りたたんでタブレットのようにして使用することが出来るからだ。 ただ、この使用方法は 1 回だけ試してみて「これは無いな」と思ってしまった。 本体が 1.3 kg もあるのでタブレットスタイルで持つと全く快適ではないし Windows 10 自体 Android と比べるとタブレットモードがあるとはいえまだまだタッチ操作の最適化が進んでいるとは言い難いところはあった。 あと、ノート PC は綺麗な液晶で見たいというのに慣れてしまっていて、指で触ってしまうと指紋が残ってしまうのが気になるところでもあった。

HUAWEI の 10 インチタブレットミドルレンジモデル

Mate 9 と合わせるわけではないが今回気になったのも HUAWEI の 10.1 インチタブレット MediaPad M3 Lite 10 である。 Lite という名前が付いているが MediaPad M3 10 Plus などという製品はない。 ただ、これの下位に位置するモデルとしては MediaPad T3 10 がある。 だがこの下位モデルの方はスペックも相当低く、液晶も 1280x800 と粗いもので全く購入対象とするには至らなかった。

この MediaPad M3 Lite 10 は Wi-Fi モデルは 3 万円を切るし LTE モデルであったとしても 3 万 5 千円弱という手頃な価格が魅力と言える。 ただその代わりスペックが低めで各種ベンチマークも振るわない結果となっており、数値上は Nexus 9 よりちょっと劣る程度しか出ない端末らしい。 流石にそれはないんじゃないか、と思い 2ch のスレッドなども漁ったが、そもそも Nexus 9 があのベンチマーク結果とは思えないくらい処理が緩慢な端末だったらしい。 私は Nexus 9 を使っていてそんなに不満には思わなかったので、恐らくこの MediaPad M3 Lite 10 でもいけるだろうと踏んだわけだ。 個人的には 5 万円くらい出してももう少し高スペックの端末が良かったのだが、現状そういうものが存在しないので仕方がない。 10 インチ級のタブレットは作ってもあまり売れないのだろう。

ちなみに MediaPad M3 というモデルは 8.4 インチのもっと高スペックのタブレットなのであるが、8.4 インチと少し小さいのが気になったので泣く泣く選択肢から外した。 Mate 9 と釣り合いをとるには 10 インチくらいないと気になるところだ。

使用用途としては Mate 9 で唯一画面が小さすぎて使う気にならなかった Kindle や PDF (技術書) 閲覧、そして Amazon ビデオや d アニメなどの動画、MJ モバイルといったところだ。 このタブレットを購入した場合 Mate 9 には引き続きバイクのナビとして活躍してもらうつもりだ。

Nexus 9 を使用していた時に挿していたデータ SIM は解約してしまったのだが、もし使うとしたら LINE モバイルあたりの 1G プランにするだろう。 月額 540 円ととても安い。 良い時代になったものだ。

素の AsyncTask は冗長過ぎる

例えば「重い処理をワーカースレッドで行い戻ってきた文字列を Toast で表示する」といった処理を AsyncTask で共通化せずに無名クラスで実装すると以下のようになる:

object : AsyncTask<Unit, Unit, String>() {
    override fun doInBackground(vararg p0: Unit?): String {
        Thread.sleep(3000)
        return "めっせーじ"
    }

    override fun onPostExecute(result: String?) {
        Toast.makeText(context, result, Toast.LENGTH_LONG).show()
    }
}.execute()

Kotlin かつちょっとした処理を書こうとしているだけなのにえらく冗長なコードになっている。 この問題点は AsyncTask が 3 つも raw type (ジェネリクスの型パラメーター) を要求する抽象クラスになっていることに加え、Kotlin の場合無名クラスを定義するのに object : (継承元クラス) といった割と仰々しい構文を使わなければならないことだ。

このような無名クラスを使うのではなく、Kotlin のシンプルなラムダ式を渡せばいいようにしたい。

SimpleAsyncTask

以下、内部的には AsyncTask を使用するが各種メソッドの override が必要な時のみラムダ式を渡せばいいように実装してみた:

class SimpleAsyncTask<R>(private val background: () -> R,
        private val pre: () -> Unit = {}, private val post: (R) -> Unit = {},
        private val cancelled: () -> Unit = {}) : AsyncTask<Unit, Unit, R>() {
    override fun onPreExecute() = pre()
    override fun doInBackground(vararg p0: Unit?): R = background()
    override fun onPostExecute(result: R) = post(result)
    override fun onCancelled() = cancelled()
}

AsyncTask が持つ経過表示を行うための機能 onProgressUpdate() はほとんど使用しないのでバッサリ切った。 また、パラメータを AsyncTask().execute(param) のように渡せる機能も共通化しないのであれば必要ないので、今回即時実行するケースのみにターゲットを絞り削った。 これで以下のように使える:

// 一番最初の AsyncTask の例と等価のコード
SimpleAsyncTask(background = {
    Thread.sleep(3000)
    "めっせーじ"
}, post = { Toast.makeText(context, it, Toast.LENGTH_LONG).show() }).execute()

あとすこし改善

かなりシンプルにはなったが AsyncTaskexecute() をコールし忘れて発火されないという凡ミスがよくある。 どうせパラメータを渡さないのだから execute() を書く必要はない。 そこで、以下の関数をトップレベルに配置する:

/**
 * SimpleAsyncTask を使用し非同期で実行する.
 *
 * @param R 非同期実行スレッドの戻り型
 * @param background 非同期実行処理
 * @param pre 非同期実行前処理 (未セットならば処理を行わない)
 * @param post 非同期実行後処理 (未セットならば処理を行わない)
 * @param cancelled 非同期実行キャンセル時処理 (未セットならば処理を行わない)
 */
fun <R> doAsync(background: () -> R, pre: () -> Unit = {}, post: (R) -> Unit = {}, cancelled: () -> Unit = {})
    = SimpleAsyncTask<R>(background, pre, post, cancelled).execute()

これで以下のように書ける:

doAsync(background = {
    Thread.sleep(3000)
    "めっせーじ"
}, post = { Toast.makeText(context, it, Toast.LENGTH_LONG).show() })

Kotlin の型推論が強力なおかげで <R> を明示する必要はない。 とてもシンプルで素晴らしい。

私が今までやってきたプログラミング言語としては Java, Kotlin, PHP, Swift, Python, VBA, JavaScript, TypeScript といったところで Scala, Haskell も勉強したことはあるが実際に作品で使用したことはない。 このうち仕事ですぐにでも使える自信があるのは、今仕事で使っているので当然ではあるのだが Kotlin, Java, PHP, JavaScript である。 TypeScript はタイピング向けサイトで実験的に使用しているのみで構文を詳しく覚えているわけではない……がそんなに難しいこともないので仕事で使うのもそんなに問題はないだろう。 Python に関してはこの Blog の開発言語であり個人的に好きな言語なのだが仕事では一切使用したことがないので習熟度もそれなりといった感じになっている。

そういえば一時期本気で Python プログラマになろうと転職を考えたこともあったのだが、結局今の職場で働いている。 勿論言語の選定も重要なのだが、開発で一番大切なのはプログラミング言語ではないことは明らかである。 デキるプログラマはどの言語を使っても綺麗なコードを書くし、逆もまた真であるといえる。 「どの配列を選んでもタイピング速度に大して影響はない」というタイピングに似ている。

習熟度も高く言語仕様としても好きな言語は今は Kotlin が出来たので (JetBrains や日本での普及を目指したエンジニアに) 正直感謝している。 Kotlin の全てが好きだとはとても言えないが Java に比べると非常にシンプルに書けとてもモダンなテイストのコードに仕上がる。 ただでさえ仕事のコーディングは大変なのだから、少しでも好きな言語・環境を使用して精神的な負担を軽減すべきだ。

社会人は仕事をしている時間が大半を占める。 その時間内で触れている言語が一番使用時間が長いということになるので望んでいるいないに関わらず一番上達する。

慣れている PHP で作るのか。 好きな Python を使うのか。 毎回悩ましい問題だ。 開発環境 (PhpStorm, PyCharm) も有料だ。 趣味で使うだけなのにそんなにお金をつぎ込むのもなかなか気が進まない……。

タイプウェルオリジナル総合 SG

ずっと上げることができなかった大小文字のみを今日やっと上げることができ、タイプウェルオリジナルの総合レベルが SG となった。 大小のみを上げることができたおかげで、一気に SG の中間までポイントが溜まった。 後は大小文字混合、すべてのキー、数字のどれかを程よく上げれば SF に到達できそうに見える。

正直練習を続けるのがなかなか厳しくなってきた印象ではあるが、オールラウンダーを目指しているので最低 SS で出来れば XJ 以上まで頑張りたいところだ。

順位 ポイント 総レ 達成日
- 935729 SG SH SG SI SE 17/08/14
- 920095 SH SJ SG SJ SF 17/08/09
- 913348 SI SJ SH SJ SH 17/08/06
593 900067 SJ A SH B SI 17/08/03

今日は迎え盆なので実家に行って墓参りをした。 やはり東京方面に帰ってくる時の渋滞はかなり凄かった……。

昼食は久々のお寿司を頂いた。 11:30 頃お店に行ったのだが、既に 4 組ほど待ちが発生していた。 12 時前に入ればすぐいけると思っていたので意外だった。 その後 30 分ほど待ってようやく席につけた。

旅行に行こうという話になって、まだ夏なので涼しいところがいいだろうという事で今月に蓼科高原に行くことにした。 今月家族旅行に行くのであれば尚更直近のツーリングはお預けになるだろうか。

天気予報的には良くなったが

連休に入る前は金曜、土曜と天気が悪いという予報だったが、今見ると天気としてはとりあえず雨は降らないということになっていた。 出かけても良かったのだが、昨日も適当な街乗りで済ませて今日もそうなりそうだったので自粛しておいた。 首都圏ツーリングプランを使うにしても 2 日間有効というのが曲者で、1 日だけ使うのは何かちょっと損した気分になるというのもある。

PHP か Python か

業務で使用するのは PHP や Java, Kotlin なのでそちらの勉強をしてもいいのだが、個人的には Python が好きなので Effective Python など引っ張り出してきて読んでいた。 実はこの自作 Blog も Python で出来ているのだが、そろそろ何かカスタマイズしてもいい頃か、とも思った。

PHP も 7.0, 7.1 になってかなり便利になったが、そうはいっても Python の言語仕様を見てしまうと依然として Python に惹かれるのは確かだ。

今日は久々にバイクに乗りたかったが、天気が悪そうだったので普通に街乗りをすることにした。 8 月とは思えないほど涼しく、メッシュジャケットだと少し肌寒いほどでバイクに乗るには最適な日ではあったが空模様がどうにも良くない……。

栃木の山にでも行こうかなとある程度北に行ったところで少し思案してそのまま高速で帰宅した。 高速の道路情報を参照すると下り線 (東京から栃木に行く方面) は物凄く渋滞していた。 3 連休初日で帰省する人が多いのだろう。

明日も天気が悪そうなので大人しく家でゆっくりすることになりそうである。

onSaveInstanceState 後に表示しようとするとクラッシュする DialogFragment

DialogFragment は例えば以下のように表示できる:

// MainActivity 内のコード
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    MyDialogFragment().show(supportFragmentManager, null)  // 表示処理
}

class MyDialogFragment : AppCompatDialogFragment() {
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog
            = AlertDialog.Builder(activity).setMessage("Hello World").create()
}

通常はこれで問題ないのだが、例えば以下のようにして画面を開いた直後に「スリープ状態にする」「画面を閉じる」など行うとクラッシュする:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    // 5 秒待ってから表示. この間にスリープやバックグラウンドに回すとクラッシュ!!
    Handler().postDelayed({ MyDialogFragment().show(supportFragmentManager, null) }, 5000)
}

class MyDialogFragment : AppCompatDialogFragment() {
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog
            = AlertDialog.Builder(activity).setMessage("Hello World").create()
}

上記のように数秒待ってからダイアログ表示することなどそうそうないと思われるかもしれないが、昨今は通信を伴ったアプリの開発が当たり前であり通信の結果やエラー表示をダイアログで行っていたりするとよくこのエラーを目にすることとなる。

クラッシュの内容は java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState といった感じで恐らく Fragment を使っている人であればよく見ているのではないかと思う。 要するに Activity や Fragment の onPause() から onResume() 間には Fragment (DialogFragment) を操作できないというわけであるが、これの回避が思ったより難しい。

onPause() から onResume() 間に操作しようとしている DialogFragment の show()dismiss()ouResume() の後に pending するという方法でうまく回避できたのでここに記しておく。

前提

今回は前提として私が Qiita に以前書いた DialogFragment の共通化処理を入れているものとする。 例えば以下のような MyDialogFragment が用意されているものとする:

class MyDialogFragment : AppCompatDialogFragment() {

    /** コールバック */
    private val mCallback: Callback by lazy {
        if (targetFragment is Callback) {
            targetFragment as Callback
        } else if (activity is Callback) {
            activity as Callback
        } else {
            throw UnsupportedOperationException()
        }
    }

    /** リクエストコード */
    private val mRequestCode: Int
        get() = if (arguments.containsKey("request_code")) arguments.getInt("request_code") else targetRequestCode

    /**
     * MyDialog で何か処理が起こった場合にコールバックされるリスナ.
     */
    interface Callback {

        /**
         * MyDialog で positiveButton, NegativeButton, リスト選択など行われた際に呼ばれる.
         *
         * @param requestCode MyDialogFragment 実行時 mRequestCode
         * @param resultCode DialogInterface.BUTTON_(POSI|NEGA)TIVE 若しくはリストの position
         * @param params MyDialogFragment に受渡した引数
         */
        fun onMyDialogSucceeded(requestCode: Int, resultCode: Int, params: Bundle) = Unit

        /**
         * MyDialog がキャンセルされた時に呼ばれる.
         *
         * @param requestCode MyDialogFragment 実行時 mRequestCode
         * @param params MyDialogFragment に受渡した引数
         */
        fun onMyDialogCancelled(requestCode: Int, params: Bundle) = Unit

        /**
         * MyDialog で multiChoice した際に呼ばれる.
         *
         * @param requestCode MyDialogFragment 実行時 mRequestCode
         * @param position チェック位置
         * @param checked チェックされたか否か
         */
        fun onMyDialogMultiChoiceClicked(requestCode: Int, position: Int, checked: Boolean) = Unit

        /**
         * MyDialog で dateSet した際に呼ばれる.
         *
         * @param requestCode MyDialogFragment 実行時 mRequestCode
         * @param year 年
         * @param month 月
         * @param date 日
         */
        fun onMyDialogDateSet(requestCode: Int, year: Int, month: Int, date: Int) = Unit
    }

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val listener = DialogInterface.OnClickListener { _, which ->
            dismiss()
            mCallback.onMyDialogSucceeded(mRequestCode, which, arguments.getBundle("params"))
        }
        val isProgress = arguments.getBoolean("is_progress")
        val title = arguments.getString("title")
        val message = arguments.getString("message")
        val positive = arguments.getString("positive")
        val negative = arguments.getString("negative")
        val items = arguments.getStringArray("items")
        val multiChoiceItems = arguments.getStringArray("multi_choice_items")
        val multiChoiceItemValues = arguments.getBooleanArray("multi_choice_item_values")
        val date = arguments.getSerializable("date") as Date?
        isCancelable = arguments.getBoolean("cancelable")

        if (isProgress) {
            return ProgressDialog(activity).apply { message.let { setMessage(it) } }
        } else if (date != null) {
            val cal = Calendar.getInstance(Locale.getDefault())
            cal.time = date
            val picker = DatePickerDialog(activity, { _, year, month, date ->
                mCallback.onMyDialogDateSet(mRequestCode, year, month, date)
            }, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DATE))
            title?.let { picker.setTitle(it) }
            message?.let { picker.setMessage(it) }
            return picker
        } else {
            val builder = AlertDialog.Builder(activity)
            title?.let { builder.setTitle(it) }
            message?.let { builder.setMessage(it) }
            positive?.let { builder.setPositiveButton(positive, listener) }
            negative?.let { builder.setNegativeButton(negative, listener) }
            items?.let { builder.setItems(items, listener) }
            multiChoiceItems?.let {
                builder.setMultiChoiceItems(multiChoiceItems, multiChoiceItemValues) {
                    _, position, checked ->
                    mCallback.onMyDialogMultiChoiceClicked(mRequestCode, position, checked)
                }
            }
            return builder.create()
        }
    }

    override fun onCancel(dialog: DialogInterface?) {
        mCallback.onMyDialogCancelled(mRequestCode, arguments.getBundle("params") ?: Bundle())
    }
}

通常のタイトルやメッセージを持ったダイアログ表示に加えて、複数のアイテムから 1 つを選択するものや複数チェックできるもの、あと DatePickerDialog での日付選択や ProgressDialog での経過表示も対応している。 これを呼ぶ場合用にもショートカット用のメソッドを以下のように用意する:

/**
 * MyDialogFragment を表示する.
 *
 * @param requestCode リスクエストコード
 * @param params Bundle パラメータ
 * @param title タイトル
 * @param message メッセージ
 * @param positive positiveButton ラベル
 * @param negative negativeButton ラベル
 * @param items シングルタップ選択用の items
 * @param multiChoiceItems 複数選択用の items
 * @param multiChoiceItemValues 複数選択用の items (値)
 * @param date DatePicker 用の初期値
 * @param tag Tag
 * @param cancelable キャンセル出来るか否か
 */
fun showDialog(requestCode: Int = -1, params: Bundle = Bundle(), title: Any? = null,
        message: Any? = null, positive: Any? = null, negative: Any? = null,
        items: Any? = null, multiChoiceItems: Any? = null, multiChoiceItemValues: BooleanArray? = null,
        date: Date? = null, tag: String = "default", cancelable: Boolean = true) {
    fun getStringArray(x: Any): Array<String> = when (x) {
        is Int -> resources.getStringArray(x)
        is ArrayList<*> -> x.toArray(arrayOfNulls(0))
        is Array<*> -> x.map(Any?::toString).toTypedArray()
        else -> throw IllegalArgumentException()
    }
    if (this !is MyDialogFragment.Callback) {
        throw UnsupportedOperationException("Using MyDialogFragment must implement Callback to Fragment")
    }
    val args = Bundle()
    title?.let { args.putString("title", if (it is Int) getString(it) else it.toString()) }
    message?.let { args.putString("message", if (it is Int) getString(it) else it.toString()) }
    items?.let { args.putStringArray("items", getStringArray(it)) }
    multiChoiceItems?.let { args.putStringArray("multi_choice_items", getStringArray(it)) }
    multiChoiceItemValues?.let { args.putBooleanArray("multi_choice_item_values", it) }
    date?.let { args.putSerializable("date", it) }
    positive?.let { args.putString("positive", if (it is Int) getString(it) else it.toString()) }
    negative?.let { args.putString("negative", if (it is Int) getString(it) else it.toString()) }
    args.putBoolean("cancelable", cancelable)
    args.putBundle("params", params)

    MyDialogFragment().apply { arguments = args }.show(supportFragmentManager, tag)
}

ちょっと複雑そうに見えるのは、各種引数をリソース ID と文字列の双方で渡せるように考慮を入れているからである (その為多くの型を Any? にしている)。 このメソッドを使用して前述の MyDialogFragment を show() するには以下のように書く:

// 5 秒待ってから表示
Handler().postDelayed({ showDialog(message = "Hello World") }, 5000)

この時点では onPause() 後の pending の対応が入っていないため先ほどと同じようにスリープにしたり画面を閉じたりすると IllegalStateException がスローされる。

対処

この対処を行う上で残念なのが結局 Fragment を拡張するようなパターンが必要 (BaseFragment のようなもの) になってしまうということだ。 どうしても onResume() と密に結びついてしまうので仕方がない。 これを避けようとすると作成した Activity や Fragment の onResume() の前に毎回お決まりのコードを入れなければならないので若干辛いものがある。

abstract class MyFragment : Fragment(), MyDialogFragment.Callback {

    /** 復帰のために貯めておく PTDialogFragment の表示データ */
    private val mDialogData: MutableList<Bundle> = mutableListOf()

    /** 復帰のために貯めておく dismissDialog のデータ */
    private val mDismissingTags: MutableList<String> = mutableListOf()

    /**
     * ProgressDialog を dismiss する.
     *
     * @param tag Tag
     */
    fun dismissDialog(tag: String = "progress") {
        if (isResumed) {
            fragmentManager.findFragmentByTag(tag)?.let {
                if (it is AppCompatDialogFragment) {
                    it.dismiss()
                } else {
                    throw UnsupportedOperationException()  // DialogFragment ではない
                }
            }
        } else {
            mDismissingTags += tag  // resume でない場合は pending する
        }
    }

    /**
     * ProgressDialog を表示する.
     *
     * @param message メッセージ
     * @param tag Tag
     * @param cancelable キャンセルできるか否か
     */
    fun showProgressDialog(message: Any? = null, tag: String = "progress", cancelable: Boolean = true) {
        val args = Bundle()
        args.putBoolean("is_progress", true)
        message?.let { args.putString("message", if (it is Int) getString(it) else it.toString()) }
        args.putBoolean("cancelable", cancelable)

        // resume でない場合は pending する
        if (isResumed) {
            with(MyDialogFragment()) {
                arguments = args
                setTargetFragment(this@MyFragment, 0)
                show(this@MyFragment.fragmentManager, tag)
            }
        } else {
            // 復帰させるために requestCode, tag を格納する
            args.putString("tag", tag)
            mDialogData += args
        }
    }

    /**
     * PTDialogFragment を表示する.
     *
     * @param requestCode リスクエストコード
     * @param params Bundle パラメータ
     * @param title タイトル
     * @param message メッセージ
     * @param positive positiveButton ラベル
     * @param negative negativeButton ラベル
     * @param items シングルタップ選択用の items
     * @param multiChoiceItems 複数選択用の items
     * @param multiChoiceItemValues 複数選択用の items (値)
     * @param date DatePicker 用の初期値
     * @param tag Tag
     * @param cancelable キャンセル出来るか否か
     */
    fun showDialog(requestCode: Int = -1, params: Bundle = Bundle(), title: Any? = null,
            message: Any? = null, positive: Any? = null, negative: Any? = null,
            items: Any? = null, multiChoiceItems: Any? = null, multiChoiceItemValues: BooleanArray? = null,
            date: Date? = null, tag: String = "default", cancelable: Boolean = true) {
        fun getStringArray(x: Any): Array<String> = when (x) {
            is Int -> context.resources.getStringArray(x)
            is ArrayList<*> -> x.toArray(arrayOfNulls(0))
            is Array<*> -> x.map(Any?::toString).toTypedArray()
            else -> throw IllegalArgumentException()
        }
        if (this !is MyDialogFragment.Callback) {
            throw UnsupportedOperationException("Using PTDialogFragment must implement Callback to Fragment")
        }

        // 既に detach されている場合は何もしない
        if (isDetached) return

        val args = Bundle()
        title?.let { args.putString("title", if (it is Int) getString(it) else it.toString()) }
        message?.let { args.putString("message", if (it is Int) getString(it) else it.toString()) }
        items?.let { args.putStringArray("items", getStringArray(it)) }
        multiChoiceItems?.let { args.putStringArray("multi_choice_items", getStringArray(it)) }
        multiChoiceItemValues?.let { args.putBooleanArray("multi_choice_item_values", it) }
        date?.let { args.putSerializable("date", it) }
        positive?.let { args.putString("positive", if (it is Int) getString(it) else it.toString()) }
        negative?.let { args.putString("negative", if (it is Int) getString(it) else it.toString()) }
        args.putBoolean("cancelable", cancelable)
        args.putBundle("params", params)

        // resume でない場合は pending する
        if (isResumed) {
            with(MyDialogFragment()) {
                arguments = args
                setTargetFragment(this@MyFragment, requestCode)
                show(this@MyFragment.fragmentManager, tag)
            }
        } else {
            // 復帰させるために requestCode, tag を格納する
            args.putInt("request_code", requestCode)
            args.putString("tag", tag)
            mDialogData += args
        }
    }

    override fun onResume() {
        super.onResume()
        mDialogData.forEach {
            with(MyDialogFragment()) {
                arguments = it
                setTargetFragment(this@MyFragment, it.getInt("request_code"))
                show(this@MyFragment.fragmentManager, it.getString("tag"))
            }
        }
        mDialogData.clear()
        mDismissingTags.forEach { dismissDialog(it) }
        mDismissingTags.clear()
    }
}

こんな MyFragment を用意する。 そうして以下のように MyFragment を継承した Fragment 上で MyDialogFragment を表示してみる:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    savedInstanceState ?: supportFragmentManager.beginTransaction()
            .add(android.R.id.content, AFragment())
            .commit()
}

class AFragment : MyFragment() {
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        
        // 5 秒後表示. 今度はスリープしてもクラッシュせずに復帰時にちゃんと Dialog が表示される!!
        Handler().postDelayed({ showDialog(message = "Hello World") }, 5000)
    }
}

今度はクラッシュしないし、画面復帰時に正しく表示しようとしていた Dialog が表示される。 肝は Dialog を表示しようとする時に isResumed でなかったら tag で表示しようとしている Dialog の情報を保存し onResume() 後に改めて表示するというところだ。 これを共通ダイアログに対して行っておくとこのようなボイラープレートコードをあちこちに仕込まなくて済むようになるので有用である。

タイプウェルオリジナル総合 SH

今日もタイプウェルオリジナルの総合レベルを SH に上げることができた。 今のところは 3 日に 1 レベルペースを維持できているが、今後どんどん上げにくくなってくると思われるので、どこまでこのペースが保てるかが興味深いところである。

配点が高い大小文字のみを前回の更新から全く上げることが出来ていない。 圧倒的に一番練習を行っているにも関わらず上げることができないでいるのがなかなか辛いところだ。 一方、ほとんど練習をしていない数字はどんどんレベルが上がっていく。 数字は実用上役に立つ場面がほとんどないのであまりモチベーションが上がらないのが難点なのだが、大小文字のみが上がらなくて疲れたら気分転換にしつこくやっていこうかと思う。

尚、タイプウェルオリジナルばかりやっている副作用として QWERTY での英語タイピングの腕も上がっているように見受けられる。 タイプウェルオリジナルはランダム文字列であるが、どうも実用上全く役に立たないというわけではないらしい。

順位 ポイント 総レ 達成日
- 920095 SH SJ SG SJ SF 17/08/09
- 913348 SI SJ SH SJ SH 17/08/06
593 900067 SJ A SH B SI 17/08/03

自宅の浴槽が壊れたのでメーカーサポートに修理を頼んでいた。 浴槽の湯を抜く栓を開ける為のボタンがあるのだが、それが押せなくなってしまったという故障。 修理はものの 30 分弱で終わったのだが、修理代が 1 万 2 千円かかった……。 まぁ出張修理というのは高いもので、技術者の拘束時間 (移動時間も含む)、交通費、電話サポートの人件費、部品代、など加算していくとその位になってしまうのは致し方ない。 ともかくこれで浴槽が直ったので数日間シャワーを浴びていたところだが今日から風呂に入れるというものだ。 この暑い中風呂か、と思われるかもしれないが、日本人として昔から真夏でも浴槽に浸かっていたので、どうも風呂に入らないと落ち着かない。 習慣というものはそうそう逸脱できるものではないようだ。

修理の応対など行っていたのと台風 5 号の接近があったので今日は有給休暇としておいた。 久々に有給休暇を取得したのでなかなか楽しい。 世間、学生は夏休みモードであるが、普通の社会人はそう気軽に休みも取得できない……。

最近ゲリラ豪雨のような激しい雨が多い気がする。 今日も帰宅途中でそんなゲリラ豪雨に見舞われ濡れながら帰った。 途中にラーメン屋があったのでそこで雨宿りしながら食事。 自宅の近くにあるそのラーメン屋は地元に根ざしたチェーン店で家族連れなどが多く賑わっている……。 この店には久々に入ったのだが、普通のラーメンを頂き満足する。 別にすごく美味いというわけではないが、以前食べた時と同じ味、同じ感想である。 しかしそれがいい。

外で食べる場合というのは、常に変わらない同じ味、同じ値段というのが長所になり得る。 むしろコロコロ替わるようでは辛い。 消費税などの正当な理由を伴わない値上げなどもそうだ。 これがちゃんと説明がなされていればいいのだが、ある日突然メニューを見るとしれっと値段を上げてくる店もある。 こういう店には何度か行ったことがあるが誠実さの欠片も見えなくて本当に失望する。 外食に限った話ではないが、これも一つの人と人とのコミュニケーションの形なのではないかと思う。

タイプウェルオリジナル総合 SI

3 日ぶりにタイプウェルオリジナルの総合レベルを上げることができた。 一番出遅れたのは配点が多い大小文字のみであったが、何とか今日 1.2 秒ほど記録を縮めて SJ に乗せることが出来た。

自分の場合月配列を SJ から SS まで上げるのに 1 ヶ月と 20 日かかったのだが、実は途中で配列改造 (月配列 2-263 式から月配列 K へ) を入れてしまったので月配列 2-263 式のまま一直線に目指していたら 1 ヶ月と 10 日程度で達成できていたと思われる。 これと比較してどうか、というのが気になるところである。 何しろタイプウェルオリジナルの難易度に関する情報はネット上を探してもほぼ無いからだ。

大小文字のみは SJ が 50 秒で 1 秒間隔で進んでいき SS が 40 秒とレベルアップしていける。 J から SJ までと比べて一見難易度が下がったように見えるが、ここまで上げるのに結構苦労しているので全く油断は禁物である。

順位 ポイント 総レ 達成日
- 913348 SI SJ SH SJ SH 17/08/06
593 900067 SJ A SH B SI 17/08/03

山間部は天気が悪そう

今日は雨が降っているわけではなかったが、ネットで天気予報を見る限りだと山間部はどうも雲行きが怪しいように見える……。 先々週ゲリラ豪雨に遭って痛い目を見たばかりなので、今日はツーリングには出かけず軽く街乗りをした後でスーパーで買い物をして家でゆっくりすることにした。

ちなみに明日も天気があまり良くなさそうに見える……。 どうもいい天気に恵まれない。

スマホのドラクエセール中

ふと Google Play のスクウェア・エニックスのアプリ一覧を見るとドラクエが軒並み 30% オフになっていた。 そういえばドラクエ 3 が 1,200 円であったがちょっと高いなと思ってずっと保留にしておいたのだが、今日見ると 840 円になっている……。 ということで購入して遊ぶことにした。 世間ではドラクエ 11 が盛り上がっているところだが、私は PS4 も 3DS も所持していない。 ハードを買ってまで遊ぼうとは思わない。

ドラクエ 3 であるが、このソフトは私が少年の頃遊んだ思い出深いソフトである。 しばらくプレイしていなかったが、基本的な進め方やピラミッドの仕掛けなどちゃんと覚えているものだから驚いた。 それだけ熱中してプレイしていたということであるが……。

今時のドラクエなので戦闘中に敵がアニメーションするのかと思っていたのだが、そうではなかった。 まぁアニメーションなしのほうがテンポ良く進められるというのはあるが、SFC 版でもアニメーションがあったのに……と思うとちょっと寂しい。

それにしてもドラクエ 3 のゲームバランスは秀逸である。 スイスイ進めてダーマ神殿でレベル上げをする。

結論

あれから散々いじりまわしてみてああでもない、これも違う、などと試行錯誤してみた。 最終的にたどり着いたのが以下の配列である:

/* 単打 */
[
  |ふ|ら|ゅ|ゃ|  |  |も|ー|  |  |
そ|こ|し|て|ょ|つ|ん|い|の|り|「|」
は|か|  |と|た|く|う|  |゛|き|れ|
す|け|に|な|さ|っ|る|、|。|゜|  |
]

[d],[k][
ぅ|ぁ|ぃ|ぇ|ぉ|  |  |  |  |  |  |
  |ひ|ほ|  |め|ぬ|え|み|や|  |  |  
ち|を|  |あ|よ|ま|お|  |わ|ゆ|  |
  |へ|せ|  |  |む|ろ|ね|  |・|  |
]

-shift[
!|@|#|$|%|’|&|*|(|)|-|=|¥|
  |  |  |  |  |  |  |  |  |;|[|]
  |  |  |  |  |  |  |  |  |  |’|~
  |  |  |  |  |  |  |,|.|?|  |
]

なんと終わってみると総合 XH を取得した月配列 K の安定版と異なる点は 「ち」をシフト側に移動したのみとなってしまった……。

まず「いうん」を QWERTY と同位置に合わせて混同を避けるという案だが思ったよりうまくいかなかった。 「う」の位置はやはり 2-263 式の J の位置が最善だと思ったのは、拗音の着地 (「しょう」「きょう」など) の打鍵感が U より J の方が良いのと文章の終わりが「ん」より「う」の方が圧倒的に多く (「~と思う。」「~に従う。」など) 、U. よりも J. の打鍵のほうが極めて自然だったのである。

今回特に悩んだのが長音「ー」の配置である。 何しろ月配列 K の安定版の位置だと「指を最上段に上げる」という挙動が QWERTY の - と同一の為混同に悩まされたからである。 この混同は右手小指を右に伸ばした位置に長音を配置した時は起きなかった。 そのため「ー」と「れ」の入れ替えを考えしばらく打ってみたわけだが、「れ」を薬指担当にしてしまうと「われ」というパターンが結構出てきて同指違鍵が発生してしまうことに気付いた。 「我々」「壊れる」「捕われる」「言われる」など結構出てくるのである。 また、小指側に移動した「ー」だが、これも小指担当の「き」と組み合わさった「キー」というパターンが結構出てきて結局小指連続パターンから逃れられないという結論になった。 では、思い切って「れ」をシフト側に移すか? しかし打鍵数が増えてしまう割にそんなにメリットがない……。 ということで前述の結論となったわけである。

但し今回の「ち」の移動には結構自信がある。 「ち」も月配列 K の歴史で何度となく動かしてきたかなであるが、ようやく定住場所を見つけたような感じだ。 以前の 7 のキーに対してシフト側に移動した KA という打鍵だが、この交互打鍵 KA は小指のほうがかなり短いため AK になってしまうというミスが起きにくくとても打ちやすい。 また A の位置から月配列 K の強みである各種拗音へと繋げやすい。

月配列 K は最上段も使用する四段配列であるが、最上段の中でも打ちにくい位置のキーである 1, 6, 7, 0 にはキーを配置していない。 実は 5 も打ちにくいのだが「ゃゅょっ」を全て単打に合わせるために頻度の低い「ゃ」を配置している。 固めておくことで「しゃ」「しゅ」「しょ」と同じような打鍵感でタイピングを楽しめるのが月配列 K のウリの 1 つである。

月配列 2-263 式はデファクトの名に恥じない出来

月配列 2-263 式は標準の状態でとても良く出来ている。 私も今回の変更で素の 2-263 式に戻そうか、と少し考えたほどである。

ただ、月配列 K の「シフトキー自体には文字を配置しない」、これが普段使いにおいてかなり効いてきたというのは今まで月配列 K を使ってきた感想であり、月配列 2-263 式に戻してしまうとまた連続シフトミスパターンに悩まされることになってしまう。 流石にそれだけは思いとどまったところだ。

タイプウェルオリジナル総合 SJ

タイプウェルオリジナルに関しては結構前から始めてはいたのだが、しばらくそのままにしていて 1 週間前ほどから本格的に再開しだした。 そして、今日遂に総合レベルにおいて SJ を達成することができた。 ここからが更に長い道のりの始まりと言えよう。

本気でオールラウンダーを目指していくとして Blog に SJ の段階からいつものように記録を付けていくことにする。 オリジナルがどの程度の難易度なのかというのは少し興味があるので、月配列の国語 K や Colemak の英単語と対比して見ていければと思う。

尚、今までの所感としては月配列を総合 SJ に上げる程度と同じか少し簡単な位ではないかと思う。 ただ、オリジナルに関しては種目ごとに大きく難易度差があり、使うキー数が少ない数字が一番簡単で、その次にレベル判定が甘い大小文字混在が来る。 難しいのはレベル判定が厳しい大 (小) 文字のみと使うキー数が多く右手小指連続が辛いすべてのキーだろう。

一番スコア配分が多い大 (小) 文字のみが難しいということで総合 SJ を目指す上での一番の壁は大小文字のみであることは間違いない。 タイプウェルのスコアの関係上ある程度満遍なく上げないといけないので、各種記号を日常的に打たない方は恐らくすべてのキーが一番難しいと思うことだろう。 但し、これはあくまで総合 SJ までの話であり、今後タイムが縮まっていくにつれてこの傾向は変わってくるものと思われる。

順位 ポイント 総レ 達成日
593 900067 SJ A SH B SI 17/08/03

今日はとても涼しかったのでバイクに乗ってみた。 帰りに雨に降られてしまったのだが、それでも涼しくて気持ちよく乗ることが出来た。

帰ってきたら先々週の最悪の雨だったツーリングの汚れを洗車で落とす。 バイクは車と違って洗う面積が狭いので、ただバケツに水汲んできて少しずつ水かけながら行うだけで大分綺麗になる。

週末の天気が回復傾向のようで嬉しい限りである。 可能であればまた安いビジネスホテルに宿泊するプランで 1 泊 2 日で行ってみたいが、悩ましいところだ。

モダン配列を使うのは楽がしたいから

Colemak を辞めてから 2 週間ほど月配列を使用せず QWERTY ローマ字のみで過ごしてみたのだが 1 年ほど使っていなかったにも関わらず最盛期に近い程度には速度が回復し月配列に近いくらいの速さになってしまった。 正直速さを求めるのであればローマ字でも JIS かな、月配列であってもそこまでは変わらない、というのが今までの結論だ。

月配列を使う理由は速さではない。 打鍵数を少なくして楽をしたいからのはずである。

「いうん」を QWERTY と同位置にすると「ん」が辛い

まず最初に書いておくが、かな出現頻度において「い」「う」「ん」は最上位に来るかなでありこれらの文字の配置は非常に重要である。

前回月配列 K の「いうん」をそれぞれ QWERTY の I, U, N と同位置に配置してみた。 「い」は元々の月配列 2-263 式でも QWERTY と同位置なのでいいのだが、問題は「う」と「ん」である。 QWERTY の U の位置はまずまず悪くない位置なのだが N の位置がいわゆる伸ばす位置であまり良くない。 ローマ字で散々打鍵しているのだが、それでも QWERTY でいうところの J の位置の方がいいに決まっている。

ということで、今回は「ん」だけどうしても QWERTY と異なる位置になってしまうのだが、月配列 2-263 式の「うとんが微妙に異なる」よりは「んだけ異なる」方が好ましいか、ということで素直に「う」と「ん」だけ入れ替えることにした。 あと「ん」を N の位置に持ってきてしまうと代わりに小書き「っ」というあまり嬉しくない文字を J に持ってきたりしなければならないという点もある。

「ち」の所在が困る

月配列 K で一番悩んでいるのが、右手小指連続パターンの脱却のために JIS キーボードでいうところの @ のキーからリストラした「ち」のかなである。 何しろ「ち」は以下の特性を持つので配置出来る場所が限られてしまう:

  • 拗音化する。「ちゃ」「ちゅ」「ちょ」「ちぇ」を無理なく打鍵できるように配慮する
  • 頻度が少ないとはいえ「ぢ」と濁点と組み合わさる

前者をクリアするには右手側に置くのが一番なのだが、後者もクリアしようとすると意外と置く位置に困ってしまう……。 以前の月配列 K では 7 のキーに置いていたのだが、この位置も最善とはいい難く、例えば「運賃」のようなワードで大幅に減速してしまうし、「ぢ」も実はそんなに打ちやすくはない。 今回はシフト側 A の位置に置いてみることにした。 試しに KAT, KA4, KA5, KAK4 の運指を試してみたが悪くはない。

「ら」「も」はオリジナルからそのまま上に持ってきた位置が最善

「ら」「も」に関しては月配列 K の歴史の中で何度も移動してきたのだが、結局オリジナルの月配列 2-263 式に対してそのまま上に持ってきた位置が一番打ちやすいという結論に達した。 例えば 8 のキーに「ら」を配置してしまうと直下にある「い」との相性が悪く、「依頼」などの割と多くのワードで同指違鍵パターンが発生してしまう。

長音や中黒、カギ括弧などはローマ字と同位置とする

他、長音や中黒、カギ括弧などは可能な限り QWERTY ローマ字と同位置とした。 そのまま配置できない場合はシフト側にした。

月配列 K

/* 単打 */
[
  |ふ|ら|ゅ|ゃ|  |  |も|を|  |ー|
そ|こ|し|て|ょ|つ|う|い|の|り|「|」
は|か|  |と|た|く|ん|  |゛|き|れ|
す|け|に|な|さ|っ|る|、|。|゜|  |
]

[d],[k][
ぅ|ぁ|ぃ|ぇ|ぉ|  |  |  |  |  |  |
  |ひ|ほ|  |め|ぬ|え|み|や|  |  |  
ち|  |  |あ|よ|ま|お|  |わ|ゆ|  |
  |へ|せ|  |  |む|ろ|ね|  |・|  |
]

-shift[
!|@|#|$|%|’|&|*|(|)|-|=|¥|
  |  |  |  |  |  |  |  |  |;|[|]
  |  |  |  |  |  |  |  |  |  |’|~
  |  |  |  |  |  |  |,|.|?|  |
]