reverseと,ついでに素数

iPod shuffleをオートフィルしている間に,軽く更新.弟が今日,C言語で配列を逆順にする,ってのをやってたので,それをRubyでやってみるかと.あと,素数求める続き.


まぁ正直RubyにはArray.reverseというのがあるので必要ないけど,これは練習なんですよ.

class Array
  def myreverse1
    (0 .. (self.size / 2)).each{ |i|
      self[i] , self[self.size - i - 1] = self[self.size - i - 1] , self[i]
    }
  end
end

うわ,ツマンネェ.


さっきのは破壊的だったけど,今度は破壊的でないものを.

class Array
  def myreverse2
    rev = []
    self.each{|e|
      rev = [e] + rev
    }
    rev
  end
end

まぁなんとも他愛もない感じで.


ついでに再起しとくか.

class Array
  def myreverse3
    rev = []
    xs = self.dup
    reverse = lambda{
      return rev if xs.empty?
      rev << xs.pop
      reverse[xs]
    }
    reverse[xs]
  end
end

実行結果は割愛.


そうそう,これを書いてる時に,reject!というメソッドを見つけたのですが,これを適用してエラトステネスの篩を書いてみます.

def primes(n)
  prime = (2 .. n).to_a
  prime.each{|e|
    next if e == nil
    break if (n / e) < 2
    (2 .. (n / e)).each{|x|
      prime[(e * x) - 2] = nil
    }
  }
  prime.reject!{|y| y == nil}
end

prime = primes(1000000)
p prime.last
p prime.length

なんかもう鬼の様に早くなった.

$ time ruby prime.rb 
999983
78498

real    0m8.736s
user    0m8.352s
sys     0m0.110s

追記

ぶっちゃけdelete nilの方が可読性あるよな,ってベットの中で考えてると寝付けなかったのでやってみたのですが,

prime.delete nil

だと返り値はnilで,primeじゃないので,これを関数の返り値にはできない.だからprimeを返り値にしようとすると,

prime.delete nil
prime

と一行長くなってしまう.その点,reject!なら,返り値がブロックで評価された要素が削除された配列なので,

prime.reject!{|e| e == nil}

で良い事になる.しかし,破壊的なreject!である必要はなく,rejectでも削除された配列が返るので,普通にrejectで良いか.実行時間も変わんなかったし.んじゃ,

prime.reject{|e| e == n}

でいいや.


破壊的じゃない場合って,返り値は新しいメモリなんかな.てか関数の返り値自体,新しいメモリなんか?あ,ガベージコレクションがあるからメモリ云々は気にしちゃ駄目ですか.