RubyでHaskell風のリスト操作

30分プログラム、その622。RubyHaskell風のリスト操作を移植しました。
Rubyでコードを書いてるときに、take_whileとかが欲しくなることが何度かあったので、実装してみました。
メソッド内部で副作用を使っているのが何個かありますが、外から見たらちゃんとFunctionalなので許してください。

使い方

p (1..4).take(2)
p (1..4).drop(2)

ソースコード

#! /opt/local/bin/ruby -w
# -*- mode:ruby; coding:utf-8 -*-
#
# prelude.rb -
#
# Copyright(C) 2009 by mzp
# Author: MIZUNO Hiroki / mzpppp at gmail dot com
# http://howdyworld.org
#
# Timestamp: 2009/07/17 22:46:58
#
# This program is free software; you can redistribute it and/or
# modify it under MIT Lincence.
#

module Enumerable
  def and()
    self.inject {|x,y| x && y }
  end

  def or()
    self.inject {|x,y| x || y }
  end

  def sum()
    self.inject{|x,y| x + y }
  end

  def product()
    self.inject{|x,y| x * y }
  end

  def scan(init=nil,&f)
    ys = []
    accum = init
    self.each do|x|
      if accum == nil
        accum = x
      else
        accum = f.call(accum,x)
      end
      ys << accum
    end
    ys
  end

  def take(n)
    xs = []
    self.each do|x|
      if n == 0 then
        break
      end
      xs << x
      n -= 1
    end
    xs
  end

  def drop(n)
    xs = []
    self.each do|x|
      if n > 0 then
        n -= 1
        next
      end
      xs << x
    end
    xs
  end

  def split_at(n)
    xs = []
    ys = []
    self.each do|x|
      if n > 0 then
        n -= 1
        xs << x
        next
      end
      ys << x
    end
    [xs,ys]
  end

  def take_while(&f)
    xs = []
    self.each do|x|
      if not f.call(x) then
        break
      end
      xs << x
    end
    xs
  end

  def drop_while(&f)
    xs = []
    self.each do|x|
      if f.call(x) then
        next
      end
      xs << x
    end
    xs
  end

  def span(&f)
    xs = []
    ys = []
    self.each do|x|
      if f.call(x) then
        xs << x
      else
        ys << x
      end
    end
    [xs, ys]
  end
end

class Array
  def init
    self[0...-1]
  end
end

p [1,2,3].init
p [true,true,true].and
p [true,true,false].or
p((1..4).sum)
p((1..4).product)
p((1..4).scan {|x,y| x+y})
p (1..4).take(2)
p (1..4).drop(2)
p (1..4).split_at(2)