Em geral, quais são as vantagens e desvantagens de usar um OpenStruct em comparação com um Struct? Que tipo de casos de uso geral se encaixaria em cada um deles?
Tenho algumas observações sobre Struct vs. OpenStruct vs. Hash no meu recente comentário no blog "Structs inside out" , apenas no caso de alguém estar interessado.
11119 Robert Klemme
As informações sobre velocidade do Hash, Struct e OpenStruct estão desatualizadas. Consulte stackoverflow.com/a/43987844/128421 para obter uma referência mais recente.
the Tin Man
Respostas:
173
Com um OpenStruct, você pode criar arbitrariamente atributos. UMAStruct , por outro lado, deve ter seus atributos definidos quando você o cria. A escolha de um sobre o outro deve basear-se principalmente em se você precisa adicionar atributos posteriormente.
A maneira de pensar sobre eles é como o meio termo do espectro entre os Hashes de um lado e as classes do outro. Elas implicam uma relação mais concreta entre os dados do que a Hash, mas não possuem os métodos de instância como uma classe. Um monte de opções para uma função, por exemplo, faz sentido em um hash; eles são apenas vagamente relacionados. Um nome, email e número de telefone necessários para uma função podem ser empacotados juntos em um Structou OpenStruct. Se esse nome, email e número de telefone precisassem de métodos para fornecer o nome nos formatos "Primeiro Último" e "Último, Primeiro", você deverá criar uma classe para lidar com isso.
"mas eles não têm os métodos de instância como teriam uma classe". bem, existe um padrão bastante comum para usá-lo como uma "classe normal":class Point < Struct.new(:x, :y); methods here; end
tokland
10
@tokland hoje, a abordagem "preferida" de personalizar a estrutura com métodos é passar o bloco para o construtor Point = Struct.new(:x, :y) { methods here }. ( fonte ) Obviamente, { ... }pode ser escrito como um bloco de várias linhas ( do ... end) e, eu acho, essa é a maneira preferida.
precisa saber é o seguinte
1
@IvanKolmychek: Legal, na verdade eu prefiro a abordagem de blocos.
tokland
@tokland good. Eu só queria esclarecer que agora existe uma abordagem melhor, visto que seu comentário é altamente votado, então, as pessoas novas no ruby podem realmente pensar "OK, então é assim que deve ser feito, porque todos concordam com isso, certo ? " :)
Ivan Kolmychek
4
Uma pergunta: quando você chega no momento em que deseja adicionar métodos à sua estrutura, por que não usar uma classe?
Para o impaciente que deseja ter uma idéia dos resultados do benchmark, sem executá-los, eis a saída do código acima (em um 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)
Informações muito úteis para viciados em desempenho como eu. Obrigado.
Bernardo Oliveira
Estou me referindo a implementação do Matz de Ruby (MRI)
Tilo
1
Olá @Tilo, você poderia compartilhar seu código para obter os resultados acima? Eu quero usá-lo para comparar Struct & OStruct com Hashie :: Mash. Obrigado.
Donny Kurnia
1
Hey @Donny, acabei de ver o voto positivo e percebi que isso foi medido em 2011 - preciso repetir isso com o Ruby 2.1: P não tenho certeza se tenho esse código, mas deve ser simples de reproduzir. Vou tentar consertar isso em breve.
Você pode pensar na classe Struct no Ruby 1.9 como equivalente à structdeclaração em C. No Ruby, Struct.newum conjunto de nomes de campos é argumentos e retorna uma nova classe. Da mesma forma, em C, umstruct declaração utiliza um conjunto de campos e permite que o programador use o novo tipo complexo, como faria com qualquer tipo interno.
Rubi:
Newtype=Struct.new(:data1,:data2)
n =Newtype.new
C:
typedef struct {
int data1;
char data2;} newtype;
newtype n;
A classe OpenStruct pode ser comparada a uma declaração de estrutura anônima em C. Ele permite que o programador crie uma instância de um tipo complexo.
Rubi:
o =OpenStruct.new(data1:0, data2:0)
o.data1 =1
o.data2 =2
Mas você está ciente de que o teste OpenStruct cria muitos hashes temporários. Sugiro uma referência ligeiramente modificada - que ainda suporta seu veredicto (veja abaixo).
Obrigado pelo exemplo. Ajuda muito a entender na prática.
Ahsan
5
Dê uma olhada na API em relação ao novo método. Muitas diferenças podem ser encontradas lá.
Pessoalmente, eu gosto bastante do OpenStruct, pois não preciso definir a estrutura do objeto de antemão e apenas adicionar o que quiser. Eu acho que essa seria sua principal (des) vantagem?
Usando o código @Robert, adiciono Hashie :: Mash ao item de referência e obtive este resultado:
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)
Bem, o resultado varia entre a versão ruby usada e o hardware usado para executá-la. Mas o padrão ainda é o mesmo, o OpenStruct é o mais lento, o Struct é o mais rápido. Hashie cai no meio.
Donny Kurnia
0
Na verdade, não é uma resposta para a pergunta, mas uma consideração muito importante se você se importa com o desempenho . Observe que toda vez que você cria uma OpenStructoperação, o cache do método é limpo, o que significa que seu aplicativo terá um desempenho mais lento. A lentidão ou não não OpenStructé apenas sobre como ela funciona sozinha, mas as implicações que as utilizam trazem para todo o aplicativo: https://github.com/charliesome/charlie.bz/blob/master/posts/things-that -clear-rubys-method-cache.md # openstructs
Respostas:
Com um
OpenStruct
, você pode criar arbitrariamente atributos. UMAStruct
, por outro lado, deve ter seus atributos definidos quando você o cria. A escolha de um sobre o outro deve basear-se principalmente em se você precisa adicionar atributos posteriormente.A maneira de pensar sobre eles é como o meio termo do espectro entre os Hashes de um lado e as classes do outro. Elas implicam uma relação mais concreta entre os dados do que a
Hash
, mas não possuem os métodos de instância como uma classe. Um monte de opções para uma função, por exemplo, faz sentido em um hash; eles são apenas vagamente relacionados. Um nome, email e número de telefone necessários para uma função podem ser empacotados juntos em umStruct
ouOpenStruct
. Se esse nome, email e número de telefone precisassem de métodos para fornecer o nome nos formatos "Primeiro Último" e "Último, Primeiro", você deverá criar uma classe para lidar com isso.fonte
class Point < Struct.new(:x, :y); methods here; end
Point = Struct.new(:x, :y) { methods here }
. ( fonte ) Obviamente,{ ... }
pode ser escrito como um bloco de várias linhas (do ... end
) e, eu acho, essa é a maneira preferida.Outra referência:
Para o impaciente que deseja ter uma idéia dos resultados do benchmark, sem executá-los, eis a saída do código acima (em um MB Pro 2.4GHz i7)
fonte
ATUALIZAR:
A partir do Ruby 2.4.1, OpenStruct e Struct estão muito mais próximos em velocidade. Consulte https://stackoverflow.com/a/43987844/128421
ANTERIORMENTE:
Para completar: Struct vs. Class vs. Hash vs. OpenStruct
Executando código semelhante ao do burtlo, no Ruby 1.9.2, (1 de 4 núcleos x86_64, 8 GB de RAM) [tabela editada para alinhar colunas]:
Os OpenStructs consomem muita memória e muita memória e não se adaptam bem a grandes conjuntos de dados
Criar 1 Mio OpenStructs é ~ 100x mais lento que criar 1 Mio Hashes .
fonte
Os casos de uso para os dois são bem diferentes.
Você pode pensar na classe Struct no Ruby 1.9 como equivalente à
struct
declaração em C. No Ruby,Struct.new
um conjunto de nomes de campos é argumentos e retorna uma nova classe. Da mesma forma, em C, umstruct
declaração utiliza um conjunto de campos e permite que o programador use o novo tipo complexo, como faria com qualquer tipo interno.Rubi:
C:
A classe OpenStruct pode ser comparada a uma declaração de estrutura anônima em C. Ele permite que o programador crie uma instância de um tipo complexo.
Rubi:
C:
Aqui estão alguns casos de uso comuns.
O OpenStructs pode ser usado para converter facilmente hashes em objetos únicos que respondem a todas as chaves de hash.
Estruturas podem ser úteis para definições de classes abreviadas.
fonte
O OpenStructs usa significativamente mais memória e apresenta desempenho mais lento do que o Structs.
No meu sistema, o código a seguir foi executado em 14 segundos e consumiu 1,5 GB de memória. Sua milhagem pode variar:
Isso terminou quase instantaneamente e consumiu 26,6 MB de memória.
fonte
Struct
:OpenStruct
:fonte
Dê uma olhada na API em relação ao novo método. Muitas diferenças podem ser encontradas lá.
Pessoalmente, eu gosto bastante do OpenStruct, pois não preciso definir a estrutura do objeto de antemão e apenas adicionar o que quiser. Eu acho que essa seria sua principal (des) vantagem?
fonte
Usando o código @Robert, adiciono Hashie :: Mash ao item de referência e obtive este resultado:
fonte
Na verdade, não é uma resposta para a pergunta, mas uma consideração muito importante se você se importa com o desempenho . Observe que toda vez que você cria uma
OpenStruct
operação, o cache do método é limpo, o que significa que seu aplicativo terá um desempenho mais lento. A lentidão ou não nãoOpenStruct
é apenas sobre como ela funciona sozinha, mas as implicações que as utilizam trazem para todo o aplicativo: https://github.com/charliesome/charlie.bz/blob/master/posts/things-that -clear-rubys-method-cache.md # openstructsfonte