grep の -C オプションみたいなのって、意外に実装が難しいなと思いました。そこで考えた問題。
リストの中の条件を満たす要素の前後 n 個以外を省略する関数を書きなさい。例えば、
A B C D E F G H I
のリストに対して、E だけが条件を満たして、n = 2 であれば
..2.. C D E F G ..2..
を出力する感じ。細かい仕様は勝手に決めてください。
def snip(a, n) a.dup.tap do |a| t = (0...a.size).to_a & a.map.with_index {|x, i| [x, i-n..i+n] } .select {|x, r| yield x } .map {|x, r| r.to_a } .flatten [-1, *t, a.size].each_cons(2) .reject {|b, e| b + 1 == e } .reverse .each {|b, e| a[b+1..e-1] = "..#{ e - b - 1 }.." } end end p snip(%w(A B C D E F G H I), 2) {|x| x == "E" }.join(" ") #=> "..2.. C D E F G ..2.."
いや、普通はこんなコード書かないですけどね (特にインデント) 。ていうか、実用的には、ストリーム的に処理して、メモリ使用量を O(n) にすべきでしょうね。
この問題は、プログラムを書くよりテストを書く方が難しいと思う。上のもほんとにあってるか自信ない。
Haskell 版も書いてみたけど、あんまりきれいにならなかったので略。ちなみに型は
snip :: Int -> (a -> Bool) -> [a] -> [Either Int a]