第44回 Ruby/Rails勉強会@関西に行ってきた
初級者向けレッスンの演習問題
Arrayクラスの次のメソッドを Stringクラスにも作ってください。 文字列はアスキー文字 (1バイト文字) だけからなるもとのします。 sort については、非破壊的なメソッドと破壊的なメソッドを作ってください。
ruby1.9 を使えばマルチバイト文字でもそのまま動く。
アプローチとして String# を使う方法と Array にして処理してから join する方法がある。(テキスト §2.3 および §2.4((テキストは第44回 Ruby/Rails勉強会@関西のページ参照。)) )
String# の方で素直に書いてみた。
#! /usr/local/bin/ruby19 # -*- coding: utf-8; -*- class String def push(o) self[size] = o self end def pop val = self[-1] self[-1] = '' unless empty? val end def shift val = self[0] self[0] = '' val end def unshift(o) self[0,0] = o self end def first self[0] end def last self[-1] end def sort!(p = 0, q = size - 1, &comp) return self if p >= q comp = :<=> unless comp i = partition!(p, q, &comp) sort!(p, i-1, &comp) sort!(i+1, q, &comp) self end def sort(p = 0, q = size - 1, &comp) s = dup s.sort!(p, q, &comp) end private def partition!(p, q, &comp) i = p (p+1 .. q).each do |j| if comp[self[p], self[j] ] > 0 i += 1 self[i], self[j] = self[j], self[i] end end self[p], self[i] = self[i], self[p] i end end if __FILE__ == $0 require 'test/unit' class TestArrayMethods <Test::Unit::TestCase def test_push s = '' assert_equal('a', s.push('a'), '空文字列に push すると与えた文字が返ること') assert_equal('a', s, 'push は破壊的であること') assert_equal('ab', s.push('b'), 'push すると文字を末尾に追加した文字列か返ること') assert_equal('ab', s, 'push は破壊的であること') assert_equal('abcd', s.push('cd'), '2文字以上 push できること') assert_equal('abcd', s, 'push は破壊的であること') assert_equal('abcd', s.push(''), '空文字を push しても変化しないこと') assert_equal('abcd', s, '変化していないこと') end def test_pop s = 'abcd' assert_equal('d', s.pop, 'pop すると最後の文字が返ること') assert_equal('abc', s, 'pop は破壊的であること') s.pop s.pop assert_equal('a', s.pop, '最後の 1 文字が返ること') assert_equal('', s, '空であること') assert_equal(nil, s.pop, '空文字列に pop すると nil が返ること') assert_equal('', s, '空であること') end def test_shift s = 'abcd' assert_equal('a', s.shift, 'shift すると最初の文字が返ること') assert_equal('bcd', s, 'shift は破壊的であること') s.shift s.shift assert_equal('d', s.shift, '最後の 1 文字が返ること') assert_equal('', s, '空であること') assert_equal(nil, s.shift, '空文字列に shift すると nil が返ること') assert_equal('', s, '空であること') end def test_unshift s = '' assert_equal('d', s.unshift('d'), '空文字列に unshift すると与えた文字が返ること') assert_equal('d', s, 'unshift は破壊的であること') assert_equal('cd', s.unshift('c'), 'unshift すると文字を先頭に挿入した文字列が返ること') assert_equal('cd', s, 'unshift は破壊的であること') assert_equal('abcd', s.unshift('ab'), '2文字以上 unshift できること') assert_equal('abcd', s, 'unshift は破壊的であること') assert_equal('abcd', s.unshift(''), '空文字を unshift しても変化しないこと') assert_equal('abcd', s, '変化していないこと') end def test_first s = 'abcd' assert_equal('a', s.first, 'first は先頭の文字を返すこと') assert_equal('abcd', s, 'first 非破壊的であること') assert_equal(nil, ''.first, '空文字列に first すると nil を返すこと') end def test_last s = 'abcd' assert_equal('d', s.last, 'last は末尾の文字を返すこと') assert_equal('abcd', s, 'last は非破壊的であること') assert_equal(nil, ''.last, '空文字列に last すると nil を返すこと') end def test_sort s = 'higaki' assert_equal(s.split('').sort.join, s.sort, 'Array#sort と同じ結果であること') assert_equal('higaki', s, 'sort は非破壊的であること') assert_equal(s.split('').sort{|a,b|b<=>a}.join, s.sort{|a,b|b<=>a}, 'Array#sort と同じ結果であること') assert_equal('higaki', s, 'sort は非破壊的であること') assert_equal('', ''.sort, '空文字列に sort しても死なないこと') assert_equal(s.split('').sort.join, s.sort!, 'Array#sort と同じ結果であること') assert_equal('higaki'.split('').sort.join, s, 'sort! は破壊的であること') assert_equal(s.split('').sort{|a,b|b<=>a}.join, s.sort!{|a,b|b<=>a}, 'Array#sort と同じ結果であること') assert_equal('higaki'.split('').sort{|a,b|b<=>a}.join, s, 'sort! は破壊的であること') end end end
- とにかく String#[]
- << も使わない。
- sort も String#[]
- 意地でも String#replace は使わない。
- 以前 id:ujihisa がよそのブログに書いてたのをパクった。
- sort{|a,b| ...} にも対応
- 以前マージソートでもやったし。
- sort_by は省略
- 以前マージソートでもやったし。