How do you reverse a string in Ruby? I know about string#reverse. I\'m interested in understanding how to write it in pure Ruby, preferably an in-place solution.
The Ruby equivalent of the builtin reverse
could look like:
# encoding: utf-8
class String
def reverse
each_char.to_a.reverse.join
end
def reverse!
replace reverse
end
end
str = "Marc-André"
str.reverse!
str # => "érdnA-craM"
str.reverse # => "Marc-André"
Note: this assumes Ruby 1.9, or else require "backports"
and set $KCODE
for UTF-8.
For a solution not involving reverse
, one could do:
def alt_reverse(string)
word = ""
chars = string.each_char.to_a
chars.size.times{word << chars.pop}
word
end
Note: any solution using []
to access individual letters will be of order O(n^2)
; to access the 1000th letter, Ruby must go through the first 999 one by one to check for multibyte characters. It is thus important to use an iterator like each_char
for a solution in O(n)
.
Another thing to avoid is to build intermediate values of increasing length; using +=
instead of <<
in alt_reverse
would also make the solution O(n^2)
instead of O(n)
.
Building an array with unshift
will also make the solution O(n^2)
, because it implies recopying all existing elements one index higher each time one does an unshift
.
In Ruby:
name = "Hello World"; reverse_proc.call(name)
name = "Hello World"; name.reverse!
name = "Hello World"; name.chars.inject([]){|s, c| s.unshift(c)}.join
name = "Hello World"; name.reverse_inplace!;
name = "Hello World"; reverse(name)
name = "Hello World"; reverse_string(name)
The solution described below. There is no need to go beyond the half of array size:
class ReverseString
def initialize(array)
@array = array
@size = @array.size
end
def process
(0...@size/2).to_a.each_with_index do |e,i|
@array[i], @array[@size-i-1] = @array[@size-i-1], @array[i]
end
@array
end
end
require 'minitest/autorun'
class ReverseStringTest < Minitest::Unit::TestCase
def test_process
assert_equal "9876543210", ReverseString.new("0123456789").process
end
end
Also, using Procs ...
Proc.new {|reverse_me| reverse_me.chars.inject([]){|r,c| r.unshift c}.join}.call("The house is blue")
=> "eulb si esuoh ehT"
Proc.new would be handy here because you could then nest your reversing algorithm (and still keep things on one line). This would be handy if, for instance, you needed to reverse each word in an already-reversed sentence:
# Define your reversing algorithm
reverser = Proc.new{|rev_me| rev_me.chars.inject([]){r,c| r.unshift c}.join}
# Run it twice - first on the entire sentence, then on each word
reverser.call("The house is blue").split.map {|w| reverser.call(w)}.join(' ')
=> "blue is house The"