web-dev-qa-db-ja.com

Rubyブロックがnilかどうかを確認する

ブロックを使用してメソッドを呼び出します。

method do
  "Hello"
end

メソッドは次のように定義されます。

def method
  yield
end

methodを定義する場合;メソッド内の変数が次のようになる可能性があるため、指定されたブロックが空(nil)であるかどうかを確認したいと思います。

method do
  ""
end

したがって、定義では、yieldブロックがnilであるかどうかを確認したいと思います。お気に入り;

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

私は上記がうまくいかないことを知っています。 Buそれは私が達成したいことです。

また、block_given?は、nilまたは空の文字列

[〜#〜] update [〜#〜]:コメント/回答のほとんどが質問が不明確であると述べているため、これが@ndnによって単純化された問題です:

ブロックを実行した結果が、最初に呼び出さずに「空」(nilまたは「」)であるかどうかを確認したいと思います。

10
1.44mb

ブロック自体を空にすることはできないため、何を求めているのかが不明確です。したがって、いくつかの異なることを意味する場合があります。

  1. 欠落しているブロック。ブロックが与えられているかどうかを確認できます

    block_given?
    
  2. 空の本文でブロックします(別名{}またはdo end)。これは不可能ではありませんが、高度なブードゥー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?"
    

[〜#〜] edit [〜#〜]:ケース#3の回避策は、2つのprocを作成することです。1つはブロックが「空」の場合に実行する操作で、もう1つは-ifそうでない場合は、最終的にブロックを呼び出すエンドポイントにそれらを渡します。これは、正確な状況に応じて適用される場合と適用されない場合があります。

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

EDIT:ある種の不正行為である別の代替手段は、指定された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と同様の動作が得られます。

16
ndnenkov

更新された回答私の 最後の努力 コメントに基づいて回答を単純化する。

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?"
6
Wand Maker

私が正しく理解していれば、ブロックの実行時の値を静的に決定する必要があります。これは、停止問題の決定不能性に起因する多くの既知の不可能問題の1つです。

言い換えれば、それはできません。

「Rubyではできない」ではなく、「難しい」ではなく、単にできない、期間。そして、それができないことは数学的に証明することができます(そして証明されています)。これまで。

6
Jörg W Mittag