循环遍历多个条件,直到满足所有条件

时间:2017-09-07 18:44:12

标签: ruby loops while-loop conditional-statements

我正在使用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

总的来说这很有效,但我想确保一次又一次地遍历每个“while”条件,直到满足所有条件。例如,如果名称“abc”已存在,则用户遵循以下顺序:

  1. 输入“abc”=&gt; “这个名字已经存在。请输入其他名称 名称“
  2. 输入“a b c”=&gt; “名称不得包含空格”
  3. 再次输入“abc”=&gt;
  4. 最后一个条目成功运行,但我不想这样做,因为它正在跳过检查重复项的条件。有没有更好的方法同时循环这些条件,每个新条目?

    感谢您的帮助!

4 个答案:

答案 0 :(得分:3)

循环的正确想法,只是错误的地方。您需要针对所有可能的无效案例检查用户的每个gets。你正在做的是检查,直到一个无效的案件通过,然后转到另一个案件,但没有检查以前的案件是否仍然通过:

# 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返回nil,如果输入无效,check_input将返回nil。否则,在最终else中,我们将返回有效输入并将其分配给变量name并停止执行until循环。

示例运行:

  

将文件重命名为:
  ABC
  那个名字已经存在。请输入其他名称:
  a b c
  名称不得包含空格:
  ABC
  那个名字已经存在。请输入其他名称:
  abc23
  [“abc”,“abc23”]

答案 1 :(得分: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?返回(truthy)消息字符串;否则返回nil
  • 如果bad_name?返回真值,则使用print而不是puts打印,因为在同一行后面会跟puts "Rename file '#{fn}' to:"。后一条消息部分是为了提醒用户正在重命名哪个文件。
  • Hash.key?用于确定建议的文件名是否与已输入的文件名重复,部分原因是哈希键查找比用于查找数组元素的线性搜索快得多。
  • 新名称可能包含原始名称。因此必须注意重命名文件。 (例如,考虑将"f1"更改为"f2"并将"f2"更改为"f1"。)如果原始名称都不会用作新文件名,则必须进行额外测试被添加到bad_name?(并且fnames必须作为该方法的第三个参数传递。)
  • 正在构造的哈希被反转(使用Hash#invert)作为最后一步,因此键是原始文件名。

答案 2 :(得分: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分配都不会解决。程序深入到堆栈中,但一旦输入通过所有检查,一切都会解决。

答案 3 :(得分: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

请告诉我这是否正在做你想要的事情!