开发者

Reverse a string in Ruby

H开发者_StackOverflowow 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.


There's already an inplace reverse method, called "reverse!":

$ a = "abc"
$ a.reverse!
$ puts a
cba

If you want to do this manually try this (but it will probably not be multibyte-safe, eg UTF-8), and it will be slower:

class String
  def reverse_inplace!
    half_length = self.length / 2
    half_length.times {|i| self[i], self[-i-1] = self[-i-1], self[i] }
    self
  end
end

This swaps every byte from the beginning with every byte from the end until both indexes meet at the center:

$ a = "abcd"
$ a.reverse_inplace!
$ puts a
dcba


Just for discussion, with that many alternates, it is good to see if there are major differences in speed/efficiency. I cleaned up the code a bit as the code showing output was repeatedly reversing the outputs.

# encoding: utf-8

require "benchmark"

reverse_proc = Proc.new { |reverse_me| reverse_me.chars.inject([]){|r,c| r.unshift c}.join }

class String
  def reverse # !> method redefined; discarding old reverse
    each_char.to_a.reverse.join
  end

  def reverse! # !> method redefined; discarding old reverse!
    replace reverse
  end

  def reverse_inplace!
    half_length = self.length / 2
    half_length.times {|i| self[i], self[-i-1] = self[-i-1], self[i] }
  end

end

def reverse(a)
  (0...(a.length/2)).each {|i| a[i], a[a.length-i-1]=a[a.length-i-1], a[i]}
  return a
end

def reverse_string(string) # method reverse_string with parameter 'string'
  loop = string.length       # int loop is equal to the string's length
  word = ''                  # this is what we will use to output the reversed word
  while loop > 0             # while loop is greater than 0, subtract loop by 1 and add the string's index of loop to 'word'
    loop -= 1                  # subtract 1 from loop
    word += string[loop]       # add the index with the int loop to word
  end                        # end while loop
  return word                # return the reversed word
end                        # end the method

lorum = <<EOT
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent quis magna eu
lacus pulvinar vestibulum ut ac ante. Lorem ipsum dolor sit amet, consectetur
adipiscing elit. Suspendisse et pretium orci. Phasellus congue iaculis
sollicitudin. Morbi in sapien mi, eget faucibus ipsum. Praesent pulvinar nibh
vitae sapien congue scelerisque. Aliquam sed aliquet velit. Praesent vulputate
facilisis dolor id ultricies. Phasellus ipsum justo, eleifend vel pretium nec,
pulvinar a justo. Phasellus erat velit, porta sit amet molestie non,
pellentesque a urna. Etiam at arcu lorem, non gravida leo. Suspendisse eu leo
nibh. Mauris ut diam eu lorem fringilla commodo. Aliquam at augue velit, id
viverra nunc.
EOT

And the results:

RUBY_VERSION # => "1.9.2"

name = "Marc-André"; reverse_proc.call(name) # => "érdnA-craM"
name = "Marc-André"; name.reverse! # => "érdnA-craM"
name = "Marc-André"; name.chars.inject([]){|s, c| s.unshift(c)}.join # => "érdnA-craM"
name = "Marc-André"; name.reverse_inplace!; name # => "érdnA-craM"
name = "Marc-André"; reverse(name) # => "érdnA-craM"
name = "Marc-André"; reverse_string(name) # => "érdnA-craM"

n = 5_000
Benchmark.bm(7) do |x|
  x.report("1:") { n.times do; reverse_proc.call(lorum); end }
  x.report("2:") { n.times do; lorum.reverse!; end }
  x.report("3:") { n.times do; lorum.chars.inject([]){|s, c| s.unshift(c)}.join; end }
  x.report("4:") { n.times do; lorum.reverse_inplace!; end }
  x.report("5:") { n.times do; reverse(lorum); end }
  x.report("6:") { n.times do; reverse_string(lorum); end }
end

# >>              user     system      total        real
# >> 1:       4.540000   0.000000   4.540000 (  4.539138)
# >> 2:       2.080000   0.010000   2.090000 (  2.084456)
# >> 3:       4.530000   0.010000   4.540000 (  4.532124)
# >> 4:       7.010000   0.000000   7.010000 (  7.015833)
# >> 5:       5.660000   0.010000   5.670000 (  5.665812)
# >> 6:       3.990000   0.030000   4.020000 (  4.021468)

It's interesting to me that the "C" version ("reverse_string()") is the fastest pure-Ruby version. #2 ("reverse!") is fastest but it's taking advantage of the [].reverse, which is in C.

  • Edit by Marc-André Lafortune *

Adding an extra test case (7):

def alt_reverse(string)
  word = ""
  chars = string.each_char.to_a
  chars.size.times{word << chars.pop}
  word
end

If the string is longer (lorum *= 10, n/=10), we can see that the difference widens because some functions are in O(n^2) while others (mine :-) are O(n):

             user     system      total        real
1:      10.500000   0.030000  10.530000 ( 10.524751)
2:       0.960000   0.000000   0.960000 (  0.954972)
3:      10.630000   0.080000  10.710000 ( 10.721388)
4:       6.210000   0.060000   6.270000 (  6.277207)
5:       4.210000   0.070000   4.280000 (  4.268857)
6:      10.470000   3.540000  14.010000 ( 15.012420)
7:       1.600000   0.010000   1.610000 (  1.601219)


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.


Here's one way to do it with inject and unshift:

"Hello world".chars.inject([]) { |s, c| s.unshift(c) }.join


str = "something"
reverse = ""
str.length.times do |i|
  reverse.insert(i, str[-1-i].chr)
end


"abcde".chars.reduce{|s,c| c + s }          # => "edcba"


Use

def reverse_string(string) # Method reverse_string with parameter 'string'.
  loop = string.length # int loop is equal to the string's length.
  word = '' # This is what we will use to output the reversed word.
  while loop > 0 # while loop is greater than 0, subtract loop by 1 and add the string's index of loop to 'word'.
    loop -= 1 # Subtract 1 from loop.
    word += string[loop] # Add the index with the int loop to word.
  end # End while loop.
  return word # Return the reversed word.
end # End the method.


def reverse(string)
  result = ""
  idx = string.length - 1
  while idx >= 0
  result << string [idx]
  idx = idx - 1
 end

 result

end 


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


This is the solution that made the most sense to me as a ruby beginner

def reverse(string)
  reversed_string = ''

  i = 0
  while i < string.length
    reversed_string = string[i] + reversed_string
    i += 1
  end

  reversed_string
end

p reverse("helter skelter")


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"


Hard to read one-liner,

def reverse(a)
    (0...(a.length/2)).each {|i| a[i], a[a.length-i-1]=a[a.length-i-1], a[i]}
    return a
end


Consider looking at how Rubinius implements the method - they implement much of the core library in Ruby itself, and I wouldn't be surprised if String#reverse and String#reverse! is implemented in Ruby.


def palindrome(string)

  s = string.gsub(/\W+/,'').downcase

  t = s.chars.inject([]){|a,b| a.unshift(b)}.join

  return true if(s == t)

  false

end


If you have sentence "The greatest victory is that" and you want to have "that is victory greatest The" you should to use this method

def solution(sentance)
  sentance.split.reverse.join(" ")
end

solution("The greatest victory is that")


Here's an alternative using the xor bitwise operations:

class String

  def xor_reverse
    len = self.length - 1
    count = 0

    while (count < len)
      self[count] ^= self[len]
      self[len] ^= self[count]
      self[count] ^= self[len]

      count += 1
      len -= 1
    end

  self
end

"foobar".xor_reverse
=> raboof


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) 


I believe this would work also

def reverse(str)
  string = ''
   (0..str.size-1).each do |i|
    string << str[str.size - 1 - i]
   end
  string
end


def reverse(string)
reversed_string = ""

idx = 0
while idx < string.length
reversed_string = string[idx] + reversed_string
idx += 1
end

return reversed_string
end


string = "This is my string"
string_arr = string.split('')
n = string_arr.length
new_arr = Array.new

17.times do |i|
  new_arr << string_arr.values_at(n - i)
end

reversed_string = new_arr.flatten.join('')
=> "gnirts ym si sihT"


Here is a simple alternative, it first breaks the string into an array, counts the length and subtracts one(because of ruby's indexing rule for array starting from 0), creates an empty variable, then runs an iteration on the keys of the array whilst appending the value of the array length minus current array index to the empty variable created and when it reaches the zeroth(sorry for my french) value it stops. Hope this helps.

class String
   def rString
       arr = self.split("")
       len = arr.count - 1
       final = ""
       arr.each_index do |i|
           final += arr[len - i]
       end
       final
  end
end


A simple classic way with n/2 complexity

str = "Hello World!";
puts str;

for i in 0..(str.length/2).to_i
  mid = (str.length-1-i);
  temp = str[i];
  str[i] = str[aa];
  str[aa] = temp;
end

puts str;
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜