Inverser les mots, déplacer les voyelles et tout mettre en minuscules

Aug 19 2020

Je suis tout nouveau sur Ruby.

La fonction prend une chaîne de n'importe quel nombre de mots et inverse l'ordre des mots. De plus, pour chaque mot, il prend les voyelles et les déplace à la fin du mot. Cela minimise également tout. Ainsi, Hello World!deviendrait wrld!o hlleo.

J'essaie d'utiliser certaines fonctionnalités de Ruby, d'où la raison pour laquelle il s'agit d'une ligne pour ainsi dire. Fondamentalement, je cherche juste des suggestions de style. Est-il approprié de faire une telle chose de cette manière (une ligne ?). Je suis sûr qu'il existe des fonctions qui pourraient accomplir la tâche plus rapidement, donc je suis également ouvert à ces suggestions, car mon code est très long et compliqué. Je dois également mentionner que je voulais écrire ceci avec uniquement Ruby de base, pas de packages/gemmes supplémentaires.

Quelqu'un a suggéré Rubocop et le guide de style, je vais donc les vérifier.

  def funky_words(s)
    s.strip.gsub(/\s+/, " ").split(" ").reverse.instance_eval{map{|elt| elt.gsub(/([aeiou])/i,"")}}.
    zip(s.strip.split(" ").reverse.map{|elt| elt.scan(/([aeiou])/i).flatten}.instance_eval{map{|elt| elt.join}}).
    map(&:join).join(" ").downcase
    #first "line" reverses word order removes vowels, second "line" captures vowels, last "line" joins vowels and all words
  end

Réponses

3 FMc Aug 26 2020 at 08:45

Les one-liners sont très amusants, mais le monde n'en a pas vraiment besoin de plus. Cela dit, ils ne doivent pas être si illisibles. Que dira votre futur cerveau dans un an si vous devez maintenir cette fonction ?

Voici une approche illustrant une technique évolutive pour rendre lisible même les longs "one-liners" en (1) utilisant généreusement les lignes, (2) indentant le code à la manière d'une structure de données joliment imprimée pour transmettre la hiérarchie de la logique (le code est une donnée, après tout), et (3) inclure des commentaires pour aider le lecteur avec la logique et l'intention.

def funky_words(s)
  (
    # Split into words.
    s
    .split
    .reverse_each
    .map { |word|
      # Within each word, push vowels to the end, while preserving
      # original order within consonants and vowels.
      word
      .each_char
      .sort_by.with_index { |c, i| "aeiouAEIOU".include?(c) ? [1, i] : [0, i] }
      .join
    }
    # Rejoin the new words.
    .join(" ")
  )
end
1 CarySwoveland Jan 19 2021 at 11:17

Votre solution effectue les opérations suivantes :

  • mettre la chaîne en minuscule
  • convertir la chaîne résultante en un tableau de mots
  • inverser le tableau des mots
  • convertir chaque mot du tableau de sorte que les voyelles soient à la fin et que l'ordre soit préservé pour les voyelles et les non-voyelles
  • joindre les mots dans le tableau résultant pour former une chaîne

Commençons par l'avant-dernière opération. Pour rendre le code plus lisible et accélérer les tests, faisons-en une méthode distincte.

VOWELS = 'aeiou'
def shove_vowels_to_end(word)
  vowels = ''
  non_vowels = ''
  word.each_char do |char|
     if VOWELS.include?(char)
       vowels << char
     else
       non_vowels << char
     end
  end
  [non_vowels, vowels].join
end

Voir String#each_char , String#include? et String#join .

A part : j'aurais pu écrire à la word.chars do |char|...place de word.each_char do |char|..., mais le premier a l'inconvénient de word.charsretourner un tableau intermédiaire, alors que le second retourne un énumérateur, consommant ainsi moins de mémoire.

Essayons:

shove_vowels_to_end("atlastdisgonehurray!")
  #=> "tlstdsgnhrry!aaioeua"      

Si vous le souhaitez, nous pourrions créer VOWELSun ensemble (pour employer Set#include? , ce qui peut accélérer un peu les calculs :

require 'set'

VOWELS = 'aeiou'.each_char.to_set
  #<Set: {"a", "e", "i", "o", "u"}>

On peut maintenant écrire le reste de la méthode autour de shove_vowels_to_end:

def funky_words(str)
  str.downcase.split.map { |word| shove_vowels_to_end(word) }.join(' ')
end

Je vais discuter du code mais essayons d'abord:

str = "Little Miss Muffett sat on her tuffet"

funky_words str
  #=> "lttlie mssi mffttue sta no hre tfftue"

Selon ce que l'on sait str, nous devrons peut-être passer str.splità str.strip.split. str.splitest identique à str.split(/\s+/), ce qui est probablement approprié. Voir String#split .

Le calcul intermédiaire est :

str.downcase.split.map { |word| shove_vowels_to_end(word) }
  #=> ["lttlie", "mssi", "mffttue", "sta", "no", "hre", "tfftue"]

c'est pourquoi nous avons besoin .join(' ')à la fin.

Notez que les espaces supplémentaires ne sont pas conservés :

funky_words "some       spaces"
  #=> "smoe spcsae"

Voici une manière d'écrire plus proche de Ruby shove_vowels_to_end:

def shove_vowels_to_end(word)
  word.each_char.with_object(['', '']) do |char, (non_vowels, vowels)|
     if VOWELS.include?(char)
       vowels << char
     else
       non_vowels << char
     end
  end.join
end

Voir Enumérateur#with_object .

Remarquez comment j'ai utilisé la décomposition de tableau à l'avantage lors de l'écriture des variables de bloc :

|char, (non_vowels, vowels)|

Voici une autre façon d'écrire funky_words. Je modifie chaque séquence de non-espaces avec String#gsub .

require 'set'
VOWELS = %w|a e i o u|.to_set
  #=> #<Set: {"a", "e", "i", "o", "u"}>
def funky_words(str)  
  str.downcase.gsub(/[^ ]+/) do |word|
    vowels = ''
    others = ''
    word.each_char do |char|
      if VOWELS.include?(char)
        vowels.prepend(char)
      else
        others.prepend(char)
      end
    end
    others + vowels
  end.reverse
end
str = "Little Miss Muffett sat on her tuffet"
funky_words(str)
  #=> "tfftue hre no sta mffttue mssi lttlie"

Envisagez de modifier le mot 'muffett'. C'est devenir 'mffttue'. Cependant, puisque j'inverse la chaîne à la fin, je dois convertir 'muffett'en 'muffett'.reverse #=> 'euttffm'. Cela s'obtient dans les étapes suivantes :

muffett
vowels = ''
others = 'm'

uffett
vowels = 'u'
others = 'm'

ffett
vowels = 'u'
others = 'fm'

fett
vowels = 'u'
others = 'ffm'

ett
vowels = 'eu'
others = 'ffm

tt
vowels = 'eu'
others = 'tffm'

t
vowels = 'eu'
others = 'ttffm'

vowels + others
  #=> `euttffm`