web-dev-qa-db-fra.com

Quand utiliser Struct au lieu de Hash dans Ruby?

Je n'ai pas beaucoup d'expérience en programmation. Mais, pour moi, Struct semble un peu similaire à Hash.

  • Que peut bien faire Struct?
  • Y a-t-il quelque chose que Struct puisse faire, que Hash ne puisse pas faire?

Après la recherche sur Google, le concept de Struct est important en C, mais je ne connais pas grand-chose de C.

65
TK.

Les structures diffèrent de l'utilisation de hashmaps des manières suivantes (en plus de l'apparence du code):

  • Une structure a un ensemble d'attributs fixe, tandis que vous ajoutez de nouvelles clés à un hachage.
  • L'appel d'un attribut qui n'existe pas sur une instance d'une structure provoquera une erreur NoMethodError, tandis que l'obtention de la valeur d'une clé non existante à partir d'un hachage renverra simplement nil.
  • Deux instances de structures différentes ne seront jamais égales, même si les structures ont les mêmes attributs et les mêmes valeurs (c'est-à-dire que Struct.new(:x).new(42) == Struct.new(:x).new(42) est false, alors que Foo = Struct.new(:x); Foo.new(42)==Foo.new(42) est true).
  • La méthode to_a pour les structures renvoie un tableau de valeurs, alors que to_a sur un hachage vous procure un tableau de paires clé-valeur (où "paire" signifie "tableau à deux éléments")
  • Si Foo = Struct.new(:x, :y, :z), vous pouvez utiliser Foo.new(1,2,3) pour créer une instance de Foo sans avoir à épeler les noms d'attribut.

Donc, pour répondre à la question: lorsque vous souhaitez modéliser des objets avec un ensemble d'attributs connu, utilisez structs. Lorsque vous souhaitez modéliser hashmaps d'utilisation arbitraire (par exemple, compter la fréquence à laquelle chaque mot apparaît dans une chaîne ou mapper des pseudonymes en noms complets, etc. ne sont certainement pas des emplois pour une structure, alors que modéliser une personne avec un nom, un âge et une adresse serait un ajustement parfait pour Person = Struct.new(name, age, address)).

En tant que sidenote: les structures C n’ont pratiquement rien à voir avec les structures Ruby, ne vous laissez donc pas troubler.

86
sepp2k

Je sais que la réponse à cette question a été quasiment bonne, mais étonnamment, personne n’a encore évoqué l’une des plus grandes différences et les avantages réels de Struct. Et je suppose que c'est pourquoi quelqu'un demande toujours .

Je comprends les différences, mais quel est l’avantage réel d’utiliser un Struct sur un hachage, quand un hachage peut faire la même chose, et qu’il est plus simple à gérer? On dirait que les structures sont un peu superflues.

Struct est plus rapide.

require 'benchmark'

Benchmark.bm 10 do |bench|
  bench.report "Hash: " do
    50_000_000.times do { name: "John Smith", age: 45 } end
  end

  bench.report "Struct: " do
    klass = Struct.new(:name, :age)
    50_000_000.times do klass.new("John Smith", 45) end
  end

end

# Ruby 2.2.2p95 (2015-04-13 revision 50295) [x64-mingw32].
#                 user     system      total        real
# Hash:       22.340000   0.016000  22.356000 ( 24.260674)
# Struct:     12.979000   0.000000  12.979000 ( 14.095455)

# Ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin11.0]
# 
#                  user     system      total        real
# Hash:       31.980000   0.060000  32.040000 ( 32.039914)
# Struct:     16.880000   0.010000  16.890000 ( 16.886061)
40
Quv

Depuis la Struct documentation:

Une structure est un moyen pratique de regrouper un certain nombre d'attributs, à l'aide de méthodes d'accès, sans avoir à écrire une classe explicite. 

D'autre part, un Hash :

Un hachage est une collection de paires clé-valeur. C'est similaire à un tableau, sauf que l'indexation est faite via des clés arbitraires de n'importe quel type d'objet, pas un index entier. L'ordre dans lequel vous parcourez un hachage avec une clé ou une valeur peut sembler arbitraire et ne sera généralement pas dans l'ordre d'insertion. 

La principale différence est la manière dont vous accédez à vos données.

Ruby-1.9.1-p378 > Point = Struct.new(:x, :y)
 => Point 
Ruby-1.9.1-p378 > p = Point.new(4,5)
 => #<struct Point x=4, y=5> 
Ruby-1.9.1-p378 > p.x
 => 4 
Ruby-1.9.1-p378 > p.y
 => 5 
Ruby-1.9.1-p378 > p = {:x => 4, :y => 5}
 => {:x=>4, :y=>5} 
Ruby-1.9.1-p378 > p.x
NoMethodError: undefined method `x' for {:x=>4, :y=>5}:Hash
    from (irb):7
    from /Users/mr/.rvm/rubies/Ruby-1.9.1-p378/bin/irb:17:in `<main>'
Ruby-1.9.1-p378 > p[:x]
 => 4 
Ruby-1.9.1-p378 > p[:y]
 => 5 

En bref, vous feriez une nouvelle Struct quand vous voulez une classe qui est une structure "plain old data" (éventuellement avec l'intention de l'étendre avec plus de méthodes), et vous utiliseriez un hachage besoin d'un type formel du tout.

10
Mark Rushakoff

Une autre différence principale est que vous pouvez ajouter des méthodes de comportement à une structure. 

 Customer = Struct.new(:name, :address) do

  def greeting; "Hello #{name}!" ; end

end

Customer.new("Dave", "123 Main").greeting  # => "Hello Dave!"
9
Jon

Si vous souhaitez simplement encapsuler les données, un hachage (ou un tableau de hachages) suffit. Si vous envisagez de manipuler les données ou d'interagir avec d'autres données, une structure peut ouvrir certaines possibilités intéressantes:

Point = Struct.new(:x, :y)
point_a = Point.new(0,0)
point_b = Point.new(2,3)

class Point
  def distance_to another_point
    Math.sqrt((self.x - another_point.x)**2 + (self.y - another_point.y)**2)
  end
end

puts point_a.distance_to point_b