【问题标题】:Ruby check if block is nilRuby 检查块是否为零
【发布时间】:2015-10-24 15:59:12
【问题描述】:

我用块调用方法;​​

method do
  "Hello"
end

方法定义为;

def method
  yield
end

以及在定义方法时;我想检查给定的块是否为空(nil),因为方法中的变量可能会像这样结束;

method do
  ""
end

所以在定义上,我想检查 yield 块是否为零。喜欢;

def method
  if yield ? yield : "Empty block? Seriously?"
end

我知道上述方法不起作用。但这正是我想要实现的目标。

还请记住,block_given? 将始终为“true”,因为即使它是 nil空字符串,也会给出块.

更新:由于大多数 cmets/answers 表明问题不清楚;这是@ndn简化的问题:

我想检查执行块的结果是否为“空”(nil 或“”) 首先调用它。

【问题讨论】:

  • 如果我理解正确,你想知道块返回的值是nil还是"",然后再屈服。这不可能。如果不是这种情况,您可以将yield 的结果分配给一个变量,然后让nil?empty? 对其进行检查。
  • 是的,你是对的。
  • 是的,如果我像@WandMaker's answer中那样分配它,如果没有给出块,它会给出错误。所以你说在让步之前没有办法知道块内容是 nil 还是 "" 。是关于这个主题的来源或讨论等,以便我了解更多吗?谢谢。
  • 好吧,让我写一个答案……

标签: ruby yield


【解决方案1】:

不清楚你在问什么,因为块本身不能为空。因此,您可能有几个不同的意思:

  1. 一个缺失的方块。您可以检查是否给出了块

    block_given?
    
  2. 块空体(又名{}do end)。这并非不可能,但需要一些高级的 voodoo ruby​​ 元编程魔法。一般来说,如果这是您正在寻找的内容,那么您要么正在写一些非常有趣的东西,要么您的方法完全错误。
  3. 您想在不先调用它的情况下检查执行块的结果是否为“空”。这是不可能的。例如,考虑以下块:

    { [nil, "", true].sample }
    

    显然,没有办法提前知道。

  4. 您可以调用该块。然后您可以将结果分配给一个变量并对其进行检查:

    def some_method
      evaluation_result = yield if block_given?
      if evaluation_result.nil? or evaluation_result == ""
        # do something if the block was not given or the result is nil/empty
        puts "Empty block? Seriously?"
      else
        # do something if the block was given and the result is non nil/empty
        puts evaluation_result
      end
    end
    

    现在当你调用some_method:

    some_method { "something" } # => "something"
    some_method { 3 + 5 } # => 8
    some_method { nil } # => "Empty block? Seriously?"
    some_method { "" } # => "Empty block? Seriously?"
    some_method { } # => "Empty block? Seriously?"
    some_method # => "Empty block? Seriously?"
    

编辑: 案例 #3 的一种解决方法可能是创建两个 proc,一个是您想要在块为“空”时执行的操作,另一个是 - 如果不是,则将它们传递到您最终将调用该块的端点。这可能适用也可能不适用,具体取决于您的具体情况。

EDIT2: 另一种解决方法是为您的 proc 实例重新定义 Proc#call 方法。但是,这不适用于yield

def secure(&block)
  insecure_call = block.method(:call)
  block.define_singleton_method(:call) do
    insecure_call_result = insecure_call.call
    if insecure_call_result.nil? or insecure_call_result == ""
      "<b>Bummer! Empty block...</b>"
    else
      insecure_call_result
    end
  end
end

x = proc { }
y = proc { "" }
z = proc { nil }
a = proc { 3 + 5 }
b = proc { "something" }
u = proc { [nil, "", true].sample }
[x, y, z, a, b, u].each { |block| secure &block }

# some method that uses the block
def user(&block)
  "What I got is #{block.call}!"
end


user &x # => "What I got is <b>Bummer! Empty block...</b>!"
user &y # => "What I got is <b>Bummer! Empty block...</b>!"
user &z # => "What I got is <b>Bummer! Empty block...</b>!"
user &a # => "What I got is 8!"
user &b # => "What I got is something!"
user &u # => Different each time

EDIT3:另一种选择,有点作弊,是将给定的 proc 包装在另一个 proc 中。这样,它也适用于yield

def wrap(&block)
  proc do
    internal_proc_call_result = block.call
    if internal_proc_call_result.nil? or internal_proc_call_result == ""
      "<b>Bummer! Empty block...</b>"
    else
      internal_proc_call_result
    end
  end
end

现在使用wrap 的结果,会得到类似于secure 的行为。

【讨论】:

  • 感谢您的详细解释。我的情况是 3。我正在为 rails 开发一个辅助类,并且一个空变量可能会出现在一个块中。 (特别是当块是通过变量动态发送的,而该变量恰好是 nil"")。所以在我的助手中,我必须检查块是否为空,并根据它构建正确的 html 输出。如果我喜欢第 4 种方法(也是@WandMaker 的回答),当我对 evaluation_result 进行分配时,输出结果。但我只想检查那里并在其他地方输出 yield
  • 请不要使用“空变量”或“空块”作为表达式(如果很明显您的意思是#2 情况,后者可能没问题)。如果您确实是指#3,正如我所说的那样-这是不可能的。简单的完整性检查来证明这将是一个随机返回结果的块(如上面的答案)或一个永远不会返回的块(例如 - 无限循环)。正如@Jörg 所说,这被称为停机问题并且无法解决。
  • 我接受这个作为答案,因为这是对问题的最详细和最准确的解释。谢谢。
  • @1.44mb,还有另一种选择。 :)
  • 我明白了,一个包含 10,000 行代码并返回 nil 的块是“空的”。
【解决方案2】:

如果我理解正确,您想静态确定块的运行时值是多少。这是由于停止问题的不可判定性而导致的许多已知的不可能问题之一。

换句话说:它做不到。

不是“不能在 Ruby 中完成”,也不是“很难”,它根本无法完成,句号。它可以(并且已经)在数学上证明它是不可能的。永远。

【讨论】:

  • 感谢 Jörg 的清晰解释。我很满意,但我渴望更多地了解这背后的技术原因(从编程和数学方面)。
  • Wiki 是一个很好的起点。
【解决方案3】:

更新答案 我的last effort 根据 cmets 简化答案..

您可以使用block_given? 检查块是否为空,并且您需要显式检查yield 的输出是否为空,如下所示

def method(&block)

    # Below if condition is to prove that block can be accessed 
    if  block_given? 
        p block
        p block.yield
    end

    b = yield if block_given?
    (b.nil? || b.empty?) ? "Empty block? Seriously?" : b
end


p method {"Hello"} # inline block
result = method do 
      "World" 
    end
p result   
p method # No blocks provided
p method {""} # Block that returns empty string

程序的输出

"Hello"
"World"
"Empty block? Seriously?"
"Empty block? Seriously?"

【讨论】:

  • 这与 OP 说不能解决他的问题的 block_given? 有什么不同? (尽管从问题中不清楚 OP 的问题是什么。)
  • @JörgWMittag 两者似乎都在做同样的事情。我错过了block_given?部分,除了自己是新手。如果我使用 block_given? ? ... 而不是 block ? - 得到相同的输出。可能是我误解了这个问题。 OP 正在询问如何检查产量的输出是否为空白字符串
  • @JörgWMittag 很抱歉我的问题不清楚。我尽量做到具体。
  • @WandMaker 我刚刚检查了这个,b = yield if block_given? 实际上产生了块。所以 yield 在被调用时会产生块(如果没有给出块则抛出错误),即使它被调用以进行赋值也是如此。所以没有帮助。不过谢谢。
  • @1.44mb 我在我的解决方案中提供了 4 个测试用例。你能告诉我你正在运行哪个额外的测试用例,你发现有问题吗?
猜你喜欢
  • 2010-09-20
  • 2017-01-04
  • 1970-01-01
  • 2012-05-02
  • 2011-04-13
  • 2014-06-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多