リスト操作関数早見表(Underscore, Scala, Haskell)

ぽんこつ@ponkotuyです。勝手に会社のプロダクトをリファクタしたり高速化したりするだけの簡単なお仕事をしています。Scalaも楽しいけど、SQLのチューニングはもっと楽しいです。

という自己紹介をガン無視してリスト操作関数とUnderscore.jsの話をします。

リスト操作関数

関数型言語の長所で真っ先に出てきて、手続型言語に慣れた人でも比較的恩恵を理解しやすいのがリスト操作関数だと思います。

例えばある数値のリストの中から偶数で一番大きい数を取るとかいう処理

-- Haskell
maximum . filter even $ [2, 4, 5, 7, 1, 8, 9, 10]
// Scala
List(1, 2, 4, 5, 7, 1, 8, 9, 10).filter(_ % 2 == 0).max
# coffee
_ = require('underscore')
_.max(_.filter [1, 2, 4, 5, 7, 1, 8, 9, 10], (x) -> x % 2 == 0)

ワンライナーで短かく、可読性を損なわずに書けます。また、副作用をできるだけ避ける書き方がしやすいように作られているので、低コストで副作用を回避できます。

Underscore.js

このように便利なリスト操作関数をJavaScriptでも使えるようにしたい。そこで便利関数を集めたUnderscore.jsです。

Undescore.jsを使えば、若干余計な_.を書かなくてはいけない問題はありつつも、とりあえずはリスト操作関数の恩恵にあずかることができます。

ただしUnderscore.jsには一方で「名前が分かりづらい」という問題があります。HaskellScalaと同じだったら苦労しないのに。そこで、Underscore.jsのある関数が、HaskellScalaだと何と呼ばれているかの一覧を作ってみました。

早見表

Underscore.js Scala Haskell 備考
each(xs, f)
forEach
xs.foreach(f) forM_ xs f
map(xs, f)
collect
xs.map(f) map f xs
pluck(xs, propName) mapの特殊形
reduce(xs, f, [memo])
inject
foldl
xs.reduceLeft(f) foldl1 f xs
空で例外
xs.foldLeft(memo)(f) foldl f memo xs
reduceRight(xs, f, [memo])
foldr
xs.reduceRight(f) foldr1 f xs
空で例外
xs.foldRight(memo)(f) foldr f memo xs
find(xs, f)
返り値はvalue | undefined
xs.find(f)
返り値はOption[A]
find f xs
返り値はMaybe
findWhere(xs, obj) findの特殊形
filter(xs, f) xs.filter(f) filter f xs
where(xs, obj) filterの特殊形
compact(xs) filterの特殊形
without(xs, values*) filterの特殊形
reject(xs, f) xs.filterNot(f)
every(xs, [f])
all
xs.forall(f) all f xs
some(xs, [f])
any
xs.exists(f) any f xs
contains(xs, value)
includes
xs.contains(value) elem value xs
max(xs, [f]) xs.max maximum xs minimumも同様なので省略
xs.maxBy(f) (maximumBy g xs)
sortBy(xs, f) xs.sortBy(f) (sortBy g xs)
groupBy(xs, f) xs.groupBy(f) ×
indexBy(xs, f) × × groupByと同じだが結果が1つのみ
countBy(xs, f) xs.count(f) ×
shuffle(xs) Random.shuffle(xs) × 副作用あり
sample(xs, [n]) × ×
size(xs) xs.size length xs
partition(xs, f) xs.partition(f) partition f xs
first(xs, [n])
head
take
返り値はvalue | undefined | array
xs.headOption
返り値はOption[A]
listToMaybe xs
xs.take(n) take n xs Lodashにはない
× xs.head head xs 例外(非推奨)
initial(xs, [n])
空のとき空が返る
xs.init
空のとき例外
init xs
空のとき例外
xs.dropRight(n) × Lodashにはない
last(xs, [n]) xs.lastOption ×
xs.takeRight(n) × Lodashにはない
× xs.last last xs 例外(非推奨)
rest(xs, [n])
tail
drop
空のとき空が返る
xs.tail
空のとき例外
tail xs
空のとき例外
xs.drop(n) drop n xs Lodashはdropでおこなう
flatten(xs, true) xs.flatten concat xs
flatten(xs) × × 再帰的なflatten
union(xs*) xs.union(ys) union xs ys 2つの場合
xss.flatten.distinct foldl union [] xss
intersection(xs*) xs & ys // Set
xs.intersect(ys) // 汎用
intersect xs ys 2つの場合
xss.reduce(_ & _) // Set
xss.reduce { case (x, y) => x.intersect(y) } // 汎用
last $ scanl1 intersect xss
difference(xs, ys) xs.diff(ys) xs \\ ys
uniq(xs)
unique
xs.distinct nub xs
zip(xs*)
unzip(xs*)
xss.transpose transpose xss JSにはTupleがないので、厳密にはzipではない
object(xs, ys) xs.zip(ys).toMap Data.Map.fromList $ xs `zip` ys
indexOf(xs, value) xs.indexOf(value) elemIndex value xs
lastIndexOf(xs, value) xs.lastIndexOf(value) ×
findIndex(xs, f) xs.indexWhere(f) findIndex f xs
findLastIndex(xs, f) xs.lastIndexWhere(f) ×
range([start], stop, [step]) start until stop
Range(start, end, [step])
List.range(start, end, [step])
[start..(end - 1)]
[start, start + step .., end - step]