2015-08-02 14 views
8

Wzywam metodę z blokiem;Ruby sprawdź czy blok jest zerowy

method do 
    "Hello" 
end 

i metoda jest zdefiniowana jako;

def method 
    yield 
end 

i definiując sposób; chcę sprawdzić, czy dany blok jest pusty (zero), czy nie, ponieważ zmienna w metodzie może skończyć się tak;

method do 
    "" 
end 

Tak w definicji, chcę, aby sprawdzić, czy wydajność blok jest zerowa lub nie. Lubić;

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

Wiem, że powyższe nie działa. Bu to jest to, co chcę osiągnąć.

Należy również pamiętać, że block_given? zawsze będzie "prawdziwy", ponieważ blok jest podawany, nawet jeśli jest to zero lub pusty ciąg.

UPDATE: Jak większość komentarzy/odpowiedzi stwierdza, że ​​pytanie jest niejasne; tutaj jest problem uproszczone poprzez @ndn:

Chcę sprawdzić, czy wynik wykonania bloku jest „pusty” (zero lub „”) bez powołując go jako pierwszy.

+0

Jeśli dobrze cię rozumiem, chcesz wiedzieć, czy wartość zwrócona przez blok to 'nil' lub' "" przed wygenerowaniem. To jest niemożliwe. Jeśli tak nie jest, możesz po prostu przypisać wynik 'yield' do zmiennej i wykonać na nim' nil? 'I' empty? 'Check. – ndn

+0

Tak, masz rację. –

+0

Tak, jeśli przypiszę to tak, jak w odpowiedzi @ WandMaker, daje błąd, jeśli blok nie jest podany. Tak więc mówisz, że nie ma możliwości dowiedzenia się, że zawartość bloku jest zerowa lub "" przed pojawieniem się. Czy jest źródło lub dyskusja itp. Na ten temat, aby dowiedzieć się więcej? Dzięki. –

Odpowiedz

9

Nie jest jasne, o co pytasz, ponieważ sam blok nie może być pusty. Dlatego możesz mieć na myśli kilka różnych rzeczy:

  1. Brakujący blok. Można sprawdzić, czy blok jest podana

    block_given? 
    
  2. Blok z pustym korpusie (aka {} lub do end).Nie jest to niemożliwe, ale wymaga zaawansowanej magii metaprogramowania ruby ​​voodoo. Ogólnie rzecz biorąc, jeśli tego właśnie szukasz, piszesz coś bardzo interesującego lub twoje podejście jest całkowicie błędne.
  3. Chcesz sprawdzić, czy wynik wykonania bloku jest "pusty" bez wywoływania go pierwszy. To jest niemożliwe. Rozważmy na przykład następujący blok:

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

    Oczywiście, nie ma sposobu, aby wiedzieć z wyprzedzeniem.

  4. Nie ma problemu z wywołaniem bloku. Następnie można przypisać wynik do zmiennej i przeprowadzać kontrole na nim:

    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 
    

    Teraz kiedy wywołasz 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: obejście przypadku # 3 może należy utworzyć dwa procy, jeden z tym, co chcesz zrobić, jeśli blok jest "pusty", a drugi - jeśli nie jest, a następnie przekazać je do punktu końcowego, w którym ostatecznie powołasz blok. To może, ale nie musi, być stosowane w zależności od Twojej dokładnej sytuacji.

EDIT2: Innym Rozwiązaniem może być przedefiniowanie metody Proc#call do wystąpień proc. Jednak to nie działa dla 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: Inną alternatywą, która jest swego rodzaju oszustwo, jest owinąć daną proc w innym proc. W ten sposób będzie działać również dla 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 

Teraz wykorzystaniem wyniku wrap i będzie Ci zachowanie podobne do secure.

+0

Dzięki za szczegółowe wyjaśnienie. Mój przypadek to 3. Rozwijam klasę pomocniczą dla szyn i możliwe jest, że pusta zmienna pojawi się w bloku. (Zwłaszcza gdy blok jest wysyłany dynamicznie przez zmienną, a ta zmienna ma postać _nil_ lub _ "" _). Więc w moim pomocniku, muszę sprawdzić, czy blok jest pusty, i budować poprawne wyjście html w zależności od tego. Jeśli podoba mi się czwarta metoda (również odpowiedź @ WandMaker), wynik uzyskuje wynik, gdy dokonam przypisania do __evaluation_result__. Ale po prostu chcę to sprawdzić i wypisać _yield_ w innym miejscu. –

+0

Proszę nie używać "pustej zmiennej" lub "pustego bloku" jako wyrażenia (te ostatnie mogą być w porządku, jeśli jasne jest, że chodzi o przypadek # 2). Jeśli rzeczywiście masz na myśli # 3, jak powiedziałem - nie jest to możliwe. Proste sprawdzanie poprawności w celu udowodnienia tego byłoby blokiem, który zwraca wynik losowo (jak w powyższej odpowiedzi) lub taki, który nigdy nie wraca (na przykład - pętla nieskończona). Jak powiedział @ Jörg, jest to znane jako problem zatrzymania i jest nierozwiązywalne. – ndn

+0

@ 1.44mb, dodano możliwe obejście tego problemu. – ndn

5

AKTUALIZACJA Odpowiedź My last effort uprościć odpowiedź w oparciu o komentarze ..

można sprawdzić bloku pustki z block_given? i trzeba jawnie sprawdzić yield wyjścia do pustki jak poniżej

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 

Wyjście z programu

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

Jak to się różni od 'block_given?', O którym mówi PO nie rozwiązuje problemu? (Chociaż nie jest jasne, na czym polega problem PO). –

+0

@ JörgWMittag Obie wydają się robić to samo. Brakowało mi block_given? część, poza tym, że sam jestem początkującym. Jeśli używam 'block_given? ? ... 'zamiast' bloku? '- uzyskać ten sam wynik. Być może źle zrozumiałem to pytanie. OP pyta, jak sprawdzić, czy wyjściowy plon był pusty. –

+0

@ JörgWMittag Przykro mi, że moje pytanie nie jest jasne. Próbowałem być tak konkretny, jak tylko mogłem. –

5

Jeśli dobrze rozumiem, chcesz statycznie określić, jaka jest wartość runtime bloku. Jest to jeden z wielu znanych niemożliwych problemów wynikających z nierozstrzygalności problemu zatrzymania.

Innymi słowy: nie można tego zrobić.

Nie "nie można tego zrobić w Ruby", a nie "trudno", po prostu nie da się tego zrobić, kropka. I może być (i było) matematycznie udowodnione, że nie można tego zrobić. Zawsze.

+0

Dziękuję Jörgowi za jasne wyjaśnienie. Jestem usatysfakcjonowany, ale jestem głodny większej ilości informacji na temat przyczyn technicznych (zarówno z punktu widzenia programowania, jak i aspektów matematycznych). –

+0

[Wiki] (https://en.wikipedia.org/wiki/Halting_problem) jest dobrym punktem wyjścia. –