Ruby - 将块传递给方法

43

我想用Highline gem实现Ruby密码输入。由于我要求用户输入密码两次,我希望能够消除我传递的块中的重复部分。例如,我目前正在执行的简单版本是:

new_pass = ask("Enter your new password: ") { |prompt| prompt.echo = false }
verify_pass = ask("Enter again to verify: ") { |prompt| prompt.echo = false }

我希望将它更改为类似于这样的形式:

foo = Proc.new { |prompt| prompt.echo = false }
new_pass = ask("Enter your new password: ") foo
verify_pass = ask("Enter again to verify: ") foo

很不幸,这不起作用。正确的方法是什么?

5个回答

74

David编写的代码可以正常工作,但这是一个更简单和更短的解决方案:

foo = Proc.new { |prompt| prompt.echo = false }
new_pass = ask("Enter your new password: ", &foo)
verify_pass = ask("Enter again to verify: ", &foo)

在定义一个方法时,你可以使用 & 符号将一个块赋值给一个变量:

def ask(msg, &block)
  puts block.inspect
end

我最初就尝试过这样做(在问这个问题之前),但是当我这样做时,Highline会忽略块的内容,并出现以下错误:undefined method `&' for "inputstring":String (NoMethodError)其中 inputstring 是我在第一个提示中输入的内容。 - Chris Bunch
听起来有点奇怪。也许你忘记了逗号,Ruby假设你想在提示字符串上调用方法“&”?我刚刚尝试了使用HighLine的相同代码,它可以正常工作。 - Adam Byrtek
是的,我想我之前写成了ask("goo") &foo而不是ask("goo", &foo)。现在可以正常工作了。谢谢Adam! - Chris Bunch
也许这对你来说很有趣。http://mudge.name/2011/01/26/passing-blocks-in-ruby-without-block.html - Kirby

13

这是你应该这样做的方式,简洁明了:

def ask(question)
    yield(question)
end

proc = Proc.new { |question| puts question }
new_pass = ask("Enter your new password: ", &proc)
verify_pass = ask("Enter again to verify: ", &proc)

4
foo = Proc.new { |prompt| prompt.echo = false }
new_pass = ask("Enter your new password: ") {|x| foo.call(x)}
verify_pass = ask("Enter again to verify: ") {|x| foo.call(x)}

2
这里有一个示例,它会使用yield方法给索引添加前缀,并使用call方法将索引添加后缀。
class Array
  def alter_each!
    self.each_with_index do |n, i|
      self[i] = yield(n,i)
    end
  end
  def modify_each!(add_one = true, &block)
    self.each_with_index do |n, i|
      j = (add_one) ? (i + 1) : i
      self[i] = block.call(n,j)
    end
  end
end

a = ["dog", "cat", "cow"]

a.alter_each! do |n, i|
  "#{i}_#{n}"
end

a.modify_each! false do |n,i|
  "#{n}_#{i}"
end

puts a

-2

我认为这种语言不支持这样的结构。我能想到的唯一泛化的方法是:

def foo(prompt)
  prompt.echo = false
end
new_pass = ask("Enter your new password: ") { |prompt| foo(prompt) }
verify_pass = ask("Enter again to verify: ") { |prompt| foo(prompt) }

虽然它确实没有缩短代码,但它确实消除了一些重复 - 如果您想做的不仅仅是将prompt.echo设置为false,那么您只需要在一个地方添加代码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接