webネタ

Webエンジニアが業務に関係することをメモしていく

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は読むべし。