無限リスト
某勉強会で『ふつうのHaskellプログラミング』を読んでいる。
4年前に読んだはずなのだが内容はすっかり忘れていた。身についていなかったということだろう。今回もそうなりそうだが、Scalaを使いこなす基礎知識が少しは得られそう。
今回の勉強会で、与えられたテキストに行番号を付加するサンプルを見ている時、サンプル中の行に行番号を付加する関数
zipLineNumber :: [String] -> [(Int, String)] zipLineNumber xs = zip [1..] xs
で、「無限リスト"[1..]"が使えるのはいいよね」「Rubyにも無限リスト欲しいよね」(なぜかこの勉強会はRuby使い率が高い)という話になった。
ということで、サンプル本体のnumbering関数をScalaで実装してみる。
def seq(i:Int):Stream[Int] = Stream.cons(i, seq(i + 1)) def numbering(s:String) = s.split("\n") .zip(seq(1)) .map(e => "%06d %s".format(e._2, e._1)) .mkString("\n")
1行目は無限リストを作る関数。Rubyでも1.9からのEnumeratorを使えば同様のことができるらしい。
でも、ここは無限リストを使う必要は無いよね?
def numbering(s:String) = { val lines = s.split("\n") lines.zip(1 to lines.size) .map(e => "%6d %s".format(e._2, e._1)) .mkString("\n") }
zipWithIndexという便利メソッド発見。
def numbering(s:String) = s.split("\n") .zipWithIndex .map(e => "%6d %s".format(e._2 + 1, e._1)) .mkString("\n")
Rubyだと1.9からEnumeratorが便利になったので
def numbering(s) s.split("\n").map.with_index{|e, i| "%6d %s"%[i + 1, e]}.join("\n") end
と、zipも使わないでよさそう。1.9使ってないので動作未確認(そろそろ入れるか)。
戻って、この例ならHaskellでも無限リスト[1..]を使わず[1..length xs]でもいいはずなのだが、xsがこの時点で評価されてしまうのが嬉しくないのか。