Scalaのリファクタリング小ネタ (コレクション)
mapしてflattenするなら、flatMap
// bad .map { ... }.flatten // good .flatMap { ... }
filterしてmapするなら、collect
// bad .filter { ... }.map { ... } // good .collect { ... }
mapしてreverseするなら、reverseMap
// bad .map { ... }.reverse // good .reverseMap { ... }
flatMap内でmapするなら、for
例えば、List[Option[A]]のようなありがちな構造に何かするとき // not bad.. l.flatMap { o => o.map(_ * 2) } // good! for { o <- l i <- o } yield i * 2 さらにfilterを追加してみる // not bad.. l.flatMap { o => o.filter(i => i == 2).map(_ * 2) } // good! for { o <- l i <- o if i == 2 } yield i * 2 そもそも内部的にはflatMapに変換される
Listへ要素を追加するときは先頭に (参考表)
末尾への追加やsizeの取得はO(n)かかってしまう。ケースごとに適切なコレクションを使う。 // bad Seq(3, 2) :+ 1 // good 1 +: Seq(2, 3)
map等の中でmatchは省略
// bad .map { op => op match { case Some(i) => i * i case None => 0 } } // good .map { case Some(i) => i * i case None => 0 }
filterしてsizeするならcount
// bad? .filter(_ % 2 == 0).size // good .count(_ % 2 == 0)
タプルの数値アクセスは極力控える
個人的には許したくない。絶対に。 // bad Seq((1, 2), (3, 4)).map { t => t._1 + ":" + t._2 } // good Seq((1, 2), (3, 4)).map { (id, age) => id + ":" + age }
タプルは多くなったらcase classに
// bad type ResultSet = (Int, String, Long) // good case class ResultSet(id: Int, name: String, width: Long)
Option等はコレクションの関数を使う
// bad Option(1) match { case Some(1) => true case _ => false } // good Option(1).exists(_ == 1) // bad if (o.isDefined && o.get == "a") Some(o.get * 2) else None // good o.collect { case q if q == "a" => q * 2 }
大量の要素を色々するときは、viewを使う
// bad (0 to 10000000).map { ... }.filter { ... }.scan { ... } // good (0 to 10000000).view.map { ... }.filter { ... }.scan { ... }.force
EffectiveScalaは読むべし。