web-dev-qa-db-fra.com

obj.nil? vs Obj == nil

Est-il préférable d'utiliser obj.nil? ou obj == nil Et quels sont les avantages des deux?

67
Danish Khan

Est-il préférable d'utiliser obj.nil? ou obj == nil

C'est exactement le meme. Il a exactement les mêmes effets observables de l'extérieur (pfff) *

et quels sont les avantages des deux.

Si vous aimez les micro-optimisations tous Les objets retourneront false au .nil? Message sauf pour l'objet nil lui-même, tandis que l'objet utilisant le == Le message effectuera une minuscule comparaison micro avec l'autre objet pour déterminer s'il s'agit du même objet.

* Voir les commentaires.

46
OscarRyz

Personnellement, je préfère object.nil? Comme cela peut être moins déroutant sur des lignes plus longues; Cependant, j'utilise aussi généralement object.blank? Si je travaille dans Rails Comme cela vérifie également la vue si la variable est vide.

11
Topher Fangio

Syntaxe et style de côté, je voulais voir comment "les mêmes" diverses approches des tests pour NIL étaient. Donc, j'ai écrit quelques points de repère à voir et j'ai jeté diverses formes de tests nul.

Tl; dr - résultats d'abord

Les résultats réels ont montré que l'utilisation de obj comme une vérification nulle est la plus rapide dans tous les cas. obj _ est toujours plus rapide de 30% ou plus que la vérification obj.nil?.

Étonnamment, obj _ Effectue environ 3 à 4 fois plus vite que les variations de obj == nil, Pour laquelle il semble y avoir une pénalité de performance punaise.

Voulez-vous accélérer votre algorithme à forte intensité de performance de 200% à 300%? Convertissez tout obj == null Vérifie à obj. Vous voulez sander la performance de votre code? Utilisez obj == null Partout que vous pouvez. (Je plaisante: ne sandez pas votre code!).

Dans l'analyse finale, utilisez toujours obj. Que les Jives avec le Guide de style rubis règle: Ne faites pas de vérification explicite non-NIL, sauf si vous avez affaire à des valeurs booléennes.

Les conditions de référence

OK, donc ce sont les résultats. Comment se passe-t-il ce point de référence, quels tests ont été effectués et quels sont les détails des résultats?

La NIL vérifie que je suis venu avec:

  • obj
  • obj.nil?
  • ! obj
  • !! Obj
  • obj == nil
  • obj! = nil

J'ai choisi diverses Ruby types à tester au cas où les résultats changés en fonction du type. Ces types étaient fixnum, flotteur, falsiflass, trueclass, chaîne et regex

J'ai utilisé ces conditions de vérification nulle sur chacun des types pour voir s'il y avait une différence entre eux, la performance sage. Pour chaque type, j'ai testé les objets NIV et les objets de valeur non nul (par exemple, 1_000_000, 100_000.0, false, true, "string", et /\w/) Pour voir s'il y a une différence de vérification de NIL sur un objet nul contre un objet qui n'est pas nulle.

Les points de repère

Avec tout cela hors de la route, voici le code de référence:

require 'benchmark'

nil_obj = nil
N = 10_000_000

puts Ruby_DESCRIPTION

[1_000_000, 100_000.0, false, true, "string", /\w/].each do |obj|
  title = "#{obj} (#{obj.class.name})"
  puts "============================================================"
  puts "Running tests for obj = #{title}"

  Benchmark.bm(15, title) do |x|
    implicit_obj_report   = x.report("obj:")            { N.times { obj            } }
    implicit_nil_report   = x.report("nil_obj:")        { N.times { nil_obj        } }
    explicit_obj_report   = x.report("obj.nil?:")       { N.times { obj.nil?       } }
    explicit_nil_report   = x.report("nil_obj.nil?:")   { N.times { nil_obj.nil?   } }
    not_obj_report        = x.report("!obj:")           { N.times { !obj           } }
    not_nil_report        = x.report("!nil_obj:")       { N.times { !nil_obj       } }
    not_not_obj_report    = x.report("!!obj:")          { N.times { !!obj          } }
    not_not_nil_report    = x.report("!!nil_obj:")      { N.times { !!nil_obj      } }
    equals_obj_report     = x.report("obj == nil:")     { N.times { obj == nil     } }
    equals_nil_report     = x.report("nil_obj == nil:") { N.times { nil_obj == nil } }
    not_equals_obj_report = x.report("obj != nil:")     { N.times { obj != nil     } }
    not_equals_nil_report = x.report("nil_obj != nil:") { N.times { nil_obj != nil } }
  end
end

Les résultats

Les résultats étaient intéressants, car les performances de fixnum, de flotteur et de chaîne ont été pratiquement identiques, la falsification et la fausseclass et la trueclass ont été beaucoup plus rapidement rapidement. Des tests ont été effectués sur des versions IRM 1.9.3, 2.0.0, 2.1.5 et 2.2.5 avec des résultats comparatifs très similaires sur les versions. Les résultats de la version IRM 2.2.5 sont présentés ici (et disponibles dans The Gist :

Ruby 2.2.5p319 (2016-04-26 revision 54774) [x86_64-darwin14]
============================================================
Running tests for obj = 1000000 (Fixnum)
                      user     system      total        real
obj:              0.970000   0.000000   0.970000 (  0.987204)
nil_obj:          0.980000   0.010000   0.990000 (  0.980796)
obj.nil?:         1.250000   0.000000   1.250000 (  1.268564)
nil_obj.nil?:     1.280000   0.000000   1.280000 (  1.287800)
!obj:             1.050000   0.000000   1.050000 (  1.064061)
!nil_obj:         1.070000   0.000000   1.070000 (  1.170393)
!!obj:            1.110000   0.000000   1.110000 (  1.122204)
!!nil_obj:        1.120000   0.000000   1.120000 (  1.147679)
obj == nil:       2.110000   0.000000   2.110000 (  2.137807)
nil_obj == nil:   1.150000   0.000000   1.150000 (  1.158301)
obj != nil:       2.980000   0.010000   2.990000 (  3.041131)
nil_obj != nil:   1.170000   0.000000   1.170000 (  1.203015)
============================================================
Running tests for obj = 100000.0 (Float)
                      user     system      total        real
obj:              0.940000   0.000000   0.940000 (  0.947136)
nil_obj:          0.950000   0.000000   0.950000 (  0.986488)
obj.nil?:         1.260000   0.000000   1.260000 (  1.264953)
nil_obj.nil?:     1.280000   0.000000   1.280000 (  1.306817)
!obj:             1.050000   0.000000   1.050000 (  1.058924)
!nil_obj:         1.070000   0.000000   1.070000 (  1.096747)
!!obj:            1.100000   0.000000   1.100000 (  1.105708)
!!nil_obj:        1.120000   0.010000   1.130000 (  1.132248)
obj == nil:       2.140000   0.000000   2.140000 (  2.159595)
nil_obj == nil:   1.130000   0.000000   1.130000 (  1.151257)
obj != nil:       3.010000   0.000000   3.010000 (  3.042263)
nil_obj != nil:   1.170000   0.000000   1.170000 (  1.189145)
============================================================
Running tests for obj = false (FalseClass)
                      user     system      total        real
obj:              0.930000   0.000000   0.930000 (  0.933712)
nil_obj:          0.950000   0.000000   0.950000 (  0.973776)
obj.nil?:         1.250000   0.000000   1.250000 (  1.340943)
nil_obj.nil?:     1.270000   0.010000   1.280000 (  1.282267)
!obj:             1.030000   0.000000   1.030000 (  1.039532)
!nil_obj:         1.060000   0.000000   1.060000 (  1.068765)
!!obj:            1.100000   0.000000   1.100000 (  1.111930)
!!nil_obj:        1.110000   0.000000   1.110000 (  1.115355)
obj == nil:       1.110000   0.000000   1.110000 (  1.121403)
nil_obj == nil:   1.100000   0.000000   1.100000 (  1.114550)
obj != nil:       1.190000   0.000000   1.190000 (  1.207389)
nil_obj != nil:   1.140000   0.000000   1.140000 (  1.181232)
============================================================
Running tests for obj = true (TrueClass)
                      user     system      total        real
obj:              0.960000   0.000000   0.960000 (  0.964583)
nil_obj:          0.970000   0.000000   0.970000 (  0.977366)
obj.nil?:         1.260000   0.000000   1.260000 (  1.265229)
nil_obj.nil?:     1.270000   0.010000   1.280000 (  1.283342)
!obj:             1.040000   0.000000   1.040000 (  1.059689)
!nil_obj:         1.070000   0.000000   1.070000 (  1.068290)
!!obj:            1.120000   0.000000   1.120000 (  1.154803)
!!nil_obj:        1.130000   0.000000   1.130000 (  1.155932)
obj == nil:       1.100000   0.000000   1.100000 (  1.102394)
nil_obj == nil:   1.130000   0.000000   1.130000 (  1.160324)
obj != nil:       1.190000   0.000000   1.190000 (  1.202544)
nil_obj != nil:   1.200000   0.000000   1.200000 (  1.200812)
============================================================
Running tests for obj = string (String)
                      user     system      total        real
obj:              0.940000   0.000000   0.940000 (  0.953357)
nil_obj:          0.960000   0.000000   0.960000 (  0.962029)
obj.nil?:         1.290000   0.010000   1.300000 (  1.306233)
nil_obj.nil?:     1.240000   0.000000   1.240000 (  1.243312)
!obj:             1.030000   0.000000   1.030000 (  1.046630)
!nil_obj:         1.060000   0.000000   1.060000 (  1.123925)
!!obj:            1.130000   0.000000   1.130000 (  1.144168)
!!nil_obj:        1.130000   0.000000   1.130000 (  1.147330)
obj == nil:       2.320000   0.000000   2.320000 (  2.341705)
nil_obj == nil:   1.100000   0.000000   1.100000 (  1.118905)
obj != nil:       3.040000   0.010000   3.050000 (  3.057040)
nil_obj != nil:   1.150000   0.000000   1.150000 (  1.162085)
============================================================
Running tests for obj = (?-mix:\w) (Regexp)
                      user     system      total        real
obj:              0.930000   0.000000   0.930000 (  0.939815)
nil_obj:          0.960000   0.000000   0.960000 (  0.961852)
obj.nil?:         1.270000   0.000000   1.270000 (  1.284321)
nil_obj.nil?:     1.260000   0.000000   1.260000 (  1.275042)
!obj:             1.040000   0.000000   1.040000 (  1.042543)
!nil_obj:         1.040000   0.000000   1.040000 (  1.047280)
!!obj:            1.120000   0.000000   1.120000 (  1.128137)
!!nil_obj:        1.130000   0.000000   1.130000 (  1.138988)
obj == nil:       1.520000   0.010000   1.530000 (  1.529547)
nil_obj == nil:   1.110000   0.000000   1.110000 (  1.125693)
obj != nil:       2.210000   0.000000   2.210000 (  2.226783)
nil_obj != nil:   1.170000   0.000000   1.170000 (  1.169347)
5
Michael Gaskill

Dans de nombreux cas, ni, juste tester la valeur de vérité booléenne

Bien que les deux opérations soient très différentes, je suis sûr qu'ils produiront toujours le même résultat, du moins jusqu'à ce que quelqu'un sur le bord de quelque chose décide de remplacer l'objet #nil? méthode. (On appelle le #nil? méthode héritée d'objet ou de remplacement dans NilClass et une compare contre le nil singleton.)

Je suggérerais que, en cas de doute, vous passez une troisième voie, réellement, et testez simplement la valeur de vérité d'une expression.

Donc, if x et pas if x == nil ou if x.nil?, afin d'avoir ce test DTRT lorsque la valeur d'expression est FALSE. Travailler de cette manière peut également aider à éviter de tenter quelqu'un de définir FalseClass#nil? comme vrai.

4
DigitalRoss

Vous pouvez utiliser le symbole # to_proc sur nil?, tandis que cela ne serait pas pratique sur x == nil.

arr = [1, 2, 3]
arr.any?(&:nil?) # Can be done
arr.any?{|x| x == nil} # More verbose, and I suspect is slower on Ruby 1.9.2
! arr.all? # Check if any values are nil or false
3
Andrew Grimm

Je me trouve pas en utilisant .nil? du tout quand vous pouvez faire:

unless obj
  // do work
end

Il est effectivement plus lent en utilisant .nil? mais pas sensiblement. .nil? est juste une méthode pour vérifier si cet objet est égal à nil, autre que l'attrait visuel et très peu de performances qu'il faut qu'il n'y a pas de différence.

0
Garrett