2015-06-03 14 views
9

Pobiegłem co następuje z powłoki bash:Ruby Command Line niejawny Warunkowe Sprawdź

echo 'hello world' | ruby -ne 'puts $_ if /hello/' 

myślałem, że to literówka w pierwszym, ale wyprowadzane hello world zaskakująco.

Chciałem wpisać:

echo 'hello world' | ruby -ne 'puts $_ if /hello/ === $_' 

Czy ktoś może wyjaśnić, czy punkt do dokumentacji, dlaczego mamy ten niejawny porównania do $_?

Chciałbym również, aby pamiętać:

echo 'hello world' | ruby -ne 'puts $_ if /test/' 

nie będzie wyjściowej cokolwiek.

+1

jeśli/hello/jest truthy jest wszystko, co jest, to nie jest fałszywy i nie zerowe –

+1

@TonyHopkinson to nieprawda, można zmodyfikować regex pasujące do niczego - spowoduje żadnych ouput – Shawn

+0

Czy jak Perl gdzie kiedy użyto '-n'' $ _' staje się niejawnym argumentem? A może '$ _' jest niejawne w wyrażeniu regularnym przez cały czas. –

Odpowiedz

4

Parser rubinowy ma specjalny przypadek do wyrażenia regularnego literałów w wersjach warunkowych. Zwykle (tj.bez korzystania z opcji wiersza poleceń e, n lub p) ten kod:

if /foo/ 
    puts "TRUE!" 
end 

produkuje:

$ ruby regex-in-conditional1.rb 
regex-in-conditional1.rb:1: warning: regex literal in condition 

Przypisywanie coś, co pasuje do wyrażenia regularnego do $_ pierwszy coś takiego:

$_ = 'foo' 
if /foo/ 
    puts "TRUE!" 
end 

produkuje:

$ ruby regex-in-conditional2.rb 
regex-in-conditional2.rb:2: warning: regex literal in condition 
TRUE! 

Jest to (źle udokumentowany) wyjątek od normalnych reguł dla warunków Ruby, gdzie wszystko, co nie jest zgodne z false lub nil, jest traktowane jako prawda.

Dotyczy to tylko regex literałów, następujące zachowuje się jak można się spodziewać na warunkowy:

regex = /foo/ 
if regex 
    puts "TRUE!" 
end 

wyjściowa:

$ ruby regex-in-conditional3.rb 
TRUE! 

To jest obsługiwane przez parser. Przeszukiwanie kod MRI dla tekstu ostrzeżenia produkuje jeden match in parse.y:

case NODE_DREGX: 
case NODE_DREGX_ONCE: 
warning_unless_e_option(parser, node, "regex literal in condition"); 
return NEW_MATCH2(node, NEW_GVAR(rb_intern("$_"))); 

Nie wiem, bizony, więc nie mogę dokładnie wyjaśnić, co tu się dzieje, ale istnieją pewne wskazówki można wydedukować . Funkcja warning_unless_e_option po prostu blokuje ostrzeżenie, jeśli została ustawiona opcja -e, ponieważ ta funkcja jest odradzana w normalnym kodzie, ale może być przydatna w wyrażeniach z wiersza poleceń (to wyjaśnia, dlaczego nie widzisz ostrzeżenia w kodzie). Następna linia wydaje się konstruować poddrzewo parse, które jest dopasowaniem wyrażenia regularnego między zmienną globalną regex a $_, która zawiera "[t]he last input line of string by gets or readline". Węzły te zostaną następnie skompilowane do wywołania metody zwykłego wyrażenia regularnego.

To pokazuje, co się dzieje, po prostu zakończyć cytatem z Kernel#gets documentation które mogą wyjaśnić, dlaczego to jest tak niejasny funkcji może

Styl programowania przy użyciu $ _ jako niejawna parametr jest stopniowo tracąc przysługę w społeczności Ruby.

+0

To jest pełna i poprawna odpowiedź. Ta gramatyka jest rekursywna, więc odnosi się również do 'if/hello/& true' oraz do' if/hello /../ goodbye/'. Naprawdę by mi się spodobało, gdyby było gdzieś udokumentowane, chociaż ... – Amadan

+0

@thanks Matt, twoja odpowiedź doprowadziła mnie do sprecyzowania mojej odpowiedzi. Powiedz mi co myślisz. Dzięki! – Shawn

+0

Fajnie, to jest dobry dzień, nauczyłem się czegoś nowego. Dziękuję Ci –

4

Po przekopaniu się przez źródło Ruby (MRI), myślę, że znalazłem wyjaśnienie.

Kod:

pp RubyVM::InstructionSequence.compile('puts "hello world" if /hello/').to_a 

daje następujący wynik:

...  
    [:trace, 1], 
    [:putobject, /hello/], 
    [:getspecial, 0, 0], 
    [:opt_regexpmatch2], 
    ... 

Instrukcje wydaje się być powołanie opt_regexpmatch2 z dwoma argumentami, pierwszym argumentem jest wyrażenie regularne /hello/ a drugi będąc powrót wartość od getspecial

getspecial można znaleźć w insns.def

/** 
    @c variable 
    @e Get value of special local variable ($~, $_, ..). 
    @j 特殊なローカル変数($~, $_, ...)の値を得る。 
*/ 
DEFINE_INSN 
getspecial 
(rb_num_t key, rb_num_t type) 
() 
(VALUE val) 
{ 
    val = vm_getspecial(th, GET_LEP(), key, type); 
} 

Należy zauważyć, że nasze instrukcje najprawdopodobniej nakazują maszynie VM przywrócenie wartości $_. $_ jest automatycznie ustawiany na nas, kiedy prowadzimy Ruby z odpowiednich opcji, np, -n

Teraz, gdy mamy nasze dwa argumenty, nazywamy opt_regexpmatch2

/** 
    @c optimize 
    @e optimized regexp match 2 
    @j 最適化された正規表現マッチ 2 
*/ 
DEFINE_INSN 
opt_regexpmatch2 
(CALL_INFO ci) 
(VALUE obj2, VALUE obj1) 
(VALUE val) 
{ 
    if (CLASS_OF(obj2) == rb_cString && 
    BASIC_OP_UNREDEFINED_P(BOP_MATCH, STRING_REDEFINED_OP_FLAG)) { 
    val = rb_reg_match(obj1, obj2); 
    } 
    else { 
    PUSH(obj2); 
    PUSH(obj1); 
    CALL_SIMPLE_METHOD(obj2); 
    } 
} 

Pod koniec dnia if /hello/' jest odpowiednik if $_ =~ /hello/ - $_ będzie nil, chyba że uruchomimy ruby z odpowiednimi opcjami.