VSCodeのVimプラグインでUndo/Redoの設定を調整する
私はVSCodeでVimプラグインを使っている。VSCodeのVimを使っていて微妙に困るのが、Vim経由でUndoをすると、自分の想定しているものより多くの変更がもとに戻ってしまうことだ。
この問題は開発元でもIssueとして認識されているが、今のところ解決には至っていない。そこで今回は、この問題の現時点(2019年6月)での回避策を紹介したい。
私はVSCodeでVimプラグインを使っている。VSCodeのVimを使っていて微妙に困るのが、Vim経由でUndoをすると、自分の想定しているものより多くの変更がもとに戻ってしまうことだ。
この問題は開発元でもIssueとして認識されているが、今のところ解決には至っていない。そこで今回は、この問題の現時点(2019年6月)での回避策を紹介したい。
私はGitの操作はほぼ全てIntelliJ系のIDEで行っている。慣れれば便利である。 コミットする際には自動的にreformat code(コードスタイルに基づいて自動的に整形してくれる機能)が走るように設定しているのだが、これが今回おもわぬ事故を引き起こした。 コミット時に自動フォーマットする機能は大変便利なのだが、特定のファイルで問題が発生するためにオフにはしたくない。 そこで特定のファイルのみ自動フォーマットされないようにする設定を探してみた。
いままでレンタルサーバ+独自ドメインで運用していたブログを、Hugo+Netlifyで配信するようにした。というのは前回の記事で書いた。 WordPressからHugoに映る部分はともかくとして、運用していたドメインの切り替えとかについて備忘録を残すことにした。 同じ作業はもうやらないとは思うのだが、もしやることになったら絶対忘れてるので・・・。
Kotlinを使ったプログラミングコンテストに参加した。たまたまTwitterで流れてきたのを見て、たまたま時間が空いてたので挑戦してみた。
https://codeforces.com/contests
腕試しになるかなぁという程度の軽い気持ちでの挑戦であったが、「俺ってまったくできないじゃん」と凹む結果に終わってしまった。 そんなに深刻になるレベルではないけれど、それなりにできるんじゃないかという妙な自身は見事に砕かれた。 もっと地道に精進しようと反省である。
例えばIntの配列の中に特定の条件を満たす要素が存在しているかどうかを調べたいとする。 今回はそんなときに使える標準関数を紹介する。
前々からブログをWordPressで運用するのをやめたいと思っていた。 すったもんだのすでにようやくHugoで運用するようにできたので、経緯やら苦労したところなどを書き残しておきたい。 まだ作業途中ではあるのだが、いい加減記事の更新もしたいので書いておく。
ほぼサーモンラン専用となった私のSplatoon2のゲームライフを、より豊かにするために。サーモンランのスケジュールだけを確認できるシンプルなアプリを作成した。
最近LeetCodeというサイトを利用してプログラミングの学習をしている。学習と腕試しと頭の体操を兼ねてというのが正確なところだろうか。
自分の中では頭の体操的な位置づけが大きい。設定された問に対して、どうやったら解けるか自分で考え実装してみる。実装の過程で、ついでにKotlinの勉強にもなったらなぁなんて感覚である。
そう、Kotlinで解いている。つい最近も、groupingByなるメソッドが用意されていることを知ったが、問題を解いている最中にこういった新たな発見に出会えるかもしれないというのが、Kotlinの勉強にもなるかなあと思っている所以である。
競技プログラミングという観点で見れば、日本語でできるAtCoderが有名だろう。こっちのほうが計算量やら書いたプログラムの効率性をちゃんと測れて良いと思う。LeetCodeでも処理時間は出るが、Kotlinで解いた場合、同じコードでもSubmitするたびに100msくらいの誤差が出る場合があって、まったく指標として役に立たない。ついでにKotlinで解いている人は少ないのか、提出されたコードの100%より早いです=自分が初めてKotlinで提出した、なんてパターンがあってさらに指標にならない。
そもそも競技プログラミングとしてやっているというよりは、先にも書いたとおり、私の場合は学習のためという面が大きい。その観点で見ると、LeetCodeは英語であることを除けばやりやすかった。AtCoderだと標準入力から入力を読み取る部分からやらないといけないのに対して、LeetCodeは引数として渡ってくるので純粋に解法に集中できるのが良かった。あとはAtCoderだとKotlinのバージョンが1.0.0で、対してLeetCodeが1.2.50だったというのも理由の1つではある。
英語であることは、障壁というよりは英語の学習にもなっていいかもしれないと考えている。英語の学習とはならなくとも、英語に慣れるのに役に立つだろう。ちなみに問題によっては英語が理解できずにそもそも解く以前の問題で詰まってしまうこともあるけれど、そういう場合は他の理解できる問題をやるようにしている。
LeetCodeのいいなと思ったところは、問題を解く以外にも学習用コンテンツがあることが挙げられる。
例えばIntroduction to Data Structure – Binary Treeは二分木についてのコーナーである。一部は課金しないとアクセスできないが、無料でもできる部分がいくつかある。
各ノードを巡回するにはどうしたらよいかという問いにすら苦労するのだが、だからこそ勉強になる。わかるわかる、再帰使えばいいんでしょと思っても、実際に適用しようとすると手が止まってしまった。
これに限らないのだが、再帰処理をすれば解けそうという場合に、どこからどこまでが再帰処理に必要なのかがすぐに言語化できない。だからなかなかコードに落とせない。LeetCodeをやったからと言ってすぐに言語化できるようになるわけではないのだが、実際に問題に直面して解くという経験を通じて徐々に理解を深めていけたらいいなぁと思っている。
そういえば感覚で理解しているといえば、lambda式も結構感覚で使っている。こう書いたらこうなるんでしょというノリで使っているということか。
こういう問題には自分でアプリを作るだけではなかなか出会えないので、こういうサイトを利用して学習するのもよいのではないかなと思う。
私はProblems→Difficultyで難易度選択、Listsで「Top 100 Liked Questions」にチェックを入れてフィルタリング、その上でSolutionつきの問題を解くようにしている。
問題の質は玉石混交で「なんやねんこれ」というような問題もあったりする。likeが多い問題はそれだけ勉強になると思った人や面白いと思った人が多いということになる。さらに解説付きであれば解法の勉強にもなるだろうからなおおすすめである。
Solutionがないやつは、ディスカッションで他の人の解法を見ることも可能ではあるが、解説になっているとは限らないのでSolutionつきのものを選んでいる。
KotlinにgroupingByなる関数があることを知った。
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/grouping-by.html
Groupingソースなるものを作成するための拡張関数で、listとか配列で使うことができる。
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-grouping/index.html
例えば”I have a pen”で各アルファベットが何回出現しているかを調べるのに使える。
val result = "I have a pen".groupingBy { it }.eachCount()
println(result) // {i=1, =3, h=1, a=2, v=1, e=2, p=1, n=1}
groupingBy自体は続く関数オブジェクトで求められるkeyを元にしたMapへ集計できるようにするためのインターフェースで、これ自体呼び出しても何も起こらない。上記の例でいうとeachCount()を呼び出して初めて集計が行われる。
keySelectorを引数に取るのはgroupByもgroupingByも同じ。
groupByだと指定したkeyごとの要素をListにもつMapを返す。イメージ的にはmapに近い処理。
groupingByはそれ自体は何もしない。keyを元に集計処理を行うインターフェースを用意するだけのメソッドなので、その後に別途集計処理が必要になる。forEachを拡張したものと考えるといいかもしれない。
両者の使い分けは、keyを元にした要素のリストがほしいのか、それともその要素を何らかの処理をして集計した結果だけが欲しいのかで使い分けることになるだろう。集計した結果のみが必要なのであれば、その中間形態であるkeyごとの要素リストは不要なので、groupingByを使ったほうが効率的である。
groupingByだけでは集計処理は行われないので、その後に以下のいずれかを利用して集計を行う。
それぞれ別にxxxToという処理も用意されていて、違いは集計先のMapが指定できるかどうか。Toがついている方は、既存のMapが集計先に利用されるので、前の集計結果にさらに付け足すのに使える。Toがつかない方は空のMapが集計先として利用される。
groupingBy自体は要素のグルーピングを行うわけではなく、aggregateなどを呼び出すことで初めて要素のグルーピングと集計が行われる。
よほど特殊な事情がない限り、aggregateを直接使うことはないと思われる。大体のケースでfoldを使ったほうが便利だろう。
というのも、aggregateは要素がkeyによるグルーピングを行った最初の要素かどうかを判定したりするのも全て自分で書く必要があるからだ。要素が初出の場合に初期値を用意し、そうでない場合に集計処理をするというのがfoldなので、大抵のケースでfoldで事足りるはず。
最終的にはどれを使ってもList<何らかの型>がMap<指定したキー, 集計後の結果>に集計される。(元のデータがListとは限らないけれど、最終的にMapに集約されるのは変わらない)
keyごとの要素の個数が欲しい場合にこれを使う。引数もいらないので最もシンプル。
gradle経由でテストタスクを実行すると、テスト結果のレポートがbuild/reportsディレクトリに出力される。このレポートファイルを確認するのに、毎回ProjectウィンドウをProject表示に切り替えて、ディレクトリを掘ってファイルをブラウザで開くようにするのは手間である。そこでテスト実行後に自動的に開くようにした。
もっとスマートにやる方法があるのではないかと思うのだが、Gradleがよくわからなくて私には無理だった。以下のタスクをapp/build.gradleに追加した。
task openReportJvmTest(type: Exec) {
workingDir "build/reports/tests/testProdDebugUnitTest"
commandLine 'open'
args "index.html"
}
task openReportInstrumentedTest(type: Exec) {
workingDir "build/reports/androidTests/connected/flavors/MOCK"
commandLine 'open'
args "index.html"
}
openReportJvmTest.onlyIf { !ciBuild && !travisBuild }
openReportInstrumentedTest.onlyIf { !ciBuild && !travisBuild }
tasks.withType(AbstractTestTask) { task ->
if (task.name == "testProdDebugUnitTest") {
task.finalizedBy openReportJvmTest
}
}
tasks.withType(com.android.build.gradle.internal.tasks.AndroidTestTask) { task ->
if (task.name == "connectedMockDebugAndroidTest") {
task.finalizedBy openReportInstrumentedTest
}
}
openReportXXXというのがテストレポートをブラウザで開くタスク。開くディレクトリを直書きしているが、がんばればタスクによって開くディレクトリを変えることはできるだろう。openコマンドを使っているが、Windowsだときっと動かない。
既存のテストタスクのFinalizerタスクとして実行するよう指定しているが、CI上ではopenコマンドが認識できないので失敗してしまう。そこでCI上では実行しないようにonlyIfで指定している。
pluginで定義されているテストタスクの後に実行させたいのだが、tasks.withTypeを使うことで各タスクの参照を得ている。直接タスクを指定すると「そんなプロパティはない」と言われたり、nullだったりしてうまくいかなくてこういう形に行き着いた。
取得したテストタスクそれぞれでfinalizedByを使ってレポートを開くタスクを指定している。dependsOnを使うとテストが全てパスしていればレポートが開くが、1つでもテストが失敗しているとopenReportタスクが実行されないので、finalizedByを使っている。
http://gradle.monochromeroad.com/docs/userguide/more_about_tasks.html
そもそもRun configurationでテストを実行できるようにすればいいのではないかと思うかもしれない。というか私も最初はその方法をとった。
しかしAndroid Instrumented Testに関してはできたが、Android JUnitではできなかった。
個別のテストをクラスを指定して実行する分には問題ないが(テストコードを表示して実行するやり方)、プロジェクトに存在するテストコードをまとめて実行する方法が取れなかった。Javaで書かれたテストコードは実行されても、Kotlinで書かれたテストが漏れてしまうのである(このプロジェクトはJavaで書かれたテストコードとKotlinで書かれたテストコードが混在している)。
しょうがないのでCIでも実行するgradleのテストタスクを走らせる方法をとることにしたのだ。