2017-09-07 2 views
1

Rubyを使用して、ユーザーからの入力を取得してファイルのリストの新しい名前を提供しています。名前を配列に格納していますが、格納する前に、ユーザーの入力が有効であることを確認するための一連の条件があります。全体的に、これはかなりうまく動作しますが、私はそれぞれ「中をループに確認するすべてが満たされるまで複数の条件でループする

puts "Rename file to:" 
new_name = gets.chomp 
new_name = check_input(new_name,@all_names) 
@all_names << new_name 

def check_input(new_name,all_names) 

    while new_name.match(/\s/) 
     puts "Names must not contain spaces:" 
     new_name = gets.chomp 
    end 

    while new_name.empty? 
     puts "Please enter a name:" 
     new_name = gets.chomp 
    end 

    while all_names.include? new_name 
     puts "That name already exists. Please enter a different name:" 
     new_name = gets.chomp 
    end 

    return new_name 

end 

:それは本質的に、この(私は質問に関係のないコードの一部を削除した)に帰着します条件は何度も繰り返され、すべての条件が満たされます。例えば、名前「ABC」が既に存在する場合、ユーザーがこの順に従います。

  1. が「ABC」=>入る「その名前がす​​でに存在している別の の名前を入力してください」
  2. は「ABCに入ります"=>『名前にはスペースを含めることはできません』
  3. 入る『最後のエントリが正常に動作しますが、それがためにチェックする条件をスキップなので、私は、それはしたくない=>

再びABCを』重複します。新しいエントリごとに、これらの条件を同時にループするより良い方法がありますか?

ありがとうございました!

答えて

3

ループの正しいアイデアは、間違った場所です。可能性のあるすべての無効なケースに対してユーザーのgetsを確認する必要があります。あなたは、単一の無効な場合は、渡されたまでチェックしてから、前のケース(s)がまだ経過しているかどうかを確認していない別のものに起こっていたやっていた。

# outputs an error message and returns nil if the input is not valid. 
# Otherwise returns the input 
def check_input(input, all_names) 
    if input.match(/\s/) 
    puts "Name must not contain spaces:" 
    elsif input.empty? 
    puts "Please enter a name:" 
    elsif all_names.include?(input) 
    puts "That name already exists. Please enter a different name:" 
    else 
    input 
    end 
end 

@all_names = ['abc'] 
puts "Rename file to:" 
# keep gets-ing input from the user until the input is valid 
name = check_input(gets.chomp, @all_names) until name 
@all_names << name 
puts @all_names.inspect 

puts以来戻っnilcheck_inputを入力が有効でない場合はnilを返します。それ以外の場合、最終的にelseに有効な入力を返し、変数nameに割り当てて、untilループの実行を停止します。

例は、実行します。

名前の変更ファイルへ:名前がすでに存在していること
ABC
。別の名前を入力してください:
bはC
名にはスペースを含めることはできません。
ABC
名がすでに存在していること。別の名前を入力してください:
abc23
[ "ABC"、 "abc23"]

+0

美しく効率的です。どうもありがとうございます! – lumos

0

これは(ちょうどここのいずれかの条件を示し、他は同じ構造です)再帰のために活用することができます:基本的に

def check_input(new_name,all_names) 

    # using if instead of while ... recursion provides the 'loop' here 
    if new_name.match(/\s/) 
     puts "Names must not contain spaces:" 
     new_name = check_input(gets.chomp, all_names) 
    end 

    # etc, other conditionals 

    new_name 

end 

、入力がすべて通過するまでこれらnew_name割り当てのどれも解決しないだろう小切手プログラムはより深くスタックに移動しますが、一部の入力がすべてのチェックを通過するとすぐにすべてが解決されます。

0

ええ、再帰は私が思うようなことをする正しい方法です。ただ、test.rbファイルでこれを投げるとruby test.rbを実行します。

@all_names = [] 

def check_name(name = nil) 
    # Find out if it's invalid and why 
    invalid_reason = if name.empty? 
     "Please enter a name:" 
    elsif name.match(/\s/) 
     "Names must not contain spaces:" 
    elsif @all_names.include?(name) 
     "That name already exists. Please enter a different name:" 
    end 

    # Either return the name or ask for it again 
    if invalid_reason 
     puts invalid_reason 
     name = check_name(gets.chomp) 
    end 

    # Once we have it return the name! 
    name 
end 

puts "Rename file to:" 
new_name = check_name(gets.chomp) 
puts "Successfully storing name '#{new_name}'..." 
@all_names << new_name 

は、それはあなたが探していたものをやっている場合は、私に教えてください!

1

コード

def rename_files(fnames) 
    fnames.each_with_object({}) do |fn,h| 
    loop do 
     puts "Rename file '#{fn}' to:" 
     new_name = gets.chomp 
     bad_name = bad_name?(new_name, h) 
     if bad_name 
     print bad_name 
     else 
     h.update(new_name=>fn) 
     break 
     end 
    end 
    end.invert 
end 

def bad_name?(new_name, h) 
    if new_name.include?(' ') 
    "Names must not contain spaces. " 
    elsif new_name.empty? 
    "Names cannot be empty. " 
    elsif h.key?(new_name) 
    "That name already exists. Duplicates are not permitted. " 
    else 
    nil 
    end 
end 

rename_files(["cat", "dog", "pig"]) 
Rename file 'cat' to: 
    # <enter "three blind mice"> 
Names must not contain spaces. Rename file 'cat' to: 
    # <enter ENTER only> 
Names cannot be empty. Rename file 'cat' to: 
    # <enter "three_blind_mice"> 
Rename file 'dog' to: 
    # <enter "four_blind_mice"> 
Rename file 'pig' to: 
    # <enter "three_blind_mice?> 
That name already exists. Duplicates are not permitted. Rename file 'pig' to: 
    # <enter "five_blind_mice" 
    #=> {"cat"=>"three_blind_mice", "dog"=>"four_blind_mice", "pig"=>"five_blind_mice"} 

ノート

  • bad_name?提案されたファイル名が指定された3つのテストのいずれかに対して無効な場合は(真実の)メッセージ文字列を返します。それ以外の場合はnilが返されます。
  • bad_name?が真理値を返す場合は、putsではなくprintを使用して印刷されます。同じ行にputs "Rename file '#{fn}' to:"が続きます。後者のメッセージは、部分的には、どのファイルが名前変更されているかをユーザーに思い出させるためのものです。
  • Hash.key?は、提案されたファイル名がすでに入力されているものと重複しているかどうかを判断するために使用されます。これは、ハッシュキールックアップが配列要素の検索に使用される線形検索よりもはるかに高速であるためです。
  • 新しい名前には元の名前が含まれる場合があります。したがってファイルの名前を変更する際には注意が必要です。たとえば、"f1""f2"に変更され、"f2""f1"に変更されたとします。元の名前が新しいファイル名として使用されない場合、追加のテストをbad_name?に追加する必要があります(そして、fnamesをその3番目の引数として渡す必要があります)方法)。
  • 最後のステップとして反転された(Hash#invertを使用)ので、キーは元のファイル名になります。
関連する問題