web-dev-qa-db-fra.com

Inverser une chaîne en Ruby

Comment inverser une chaîne en Ruby? Je connais la chaîne # reverse. Je suis intéressé à comprendre comment l'écrire en Ruby pur, de préférence une solution sur place.

35
rubynoob

Il existe déjà une méthode inverse en place, appelée "reverse!":

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

Si vous voulez le faire manuellement, essayez ceci (mais ce ne sera probablement pas compatible avec plusieurs octets, par exemple UTF-8), et ce sera plus lent:

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

Cela échange chaque octet depuis le début avec chaque octet depuis la fin jusqu'à ce que les deux index se rencontrent au centre:

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

Juste pour discussion, avec autant de remplaçants, il est bon de voir s’il existe des différences majeures de rapidité/efficacité. J'ai nettoyé le code un peu car le code montrant la sortie inversait à plusieurs reprises les sorties. 

# 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

Et les résultats:

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)

Il est intéressant pour moi que la version "C" ("reverse_string ()") soit la version pure de Ruby la plus rapide. # 2 ("reverse!") Est le plus rapide mais profite du [].reverse, qui est en C.

  • Edité par Marc-André Lafortune *

Ajouter un cas de test supplémentaire (7):

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

Si la chaîne est plus longue (lorum *= 10, n/=10), on peut voir que la différence s'élargit car certaines fonctions sont en O (n ^ 2) alors que d'autres (les miennes :-) sont en 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)
11
the Tin Man

Voici un moyen de le faire avec inject et unshift:

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

L'équivalent Ruby de la variable reverse intégrée pourrait ressembler à:

# 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é"

Remarque : ceci suppose Ruby 1.9, ou bien require "backports" et définissez $KCODE pour UTF-8.

Pour une solution n'impliquant pas reverse, on pourrait faire:

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

Remarque: toute solution utilisant [] pour accéder à des lettres individuelles sera de l’ordre O(n^2); Pour accéder à la 1000ème lettre, Ruby doit parcourir les 999 premières lettres une par une pour rechercher les caractères multi-octets. Il est donc important d’utiliser un itérateur tel que each_char pour une solution dans O(n).

Une autre chose à éviter est de construire des valeurs intermédiaires de longueur croissante; utiliser += au lieu de << dans alt_reverse rendrait également la solution O(n^2) au lieu de O(n).

Construire un tableau avec unshift rendra également la solution O(n^2), car cela implique de recopier tous les éléments existants d’un index supérieur à chaque fois que l’on fait unshift.

7
str = "something"
reverse = ""
str.length.times do |i|
  reverse.insert(i, str[-1-i].chr)
end
4
Rishav Rastogi
"abcde".chars.reduce{|s,c| c + s }          # => "edcba"
4
abigure

Utilisation

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.
3
VoodooChild

La solution décrite ci-dessous. Il n'est pas nécessaire d'aller au-delà de la moitié de la taille du tableau:

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
2
Anatoly

En outre, en utilisant 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 serait pratique ici car vous pouvez alors imbriquer votre algorithme d'inversion (tout en conservant les éléments sur une seule ligne). Cela serait pratique si, par exemple, vous deviez inverser chaque mot dans une phrase déjà inversée:

# 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"
1
Ben

Difficile à lire sur une ligne,

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
1
dagoof
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
1
nabin

Pensez à la façon dont Rubinius implémente la méthode - ils implémentent une grande partie de la bibliothèque principale de Ruby même, et je ne serais pas surpris que String#reverse et String#reverse! soient implémentés dans Ruby.

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

 result

end 
1
EdwardF

C’est la solution qui a le plus de sens pour moi en tant que débutante Ruby 

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")
1

Si vous avez la phrase "La plus grande victoire est que" et que vous voulez avoir "c'est la victoire la plus grande", vous devez utiliser cette méthode

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

solution("The greatest victory is that")
0
Nikita Sitnikov
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"
0
Bryan Finlayson

Voici une alternative simple: il décompose d'abord la chaîne en un tableau, en compte la longueur et en soustrait une (à cause de la règle d'indexation de Ruby pour un tableau commençant à 0), crée une variable vide, puis exécute une itération sur les clés du tableau ajouter la valeur de la longueur du tableau moins l'index du tableau actuel à la variable vide créée et quand il atteint la valeur zeroth (désolé pour mon français), il s'arrête. J'espère que cela t'aides.

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
0
Precious George

Voici une alternative utilisant les opérations xor au niveau du bit:

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
0
patrick

En rubis:

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) 
0
bobsags

Je crois que cela fonctionnerait aussi

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

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

return reversed_string
end
0
Ethan