KotlinのgroupingByについて調べてみた

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()を呼び出して初めて集計が行われる。 groupByとの違い keySelectorを引数に取るのはgroupByもgroupingByも同じ。 groupByだと指定したkeyごとの要素をListにもつMapを返す。イメージ的にはmapに近い処理。 groupingByはそれ自体は何もしない。keyを元に集計処理を行うインターフェースを用意するだけのメソッドなので、その後に別途集計処理が必要になる。forEachを拡張したものと考えるといいかもしれない。 両者の使い分けは、keyを元にした要素のリストがほしいのか、それともその要素を何らかの処理をして集計した結果だけが欲しいのかで使い分けることになるだろう。集計した結果のみが必要なのであれば、その中間形態であるkeyごとの要素リストは不要なので、groupingByを使ったほうが効率的である。 集計処理 groupingByだけでは集計処理は行われないので、その後に以下のいずれかを利用して集計を行う。 aggregate fold reduce eachCount それぞれ別にxxxToという処理も用意されていて、違いは集計先のMapが指定できるかどうか。Toがついている方は、既存のMapが集計先に利用されるので、前の集計結果にさらに付け足すのに使える。Toがつかない方は空のMapが集計先として利用される。 groupingBy自体は要素のグルーピングを行うわけではなく、aggregateなどを呼び出すことで初めて要素のグルーピングと集計が行われる。 fold / reduce / eachCount の使い分け よほど特殊な事情がない限り、aggregateを直接使うことはないと思われる。大体のケースでfoldを使ったほうが便利だろう。 というのも、aggregateは要素がkeyによるグルーピングを行った最初の要素かどうかを判定したりするのも全て自分で書く必要があるからだ。要素が初出の場合に初期値を用意し、そうでない場合に集計処理をするというのがfoldなので、大抵のケースでfoldで事足りるはず。 最終的にはどれを使ってもList<何らかの型>がMap<指定したキー, 集計後の結果>に集計される。(元のデータがListとは限らないけれど、最終的にMapに集約されるのは変わらない) eachCount keyごとの要素の個数が欲しい場合にこれを使う。引数もいらないので最もシンプル。 foldとreduce 要素の集計にロジックが必要な場合にこちらを利用する。オブジェクトの特定のフィールドだけが集計対象であるときなどに利用することが考えられるだろうか。 どちらも集計処理を行う関数オブジェクトを引数にとるのは同じ。 違いはfoldは集計値の初期値を設定する必要があるが、reduceは初期値すら省略できるというところ。reduceはkeyごとに出現した最初の要素が初期値に使われるからである。 集計処理を行う関数オブジェクトは、集計後の値を返すような関数オブジェクトにすればいい。この関数オブジェクトの戻り値が、次の要素のaccumulatorの値になる。 foldとreduceの違い 集計結果が要素と同じ型になるのかどうかが使い分けの分岐点となる。集計結果が元の要素と同じ型ならreduceを使ったほうが便利(初期値の指定がいらないので)。 というのもreduceの初期値はkeyごとに最初に出現した要素になるからである。だから型の変換が行えない。 data class SalesInfo(val id: String, val date: Date, val sales: Int) val dailySales = listOf(...) // 日々の売上データ // 商品IDごとに売上を集計 val sum = dailySales.groupingBy { it.id } .fold(0) { _, acc, element -> acc + element.sales } println(sum["hoge"]) // 商品ID"hoge"の1ヶ月分の売上を表示 上記の例で言えばSalesInfo型の要素をInt型に集計している。このケースではreduceは使えない。
Read full post gblog_arrow_right

KotlinでUnitテストでassertionに何を使うか

KotlinでUnitテストを書く際に、assertionに何を使うかという話。 私の場合特にこだわりがあるわけではないのだが、基本的には多数派に従いたいという気持ちが強い。しかしながら、Kotlinのテストのassertionに使うならこれ一択、みたいなものがない(と思っている)ので、いつも困る。 Javaなら特に何もせずともhamcrestのassertThat(sut.getHoge()).is("fuga")みたいなassertionを使っていればいいのだが、Kotlinの場合はisが予約語であるという問題がある。いちいちisをバッククォートでくくらなければならず美しくない。 knit https://github.com/ntaro/knit 日本ではいちばん有名なやつだと思われる(DroidKaigiのアプリでも使われていたはずなので)。 sut.getHoge().should be "fuga"みたいな感じで使う。 ただrepositoryを追加しないと組み込めないので私はあまり使っていない。build.gradleに一行追加するだけの話なんだけどね・・・。 expekt https://github.com/winterbe/expekt knitに似たsut.getHoge().should.equals("fuga")みたいな感じのAPI。リポジトリの追加が必要ないので、私はこっちをよく使っている。 ただメンテされてないのが玉に瑕。 kotlintest https://github.com/kotlintest/kotlintest assertionライブラリではなくて、Spec風のテストを書くためのライブラリ。 ただsut.getHoge() shouldBe "fuga"みたいなassertionが含まれている。 SpekというSpec風のテストを書くためのライブラリがあるが、こちらはJava8でなければ使えないという制約があって、Androidのプロジェクトに適用するのはなんだか難しそうで手を出していない。 kotlintestはネストしたテストを書きやすくて、これいいかもとか思ったこともあったのだけれど、androidTestに組み込むとメソッドカウントがオーバーしてしまうので使えない。 assertk https://github.com/willowtreeapps/assertk Android Weeklyで流れてきて知った。AssertJチックなassertionライブラリで、Kotlinで使うならコレのが便利なのだろうか。 assert(sut.getHoge()).isEqualTo("fuga")みたいな感じで使う。 しかし個々最近はshould系ばかり使ってきているので、最初にassertを書かないといけない形式はめんどくさいと感じてしまっている。 AssertJ https://joel-costigliola.github.io/assertj/ Javaのコードも考慮に入れるならJavaで使えるライブラリを使うのがいいのだろう。 私はJavaだとhamcrest使っとけばいいやな人だったので、Javaでassersionライブラリを何使うかなんて特に気にしたことはなかった。 みんなが使っているものが知りたい みんなはどれを使っているのだろうか。他にこれが使いやすいみたいなのがあったら教えて欲しい。 私はexpektを使う傾向が強いが、揺らいでいる。 正直なところassertionさえできればなんでもいいのだろうからして、自分が使いやすいものを使えばいいってだけの話なのだろうけれども。 https://discuss.kotlinlang.org/t/what-assertions-library-do-you-use/1809 ここをみると、kotlintestが多いっぽい。

Kotlinを使っていて感じるつらさ

ちなみにこの記事を書いたのはかなり昔のことなので、今はだいぶ環境が変わっている。マシンスペックによる辛さも含まれていたので、現在ではだいぶ解消されていると思って読んで欲しい。

なにせ3年もまえの記事だからね・・・あんまり真に受けないように。19年時点の私はだいぶKotlinラバーですから。

Read full post gblog_arrow_right

Kotlin使ってみて感じる便利さと葛藤

最近アプリやAndroid Studio用プラグインを作るのにKotlinを使っています。 始めたばかりの頃は「Javaだとああ書くんだけど、Kotlinだとどう書けばいいんだ」ということが多かったです。Javaでまともに書けないのにKotlinに手を出すのは早いんじゃないかとも思っていました。 しかし少しずつ試していくと、Kotlinの便利な部分が分かってきてきました。 私の場合、「Kotlinで書き始めたんだけどやっぱ使い方よくわからないからJavaに戻そう」とう場面が初期の頃はよくありました。はじめはKotlinで書いていたけど、やっぱりJavaで実装しようとという感じです。 そんなときに、「Javaに戻すのめんどくせえ」と感じる部分があって、そこで改めて「Kotlinってやっぱ便利やなぁ」なんて実感しました。 それからというもの、Kotlinの比重が徐々に増えてきて、今では逆にJavaで書く方が面倒くさいと感じるようになってしまいました。 一方で、Kotlinが無敵というわけではありません。Annotation Processingを使うライブラリがKotlinだとうまく使えないことがあったり(基本的には大丈夫ですが、Javaで書けば動くコードがKotlinで同じように書くと動かないことがあったりします)、Javaと比べるとコード補完が遅かったり、Kotlinを使うことで感じるストレスもあります。 ですが、不便さを差し置いてもKotlinで書いた方がすっきり書けるのはやっぱり快適だと思っています。 テストコードから始めるといいかも どなたかの記事で、Kotlinはテストコードから導入してみたらどうかという記事を読みました。私もいい方法だと思います。Kotlinの便利さを実感するためではなく、どう書くかを知るのにちょうどいいと思います。 私もJUnitでのユニットテストをKotlinで書いています。テスト対象をKotlinで書いてるからとか、セミコロンつけなくてもいいから、とかそんな理由です。ユニットテストについてはKotlinが便利だからという理由はあまりないかもしれません。 ユニットテストにおいてKotlinが便利だと思うのは、バッククオート(`)で囲むことで、メソッド名やクラス名を数字から始めることができたり、途中に空白を含めることができたりすることでしょうか。 私はテスト名に日本語を使うことが多いです。そしてそのときに、メソッド名に使える文字に制約があるのが微妙に困ります。 例えば各月の最終日を求めるメソッドのテストをするのに「4月の場合は30日を返す」というテスト名にしたくてもできません。Javaではメソッド名を数字から始めることができないからです。だからこんなときは「月に4月を指定したら30日を返す」という感じのメソッド名にするのですが、これが微妙なストレスになります。(頭に「月を」つけるだけやんという感じですが、微妙にストレス感じるんですよこれ) Kotlinではこの制約に煩わされることがありません。メソッド名をバッククオートで囲めば、数字から始めようが、途中に空白を挟もうが問題ないのです。 @Test fun `2つの時刻の差を求める`(){ val time1 = LocalDateTime.of(2014, 1, 1, 23, 58, 30) val time2 = LocalDateTime.of(2014, 1, 2, 0, 4, 30) val actual = Duration.between(time1,time2).seconds assertThat(actual, `is`(360L)) } なにそれ気持ち悪いと思われるかもしれませんが、これはれっきとしたKotlinの仕様です。 Grammar – SimpleName Javaのメソッド名規約によってテストメソッド名を考えるのが面倒くさいなぁと感じている人は私だけではないと思いたい。 一方でKotlinでユニットテスト書けば便利なことばかりではありません。例えばassertThatなどを使おうとするとimport文を手書きで書かないと認識してくれないのが不便です。(私の環境の問題なのかもしれません。JavaだとALT+Enterでimportできるんですけどね・・・) セミコロンつけなくてもいい 単純なことですが、Kotlinは文末にセミコロンをつけなくてもいいのです。 これが便利・・・と言いたいところですが、私は半々かなぁと感じています。 確かにいちいちセミコロンつけなくてもいいので楽です。たまにJavaでコードを書くときに、しょっちゅうセミコロンつけ忘れます。それくらいには快適です。 一方セミコロンが不要なせいで、メソッドチェーンするときに私は微妙にストレスを感じます。 Javaだとメソッドチェーンするときに改行をするとインデントを一段深くしてくれます。 しかしKotlinでは改行した時に文末なのか次の文に移るのかが判別不能なので、インデントを深くしてくれたりしません。これが毎回微妙にめんどうくさいです。私はいつもドットを打ってカーソル戻して改行するという方法をとって回避しています。 多分Reformat Code(Cmd+Alt+l)を使うのが楽なんでしょうけども。みんなどうしてるんだろう・・・。 文字列の扱いが便利 Strings Javaだと文字列に変数を埋め込もうと思うと、+演算子で連結しなければなりません。もしくはString.format()を使うかですね。 Kotlinだとそんな面倒くさいことをせずとも、文字列中に変数を埋め込めるので便利です。こんな感じに書けるわけです。 val hoge = "num is $num" 埋め込む変数が多くなればなるほど、これはとても便利になります。デバッグのために変数の中身を文字列として出力して確認することがよくあると思いますが、そんなときに特に楽だと思います。 複数行に渡る文字列も、"""で囲むことでそのまま文字列として扱うことができます。ただしこれを使うと、インデントによる空白も文字列に含まれてしまうので、使いドコロが難しい気もします。 配列操作が便利 例えば配列の要素の中から最大値を取得しようと思ったら、Javaだとこんな感じになるでしょう。 int[] array = {1,20,3,40,5,16,7}; int max = 0; for (int num : array) { max = Math.max(num, max); } Kotlinだとこうです。 val array = arrayOf(1,20,3,40,5,16,7) val max = array.
Read full post gblog_arrow_right