Mam kilka uwag na temat Struct vs. OpenStruct vs. Hash w moim niedawnym komentarzu na blogu „Struktury odwrócone” , na wypadek, gdyby ktoś był zainteresowany.
Robert Klemme,
Informacje dotyczące szybkości Hash, Struct i OpenStruct są nieaktualne. Zobacz najnowszy test porównawczy stackoverflow.com/a/43987844/128421 .
Tin Man
Odpowiedzi:
172
Za pomocą OpenStructmożesz dowolnie tworzyć atrybuty. Z Structdrugiej strony, podczas tworzenia należy zdefiniować jego atrybuty. Wybór jednego z pozostałych powinien opierać się przede wszystkim na tym, czy trzeba będzie później dodać atrybuty.
Sposób myślenia o nich jest środkiem pola między Hashes z jednej strony i klasami z drugiej. Implikują one bardziej konkretny związek między danymi niż a Hash, ale nie mają metod instancji, tak jak klasa. Na przykład wiele opcji dla funkcji ma sens w haszowaniu; są tylko luźno spokrewnione. Imię, adres e-mail i numer telefonu wymagane przez funkcję mogą być spakowane razem w Structlub OpenStruct. Jeśli ta nazwa, adres e-mail i numer telefonu wymagały metod, aby podać nazwę zarówno w formacie „First Last”, jak i „Last, First”, należy utworzyć klasę do obsługi tej nazwy.
„ale nie mają metod instancji, tak jak klasa”. cóż, istnieje dość powszechny wzorzec używania go jako „normalnej klasy”:class Point < Struct.new(:x, :y); methods here; end
tokland
10
@tokland na dzień dzisiejszy „preferowanym” podejściem do dostosowywania struktury metodami jest przekazanie bloku do konstruktora Point = Struct.new(:x, :y) { methods here }. ( źródło ) Oczywiście, { ... }można napisać jako blok wieloliniowy ( do ... end) i myślę, że jest to preferowany sposób.
Ivan Kolmychek
1
@IvanKolmychek: Cool, właściwie wolę podejście blokowe.
tokland
@tokland good. Chciałem tylko wyjaśnić, że teraz jest ładniejsze podejście, ponieważ twój komentarz jest wysoko oceniany, więc ludzie, którzy nie znają Rubiego, mogą naprawdę myśleć „OK, więc tak należy to zrobić, ponieważ wszyscy się z tym zgadzają, prawda ? :)
Ivan Kolmychek
4
Pytanie: kiedy już przyjedziesz, chcesz dodać metody do swojej struktury, dlaczego nie skorzystać z klasy?
Dla niecierpliwych, którzy chcą zorientować się w wynikach testu, bez uruchamiania ich samych, oto wynik powyższego kodu (na MB Pro 2.4GHz i7)
user system total real
OpenStruct slow 4.4300000.2500004.680000(4.683851)OpenStruct fast 4.3800000.2700004.650000(4.649809)Struct slow 0.0900000.0000000.090000(0.094136)Struct fast 0.0800000.0000000.080000(0.078940)
Bardzo przydatne informacje dla uzależnionych od wydajności, takich jak ja. Dzięki.
Bernardo Oliveira,
Mam na myśli implementację Rubza (MRI) przez Matza
Tilo
1
Cześć @Tilo, czy możesz udostępnić swój kod, aby uzyskać powyższe wyniki? Chcę go użyć do porównania Struct & OStruct z Hashie :: Mash. Dzięki.
Donny Kurnia
1
Hej, Donny, właśnie zobaczyłem opinię i zdałem sobie sprawę, że zmierzyłem to w 2011 roku - muszę ponownie uruchomić to w Ruby 2.1: P, nie jestem pewien, czy mam ten kod, ale powinno być łatwe do odtworzenia. Spróbuję to naprawić wkrótce.
Przypadki użycia tych dwóch elementów są zupełnie inne.
Możesz myśleć o klasie Struct w Ruby 1.9 jako ekwiwalencie structdeklaracji w C. W Ruby Struct.newbierze zestaw nazw pól jako argumenty i zwraca nową klasę. Podobnie w C structdeklaracja przyjmuje zestaw pól i pozwala programiście używać nowego typu złożonego, tak jak każdego wbudowanego typu.
Rubin:
Newtype=Struct.new(:data1,:data2)
n =Newtype.new
DO:
typedef struct {
int data1;
char data2;} newtype;
newtype n;
Klasę OpenStruct można porównać do anonimowej deklaracji struktury w C. Pozwala to programiście utworzyć instancję typu złożonego.
Rubin:
o =OpenStruct.new(data1:0, data2:0)
o.data1 =1
o.data2 =2
To świetna odpowiedź na różnicę koncepcyjną między nimi. Dzięki za wskazanie anonimowości OpenStruct, wydaje mi się, że dzięki temu jest to o wiele bardziej jasne.
bryant
Świetne wyjaśnienie!
Jurij Ghensev,
24
OpenStruct zużywa znacznie więcej pamięci i działa wolniej w porównaniu do Struct.
Ale zdajesz sobie sprawę, że test OpenStruct tworzy wiele tymczasowych skrótów. Sugeruję nieco zmodyfikowany test porównawczy - który nadal wspiera twój werdykt (patrz poniżej).
Dzięki za przykład. Bardzo pomaga zrozumieć w praktyce.
Ahsan,
5
Zobacz API w odniesieniu do nowej metody. Można tam znaleźć wiele różnic.
Osobiście bardzo podoba mi się OpenStruct, ponieważ nie muszę wcześniej definiować struktury obiektu i po prostu dodawać rzeczy, jak chcę. Myślę, że to byłaby jego główna (nie) zaleta?
Używając kodu @Robert, dodaję Hashie :: Mash do elementu testu i otrzymałem ten wynik:
user system total real
Hashie::Mash slow 3.6000000.0000003.600000(3.755142)Hashie::Mash fast 3.0000000.0000003.000000(3.318067)OpenStruct slow 11.2000000.01000011.210000(12.095004)OpenStruct fast 10.9000000.00000010.900000(12.669553)Struct slow 0.3700000.0000000.370000(0.470550)Struct fast 0.1400000.0000000.140000(0.145161)
Wynik będzie różny w zależności od używanej wersji Ruby i sprzętu używanego do jej uruchomienia. Ale wzór jest nadal taki sam, OpenStruct jest najwolniejszy, Struct jest najszybszy. Hashie spada na środek.
Donny Kurnia
0
Nie jest to właściwie odpowiedź na pytanie, ale bardzo ważna uwaga, jeśli zależy Ci na wydajności . Zauważ, że za każdym razem, gdy tworzysz OpenStructoperację, pamięć podręczna metod jest czyszczona, co oznacza, że aplikacja będzie działała wolniej. Powolność, czy nie, nie OpenStructpolega tylko na tym, jak to działa sama, ale implikacje, które przynoszą ich zastosowanie dla całej aplikacji: https://github.com/charliesome/charlie.bz/blob/master/posts/things-that -clear-rubys-method-cache.md # openstructs
Odpowiedzi:
Za pomocą
OpenStruct
możesz dowolnie tworzyć atrybuty. ZStruct
drugiej strony, podczas tworzenia należy zdefiniować jego atrybuty. Wybór jednego z pozostałych powinien opierać się przede wszystkim na tym, czy trzeba będzie później dodać atrybuty.Sposób myślenia o nich jest środkiem pola między Hashes z jednej strony i klasami z drugiej. Implikują one bardziej konkretny związek między danymi niż a
Hash
, ale nie mają metod instancji, tak jak klasa. Na przykład wiele opcji dla funkcji ma sens w haszowaniu; są tylko luźno spokrewnione. Imię, adres e-mail i numer telefonu wymagane przez funkcję mogą być spakowane razem wStruct
lubOpenStruct
. Jeśli ta nazwa, adres e-mail i numer telefonu wymagały metod, aby podać nazwę zarówno w formacie „First Last”, jak i „Last, First”, należy utworzyć klasę do obsługi tej nazwy.źródło
class Point < Struct.new(:x, :y); methods here; end
Point = Struct.new(:x, :y) { methods here }
. ( źródło ) Oczywiście,{ ... }
można napisać jako blok wieloliniowy (do ... end
) i myślę, że jest to preferowany sposób.Inne punkty odniesienia:
Dla niecierpliwych, którzy chcą zorientować się w wynikach testu, bez uruchamiania ich samych, oto wynik powyższego kodu (na MB Pro 2.4GHz i7)
źródło
AKTUALIZACJA:
Począwszy od Ruby 2.4.1 OpenStruct i Struct są znacznie szybsze. Zobacz https://stackoverflow.com/a/43987844/128421
POPRZEDNIO:
Dla kompletności: Struct vs. Class vs. Hash vs. OpenStruct
Uruchamianie kodu podobnego do burtlo na Ruby 1.9.2, (1 z 4 rdzeni x86_64, 8 GB RAM) [tabela edytowana w celu wyrównania kolumn]:
OpenStructs są bardzo wolne i wymagają dużej ilości pamięci i nie skalują się dobrze dla dużych zestawów danych
Utworzenie 1 Mio OpenStruct jest ~ 100x wolniejsze niż utworzenie 1 Mio Hashes .
źródło
Przypadki użycia tych dwóch elementów są zupełnie inne.
Możesz myśleć o klasie Struct w Ruby 1.9 jako ekwiwalencie
struct
deklaracji w C. W RubyStruct.new
bierze zestaw nazw pól jako argumenty i zwraca nową klasę. Podobnie w Cstruct
deklaracja przyjmuje zestaw pól i pozwala programiście używać nowego typu złożonego, tak jak każdego wbudowanego typu.Rubin:
DO:
Klasę OpenStruct można porównać do anonimowej deklaracji struktury w C. Pozwala to programiście utworzyć instancję typu złożonego.
Rubin:
DO:
Oto kilka typowych przypadków użycia.
OpenStructs może być używany do łatwej konwersji skrótów na jednorazowe obiekty, które reagują na wszystkie klucze skrótu.
Struktury mogą być przydatne do definicji klas skrótowych.
źródło
OpenStruct zużywa znacznie więcej pamięci i działa wolniej w porównaniu do Struct.
W moim systemie następujący kod został wykonany w ciągu 14 sekund i zużył 1,5 GB pamięci. Twój przebieg może się różnić:
To zakończyło się niemal natychmiast i zużyło 26,6 MB pamięci.
źródło
Struct
:OpenStruct
:źródło
Zobacz API w odniesieniu do nowej metody. Można tam znaleźć wiele różnic.
Osobiście bardzo podoba mi się OpenStruct, ponieważ nie muszę wcześniej definiować struktury obiektu i po prostu dodawać rzeczy, jak chcę. Myślę, że to byłaby jego główna (nie) zaleta?
źródło
Używając kodu @Robert, dodaję Hashie :: Mash do elementu testu i otrzymałem ten wynik:
źródło
Nie jest to właściwie odpowiedź na pytanie, ale bardzo ważna uwaga, jeśli zależy Ci na wydajności . Zauważ, że za każdym razem, gdy tworzysz
OpenStruct
operację, pamięć podręczna metod jest czyszczona, co oznacza, że aplikacja będzie działała wolniej. Powolność, czy nie, nieOpenStruct
polega tylko na tym, jak to działa sama, ale implikacje, które przynoszą ich zastosowanie dla całej aplikacji: https://github.com/charliesome/charlie.bz/blob/master/posts/things-that -clear-rubys-method-cache.md # openstructsźródło