Invertire le parole, spostare le vocali e abbassare tutto

Aug 19 2020

Sono nuovo di zecca su Ruby.

La funzione accetta una stringa di qualsiasi numero di parole e inverte l'ordine delle parole. Inoltre, per ogni parola, prende le vocali e le sposta alla fine della parola. Riduce anche tutto. Così, Hello World!diventerebbe wrld!o hlleo.

Sto cercando di utilizzare alcune funzionalità di Ruby, ecco perché è una riga per così dire. Fondamentalmente sto solo cercando suggerimenti di stile. È appropriato fare una cosa del genere in questo modo (una riga?). Sono sicuro che ci sono funzioni che potrebbero svolgere l'attività più rapidamente, quindi sono aperto anche a questi suggerimenti, poiché il mio codice è molto lungo e contorto. Inoltre dovrei menzionare che volevo scrivere questo solo con Ruby base, senza pacchetti/gemme extra.

Qualcuno ha suggerito Rubocop e la Guida allo stile, quindi li controllerò.

  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

Risposte

3 FMc Aug 26 2020 at 08:45

Le battute sono molto divertenti, ma il mondo non ne ha davvero bisogno di più. Detto questo, non devono essere così illeggibili. Cosa dirà il tuo futuro cervello tra un anno se dovrai mantenere quella funzione?

Ecco un approccio che illustra una tecnica scalabile per rendere leggibili anche lunghe "one-liner" (1) utilizzando generosamente le righe, (2) indentando il codice alla maniera di una struttura di dati ben stampata per trasmettere la gerarchia della logica (il codice è dati, dopo tutto), e (3) includendo commenti per aiutare il lettore con la logica e l'intento.

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

La tua soluzione fa quanto segue:

  • downcase la stringa
  • convertire la stringa risultante in un array di parole
  • invertire l'ordine delle parole
  • convertire ogni parola nell'array in modo che le vocali siano alla fine e l'ordine sia preservato sia per le vocali che per le non vocali
  • unire le parole nell'array risultante per formare una stringa

Iniziamo con la penultima operazione. Per rendere il codice più leggibile e velocizzare il test, rendiamolo un metodo separato.

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

Vedi String#each_char , String#include? e String#join .

A parte: avrei potuto scrivere word.chars do |char|...al posto di word.each_char do |char|..., ma il primo ha lo svantaggio di word.charsrestituire un array intermedio, mentre il secondo restituisce un enumeratore, consumando così meno memoria.

Proviamolo:

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

Se lo desideriamo, potremmo creare VOWELSun set (per utilizzare Set#include? , che potrebbe velocizzare un po' i calcoli:

require 'set'

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

Ora possiamo scrivere il resto del metodo attorno a shove_vowels_to_end:

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

Discuterò il codice ma prima proviamolo:

str = "Little Miss Muffett sat on her tuffet"

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

A seconda di ciò che si sa str, potrebbe essere necessario passare str.splita str.strip.split. str.splitè uguale a str.split(/\s+/), che è probabilmente appropriato. Vedi String#split .

Il calcolo intermedio è:

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

ecco perché abbiamo bisogno .join(' ')alla fine.

Si noti che gli spazi extra non vengono preservati:

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

Ecco un modo di scrivere più simile a 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

Vedere Enumeratore#with_object .

Si noti come ho utilizzato la scomposizione dell'array a vantaggio durante la scrittura delle variabili di blocco:

|char, (non_vowels, vowels)|

Ecco un altro modo di scrivere funky_words. Modifico ogni sequenza di non spazi con 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"

Valuta di modificare la parola 'muffett'. È diventare 'mffttue'. Tuttavia, poiché inverto la stringa alla fine, devo convertire 'muffett'in 'muffett'.reverse #=> 'euttffm'. Che si ottiene nei seguenti passaggi:

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`