我正在使用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”已存在,则用户遵循以下顺序:
最后一个条目成功运行,但我不想这样做,因为它正在跳过检查重复项的条件。有没有更好的方法同时循环这些条件,每个新条目?
感谢您的帮助!
答案 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:"
。后一条消息部分是为了提醒用户正在重命名哪个文件。"f1"
更改为"f2"
并将"f2"
更改为"f1"
。)如果原始名称都不会用作新文件名,则必须进行额外测试被添加到bad_name?
(并且fnames
必须作为该方法的第三个参数传递。)答案 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
请告诉我这是否正在做你想要的事情!