2015-08-02 8 views
8

Ich rufe eine Methode mit einem Block;Ruby überprüfen, ob Block 0 ist

method do 
    "Hello" 
end 

und die Methode ist definiert als;

def method 
    yield 
end 

und bei der Definition Verfahren; Ich möchte überprüfen, ob der angegebene Block leer ist (nil) oder nicht, weil die Variable in der Methode so enden kann;

method do 
    "" 
end 

So in Definition, ich möchte überprüfen, ob die Ausbeute Block gleich Null ist oder nicht. Mögen;

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

Ich weiß, dass das obige nicht funktioniert. Bu, das will ich erreichen.

Denken Sie auch daran, dass block_given? wird immer "wahr" sein, da der Block auch dann gegeben ist, wenn er oder leere Zeichenfolge ist.

UPDATE: Wie die meisten Kommentare/Antworten sagen, dass die Frage unklar ist; hier ist das Problem durch @ndn vereinfacht:

ich, wenn das Ergebnis überprüfen möchten einen Block ausführen „leer“ ist (null oder „“) ohne es zuerst aufgerufen wird.

+0

Wenn ich Sie richtig verstehe, möchten Sie wissen, ob der vom Block zurückgegebene Wert "Nil" oder "" ist, bevor er nachgibt. Das ist nicht möglich. Wenn das nicht der Fall ist, können Sie einfach das Ergebnis von "yield" einer Variablen zuweisen und "nil?" Und "empty?" Prüfen. – ndn

+0

Ja, Sie haben Recht. –

+0

Ja, wenn ich es wie in @ WandMaker's Antwort zuweisen, gibt es einen Fehler wenn Block nicht gegeben ist. Sie sagen also, es gibt keine Möglichkeit zu lernen, ob der Blockinhalt Null oder "" ist bevor er nachgibt. Ist das eine Quelle oder Diskussion, etc. zu diesem Thema, damit ich mehr lerne? Vielen Dank. –

Antwort

9

Es ist unklar, was Sie fragen, weil ein Block selbst nicht leer sein kann. Daher könnten Sie ein paar verschiedene Dinge meinen:

  1. Ein fehlender Block. Sie können prüfen, ob ein Block

    block_given? 
    
  2. -Block mit leeren Körper gegeben ist (aka {} oder do end).Dies ist nicht unmöglich, erfordert aber fortgeschrittene Voodoo-Rubin-Metaprogrammierungs-Magie. Im Allgemeinen, wenn Sie das suchen, schreiben Sie entweder etwas sehr Interessantes oder Ihr Ansatz ist völlig falsch.
  3. Sie möchten prüfen, ob das Ergebnis der Ausführung eines Blocks "leer" ist, ohne es zuerst aufzurufen. Das ist unmöglich. Betrachten Sie zum Beispiel den folgenden Block:

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

    Offensichtlich gibt es keine Möglichkeit, im Voraus zu wissen.

  4. Sie können den Block aufrufen. Dann können Sie das Ergebnis einer Variablen zuweisen und machen Kontrollen darauf:

    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 
    

    Nun, wenn Sie some_method aufrufen:

    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: Eine Abhilfe für Fall # 3 Macht Sei es, um zwei Procs zu erstellen, einen mit dem, was du machen willst, wenn der Block "leer" ist, und einen - falls nicht, dann gib ihn an den Endpunkt weiter, wo du schließlich den Block aufrufen wirst. Dies kann oder kann nicht anwendbar sein, abhängig von Ihrer genauen Situation.

EDIT2: Eine andere Lösung kann die Proc#call Methode für Ihre proc Instanzen neu zu definieren sein. Allerdings funktioniert das nicht für 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: Eine weitere Alternative, die Art von Betrug ist, ist die gegebene proc in einem anderen proc zu wickeln. Auf diese Weise funktioniert es auch für 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 

nun das Ergebnis der wrap mit und werden Sie ein ähnliches Verhalten wie secure erhalten.

+0

Danke für detaillierte Erklärung. Mein Fall ist 3. Ich entwickle eine Hilfsklasse für Schienen, und es ist möglich, dass eine leere Variable in einem Block kommt. (Vor allem, wenn der Block dynamisch über eine Variable gesendet wird und diese Variable _nil_ oder _ "" _ ist). In meinem Helfer muss ich also prüfen, ob der Block leer ist, und dann die korrekte HTML-Ausgabe erstellen. Wenn ich in der 4. Methode (auch @ WandMaker's Antwort) mag, wird das Yield-Ergebnis ausgegeben, wenn ich die Zuweisung zu __evaluation_result__ mache. Aber ich möchte nur dort nachsehen und das _Yield_ an einem anderen Ort ausgeben. –

+0

Bitte verwenden Sie nicht "leere Variable" oder "leeren Block" als Ausdruck (letzteres könnte in Ordnung sein, wenn es klar ist, dass Sie den # 2 Fall meinen). Wenn Sie tatsächlich # 3 meinen, wie ich schon sagte - es ist nicht möglich. Einfache Plausibilitätsprüfungen, um zu beweisen, dass dies ein Block ist, der ein Ergebnis zufällig zurückgibt (wie in der obigen Antwort) oder eines, das niemals zurückkehrt (zum Beispiel - Endlosschleife). Wie @ Jörg sagte, ist dies als Halteproblem bekannt und unlösbar. – ndn

+0

@ 1.44mb, eine mögliche Problemumgehung hinzugefügt. – ndn

5

AKTUALISIERT Antwort Meine last effort die Antwort vereinfachen auf Kommentare basiert ..

Sie für Block Leere mit block_given? überprüfen und Sie müssen explizit wie für yield Ausgang für Leere überprüfen unter

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 

Ausgabe des Programms

"Hello" 
"World" 
"Empty block? Seriously?" 
"Empty block? Seriously?" 
+1

Wie unterscheidet sich das von 'block_given?' Was das OP sagt, löst sein Problem nicht? (Obwohl es aus der Frage nicht klar ist, was das OP-Problem überhaupt ist.) –

+0

@ JörgWMittag Beide scheinen dasselbe zu tun. Ich habe block_given verpasst? Teil, abgesehen davon, dass ich selbst ein Neuling bin. Wenn ich 'block_given 'verwende? ? ... 'statt' Block? '- gleiche Ausgabe erhalten. Vielleicht habe ich die Frage falsch verstanden. OP fragt, wie man überprüft, ob die Ausgabe von Yield leer war String –

+0

@ JörgWMittag Es tut mir leid, dass meine Frage nicht klar ist. Ich habe versucht, so spezifisch wie möglich zu sein. –

5

Wenn ich richtig verstehe, möchten Sie statisch bestimmen, was der Laufzeitwert eines Blocks ist. Dies ist eines der vielen unmöglichen Probleme, die sich aus der Unentscheidbarkeit des Halteproblems ergeben.

Mit anderen Worten: es kann nicht getan werden.

Nicht "es kann nicht in Ruby getan werden", nicht "es ist schwer", es kann einfach nicht getan werden, Punkt. Und es kann (und wurde) mathematisch bewiesen werden, dass es nicht möglich ist. Je.

+0

Danke Jörg für die klare Erklärung. Ich bin zufrieden, aber ich bin hungrig, um mehr über die technischen Gründe zu erfahren, die dahinter stehen (sowohl aus programmtechnischer als auch aus mathematischer Sicht). –

+0

Das [Wiki] (https://en.wikipedia.org/wiki/Halting_problem) ist ein guter Ausgangspunkt. –

Verwandte Themen