2017-08-20 5 views
0

J'ai actuellement cette méthode dans mon travail pour une tâche Ruby où j'ai besoin de changer les voyelles et les consonnes dans un nom mis par un utilisateur, à la voyelle/consonne successive suivante, donc (a = e, e = i, etc.) et le même processus pour les consonnes (b = c, d = f, etc.). Je l'ai fait de manière 'facile', mais maintenant j'ai besoin de le changer pour qu'au lieu de cela, j'écrète à travers un tableau et change les voyelles/consonnes de cette façon. Je suis nouveau à l'itération donc j'ai des problèmes avec cela.Comment puis-je itérer dans un tableau et modifier les caractères efficacement?

Voici ma méthode originale:

puts "What is your full name?" 
full_name = gets.chomp 

def vowelreplace(full_name) 
vowels = 'aeiou' 
replacements = 'eioua' 
full_name.tr(vowels, replacements) 
end 

name_next_vowel = vowelreplace(full_name) 

p name_next_vowel 

def consonantreplace(name_next_vowel) 
    consonants = 'bcdfghjklmnpqrstvwxyz' 
    replacements = 'cdfghjklmnpqrstvwxyzb' 
    name_next_vowel.tr(consonants, replacements) 
end 

new_spyname = consonantreplace(name_next_vowel) 

p new_spyname 

Voici ce que j'ai commencé à travailler avec le changer ci-dessous à l'aide d'un tableau et la méthode bloc. Y a-t-il une façon moins longue et plus facile de faire cela? Existe-t-il une nouvelle façon de le faire, ou en général, un moyen plus efficace?

puts "What is your first name?" 
first_name = gets.chomp 

arr = first_name.split("") 

p arr 

arr.map! { |element| 
    if(element == "a") 
    "e" 
    elsif(element == "e") 
    "i" 
    elsif(element == "i") 
    "o" 
    elsif(element == "o") 
    "u" 
    elsif(element == "u") 
    "a" 
    else 
    element 
    end 
} 

p arr 

new_arr = arr 

new_arr.map! { |element| 
    if(element == "b") 
    "c" 
    elsif(element == "c") 
    "d" 
    elsif(element == "d") 
    "f" 
    elsif(element == "f") 
    "g" 
    elsif(element == "g") 
    "h" 
    elsif(element == "h") 
    "j" 
    elsif(element == "j") 
    "k" 
    elsif(element == "k") 
    "l" 
    elsif(element == "l") 
    "m" 
    elsif(element == "m") 
    "n" 
    elsif(element == "n") 
    "p" 
    elsif(element == "p") 
    "q" 
    elsif(element == "q") 
    "r" 
    elsif(element == "r") 
    "s" 
    elsif(element == "s") 
    "t" 
    elsif(element == "t") 
    "v" 
    elsif(element == "v") 
    "w" 
    elsif(element == "w") 
    "x" 
    elsif(element == "x") 
    "y" 
    elsif(element == "y") 
    "z" 
    elsif(element == "z") 
    "b" 
    else 
    element 
    end 
} 

p new_arr 

arr.join("") 
+0

Puisqu'il s'agit d'une mission, je suis hésitant à donner une solution complète. D'abord, vous pouvez facilement utiliser la méthode 'tr' comme vous l'avez fait à l'origine, mais faites-le pour chaque caractère de votre bloc' map' (itérateur). Une autre possibilité consiste à créer un mappage entre les caractères d'origine et ce à quoi ils doivent être transformés. "a" => "e", "b" => "c", etc. C'est juste la notation Hash! Vous pourriez chaque caractère pour rechercher facilement la prochaine valeur. –

Répondre

1

J'aime l'approche tr, mais si vous voulez quelque chose de similaire à next, alors Array#rotate pourrait être une bonne option; voici un exemple:

def letter_swap(full_name) 
    vowels  = %w(a e i o u) 
    consonants = %w(b c d f g h j k l m n p q r s t v w x y z) 

    full_name.each_char.with_object("") do |char, new_name| 
    if vowels.include?(char) 
     new_name << vowels.rotate(vowels.index(char) + 1).first 
    elsif consonants.include?(char) 
     new_name << consonants.rotate(consonants.index(char) + 1).first 
    else 
     new_name << char 
    end 
    end 
end 

Bien sûr, vous pouvez DRY ce code, mais au détriment de la lisibilité (OMI); par exemple:

def letter_swap(full_name) 
    letters = [%w(a e i o u), %w(b c d f g h j k l m n p q r s t v w x y z)] 

    full_name.each_char.with_object("") do |char, new_name| 
    group = letters.find { |group| group.include?(char) } 
    new_name << (group.nil? ? char : group.rotate(group.index(char) + 1).first) 
    end 
end 
+0

Merci beaucoup! J'aime beaucoup la première méthode pour sa lisibilité ... c'est logique. Je n'ai pas encore utilisé la méthode de rotation, c'est génial! –

+0

@BrittFlowers Content de vous aider! :) N'oubliez pas de marquer la réponse comme acceptée (celle-ci, ou celle que vous trouvez la plus utile) en cliquant sur la coche à gauche. – Gerry

+1

merci pour les heads up! Je suis un novice de débordement de pile, donc j'apprécie les conseils :) –

1

Vous pourriez peut-être utiliser une série d'instructions de cas à l'intérieur de votre boucle de carte à la place?

arr.map! { |element| 
    case element 
    when "a" then "e" 
    when "e" then "i" 
    when "i" then "o" 
    when "o" then "u" 
    when "u" then "a" 
    end 
} 

irait de même pour les consonnes et ..

+0

Merci beaucoup! Wow, c'est un moyen beaucoup plus facile, génial! –

+0

De rien! J'apprécierais vraiment un upvote si vous sentez que ma réponse a été utile! – JSilvia

1

Vous pouvez utiliser String#tr avec un tableau de caractères comme vous le feriez avec une chaîne, mais tr a la complexité du temps O (n), étant donné que les caractères du premier argument sont examinés successivement en essayant de trouver un match. Si un certain nombre de ces recherches doivent être effectuées, il serait beaucoup plus rapide d'utiliser un hachage, qui a un temps de recherche presque constant. Vous pourriez écrire une méthode comme suit.

code

VOWELS = %w|a e i o u| 
    #=> ["a", "e", "i", "o", "u"] 
NEXT_VOWEL = VOWELS.zip(VOWELS.rotate(1)).to_h 
    #=> {"a"=>"e", "e"=>"i", "i"=>"o", "o"=>"u", "u"=>"a"} 
CONSONANTS = ('a'..'z').to_a - VOWELS 
    #=> ["b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", 
    # "p", "q", "r", "s", "t", "v", "w", "x", "y", "z"] 
NEXT_CONSONANT = CONSONANTS.zip(CONSONANTS.rotate(1)).to_h 
    #=> {"b"=>"c", "c"=>"d", "d"=>"f", "f"=>"g", "g"=>"h", "h"=>"j", "j"=>"k", 
    # "k"=>"l", "l"=>"m", "m"=>"n", "n"=>"p", "p"=>"q", "q"=>"r", "r"=>"s", 
    # "s"=>"t", "t"=>"v", "v"=>"w", "w"=>"x", "x"=>"y", "y"=>"z", "z"=>"b"} 

def code(arr) 
    arr.map { |c| NEXT_CONSONANT[c] || NEXT_VOWEL[c] || c } 
end 

Vraisemblablement, vous devez être en mesure de décoder aussi bien. Vous pouvez écrire la méthode de décodage comme suit, en utilisant la méthode Hash#invert.

PREVIOUS_VOWEL = NEXT_VOWEL.invert 
    # => {"e"=>"a", "i"=>"e", "o"=>"i", "u"=>"o", "a"=>"u"} 
PREVIOUS_CONSONANT = NEXT_CONSONANT.invert 
    #=> {"c"=>"b", "d"=>"c", "f"=>"d", "g"=>"f", "h"=>"g", "j"=>"h", "k"=>"j", "l"=>"k", 
    # "m"=>"l", "n"=>"m", "p"=>"n", "q"=>"p", "r"=>"q", "s"=>"r", "t"=>"s", "v"=>"t", 
    # "w"=>"v", "x"=>"w", "y"=>"x", "z"=>"y", "b"=>"z"} 

def decode(arr) 
    arr.map { |c| PREVIOUS_CONSONANT[c] || PREVIOUS_VOWEL[c] || c } 
end 

Exemple

de code d'abord la matrice.

arr = "billy-bob".chars 
    #=> ["b", "i", "l", "l", "y", "-", "b", "o", "b"] 
a = code(arr) 
    #=> ["c", "o", "m", "m", "z", "-", "c", "u", "c"] 
a.join 
    #=> "commz-cuc" 

Puis le décoder.

a = decode ["c", "o", "m", "m", "z", "-", "c", "u", "c"] 
    #=> ["b", "i", "l", "l", "y", "-", "b", "o", "b"] 
a.join 
    #=> "billy-bob" 

Explication

Considérons l'expression

NEXT_CONSONANT[c] || NEXT_VOWEL[c] || c 

dans le procédé code. Supposons c = 'd'. Alors NEXT_COSONANT['d'] #=> 'f', nous avons donc

'f' || NEXT_VOWEL[c] || c 

Depuis 'f' est truthy, 'f' est retourné. Ruby n'a aucune raison d'évaluer NEXT_VOWEL[c] || c alors elle ne le fait pas.

Supposons maintenant c = 'e'. Alors, puisque NEXT_CONSONANT n'a pas 'e' clé, NEXT_CONSONANT['e'] #=> nil et l'expression devient

nil || NEXT_VOWEL[c] || c 

Il est donc nécessaire d'évaluer NEXT_VOWEL['e']. Comme cela renvoie 'i', ce qui est la vérité, 'i' est retourné et il n'est pas nécessaire d'évaluer c.

Enfin, si c = '-', on obtient

nil || nil || c 

si c est évaluée et retournée.