元記事
Rubyでファイルの末尾n行を表示するtailっぽい何かを作った
tailっぽい何かをレビューしてみます。
tailを実装する
ということで、自分もやってみたら、なぜかこうなった。
tail.rb
#!/usr/bin/env ruby
class IO
# 現在のposからn行だけ配列で返す。
# posは読み込んだところまで。
def head(n)
lazy.first(n)
end
# 現在のposからeofまでのうち終わりn行だけ配列で返す。
# posはeofになる。
def tail(n)
reverse_each.lazy.first(n).reverse
end
end
if $0 == __FILE__
filename = ARGV[0]
size = ARGV[1].to_i
open(filename) do |io|
io.tail(size).each do |line|
print line
end
end
end
どんな感じかというと。
-
tail
だけなんてかわいそう、head
も実装してあげないと! - やっぱ、オブジェクト指向っぽく
IO#tail(n)
って感じで呼び出したいよね! - 時代は関数型プログラミング!ということでlazy。tailの方の実装でlazyする意味は?とか聞かないで。たぶん、lazyのほうがちょっとだけ速い(気がする)。
- シークとかファイルサイズとか調べるの面倒だから、全部読み込んでもいいんじゃね?そもそも
IO#size
ないし。メモリ喰い過ぎじゃ無いかって?Rubyでメモリを気にしたら、負けだと思っている。
と、なっております。
ファイルシステムやサイズによりますけど、ファイルのシークは結構高コストです。それに比べて、シーケンシャルアクセスだとバッファとかもあってかなり高速ってのもあるので、数MB程度なら何も考えずに全部取った方が速いかもです。でも、さすがにGB級だと上では対応できないので、io.tail(n)
する前にio.seek(-(n * 1024), IO::SEEK_END)
とかすべきかな。