2013-02-28 10 views
6

uczę Ruby (2.0) i to właśnie mnie zaskoczyła:

s = "1234" 
s =~ /\d+/ 
$& ==> "1234"  # as expected, $& contains the matched string 
$&.slice!(-2..-1) # should mutate string 
$& ==> "1234"  # what? 
s.slice(-2..-1) 
s ==> "12"   # as expected 

Sposób slice! ma zmutować ciąg. Inne metody mutatora zachowują się w ten sam sposób. Moje pytania: dlaczego nie powoduje to błędu, czego oczekuję, gdy funkcja nie może zrobić tego, co mówi? Czy to gdzieś dokumentuje? Czy istnieje uzasadnienie?

Aktualizacja

Tak, widzę, że $& nie zachowuje się jak zmiennej globalnej. Każde odniesienie do niej daje nowy obiekt, jakby to naprawdę funkcja no-arg:

irb> $foo = "1234" 
=> "1234" 
irb> $foo.object_id 
=> 70205012205980 
irb> $foo.object_id 
=> 70205012205980 # the same 
irb> $&.object_id 
=> 70205003531300 
irb> $&.object_id 
=> 70205011619040 # different object 

... Więc moje pytanie brzmi: czy jest to po prostu „magię” od tłumacza, czy rzeczywiście nie $& Funkcja -arg tak, jak mógłbym zdefiniować w Rubim używając def ... end? I jak mogę odróżnić? W Pythonie mogę odnosić się do funkcji foo używając tylko jego nazwa:

>>> foo 
<function foo at 0x10d3117d0> 

Czy istnieje sposób, aby zrobić to w Ruby? Mógłbym wtedy spojrzeć na to, czym jest "naprawdę" & (jeśli to nie jest magia).

+1

Dlaczego chcesz zmienić '$ &'? Okłamać kolejny kod o tym, co zostało dopasowane? Zauważ, że '$ &' nie ma żadnych "setterów": '$ &. Methods.grep (/ = /) => [: ===,: = ~,: <=>,: ==,:! = ] ' –

+0

Parsuję coś, a metoda' slice! 'Zapewniła wygodny sposób na wybranie napisu, tak jak chciałem, w jednym wierszu. Ale to nie ma znaczenia. Obiekt ma typ String, dlatego powinien odpowiadać na metody String. Ma mutatory (metody, które kończą się!), A jeśli obiekt nie może odpowiedzieć na jego metodę, powinien rzucić wyjątek. –

+1

"Obiekt ma typ String". Obiekt * zwrócony * jest ciągiem. Przypisuj to do czegoś i możesz je połączyć z treścią twojego serca. Zmiana '$ &' byłaby zła. Zmiana tego, co zwraca '$ i', jest w porządku. –

Odpowiedz

5

Interfejs API Ruby C zawiera zmienne zmienne i wirtualne. Od README.EXT:

Możesz zdefiniować zmienne zahaczone. Funkcje akcesora (getter i setter ) są wywoływane przy dostępie do zmienionych haczyków.

void rb_define_hooked_variable(const char *name, VALUE *var, 
      VALUE (*getter)(), void (*setter)()) 

Jeśli trzeba dostarczyć zarówno setter czy getter, po prostu dostarczyć 0 na haku nie trzeba. Jeśli oba haki mają wartość 0, rb_define_hooked_variable() działa tak samo jak rb_define_variable().

prototypy funkcji getter i setter są następujące:

VALUE (*getter)(ID id, VALUE *var); 
void (*setter)(VALUE val, ID id, VALUE *var); 

Ponadto można zdefiniować zmienną globalną Ruby bez odpowiedniego C zmiennej . Wartość zmiennej zostanie ustawiona/otrzymana tylko za pomocą haków.

void rb_define_virtual_variable(const char *name, 
      VALUE (*getter)(), void (*setter)()) 

prototypy funkcji pobierających i ustawiających, są następujące:

VALUE (*getter)(ID id); 
void (*setter)(VALUE val, ID id); 

$& jest przykładem zmiennej wirtualnego, to jest zdefiniowane w re.c, odpowiedni pochłaniacza jest last_match_getter i tam nie jest wyznaczonym seterem.

Tak więc $& jest w pewnym sensie funkcją no-arg, tylko jest zaimplementowana w C. Nie możesz (o ile mi wiadomo) zdefiniować swoich własnych wirtualnych globali jak w czystej Ruby (ty można, jeśli utworzysz własne rozszerzenie C).

2

Ponieważ celem $& jest podanie tego, co zostało dopasowane, byłoby sensowne, że dopasowywanie powinno być jedyną operacją, która może ją zaktualizować.

1

To interesujące pytanie. Odpowiedź Jeconnera i wiele komentarzy na to pytanie mówi, że nie jest to użyteczne, ale to nie odpowiada na pytanie. Niezależnie od tego, czy powinno to być zrobione w praktycznym kodeksie, pytanie OP wciąż pozostaje.

Odpowiedź na pytanie brzmi, że wygląda to jak funkcja dla ciągów tylko do odczytu. Po zastosowaniu operacji destrukcyjnej w łańcuchu tylko do odczytu zwraca on inny obiekt ciągu. Zgadzam się z OP, że to jest dziwne. Powinien raczej wywołać błąd.

Jeśli ktoś nie przedstawi przekonującego wyjaśnienia, sugeruję, abyś zapytał go o rdzeń Ruby i/lub poprosił o funkcję, aby podniosła błąd.

Powiązane problemy