Pythonのリストのスライスに同等のメソッドをRubyで実装 - Camera Obscura
上の記事では Python のスライスを Ruby の Array に対して実装しましたが、ほんの少しの修正で完全に String にも適用できます。いわゆる「ダック・タイピング」ですね。なので、モジュール化して Array と String に include させました。モジュールのいわゆる Mix-in です。
module Pyrb def pickup(left, right, step=nil) klass = self.class len = self.length unless step return klass.new unless !right or right != 0 left ||= 0 right ||= 0 right -= 1 self[left..right] else if step > 0 left ||= 0 right ||= len left = convp(left, len) right = convp(right, len) right = len if right >= len right -= 1 elsif step < 0 left ||= len - 1 left = len - 1 if left >= len right ||= - len - 1 left = convm(left, len) right = convm(right, len) right = - len - 1 if right < - len - 1 right += 1 else raise "ValueError: slice step cannot be zero" end selected = klass.new left.step(right, step) {|i| selected << self[i]} selected end end def convp(i, len) return i if i >= 0 i + len end def convm(i, len) return i if i < 0 i - len end private :convp, :convm end Array.send(:include, Pyrb) String.send(:include, Pyrb)
実際にうまくいきました。pickup メソッドが、Array と String でまったく同様に実行されています。ポリモーフィズム!
irb(main):050:0> a = "327865" => "327865" irb(main):051:0> a.pickup(0, 4) => "3278" irb(main):052:0> a.pickup(-1, -4, -1) => "568" irb(main):053:0> a.pickup(2, nil) => "7865" irb(main):054:0> b = [3, 2, 7, 8, 6, 5] => [3, 2, 7, 8, 6, 5] irb(main):055:0> b.pickup(0, 4) => [3, 2, 7, 8] irb(main):056:0> b.pickup(-1, -4, -1) => [5, 6, 8] irb(main):057:0> b.pickup(2, nil) => [7, 8, 6, 5] irb(main):058:0> b.pickup(1, 0) => [] irb(main):059:0> a.pickup(1, 0) => "" irb(main):060:0> a.pickup(nil, nil) => "327865" irb(main):061:0> b.pickup(nil, nil) => [3, 2, 7, 8, 6, 5]