¿Cuáles son los Ruby Gotchas sobre los que se debe advertir a un novato? [cerrado]

108

Recientemente aprendí el lenguaje de programación Ruby y, en general, es un buen lenguaje. Pero me sorprendió bastante ver que no era tan simple como esperaba. Más precisamente, la "regla de la mínima sorpresa" no me pareció muy respetada (por supuesto, esto es bastante subjetivo). Por ejemplo:

x = true and false
puts x  # displays true!

y el famoso:

puts "zero is true!" if 0  # zero is true!

¿Cuáles son los otros "problemas" sobre los que advertiría a un novato en Ruby?

MiniQuark
fuente
@ frases.insertar (0, p) OK @ frases.insertar (p) NADA pasa @phrases << p # OK
Anno2001
¿Por qué true and falsevuelve verdadero?
Jürgen Paul
3
Porque "x = verdadero y falso" en realidad se interpreta como "(x = verdadero) y falso". Es una cuestión de prioridad del operador: "y" tiene una prioridad menor que "=". La mayoría de los otros lenguajes tienen la prioridad inversa, no sé por qué eligieron este orden en Rails, lo encuentro muy confuso. Si desea el comportamiento "normal", simplemente escriba "x = (verdadero y falso)", entonces x será falso.
MiniQuark
4
Otra solución es usar "&&" y "||" en lugar de "y" y "o": se comportan como se esperaba. Por ejemplo: "x = verdadero && falso" da como resultado que x sea falso.
MiniQuark
“El principio de la menor sorpresa significa principio del mínimo mi sorpresa.” de en.wikipedia.org/wiki/Ruby_(programming_language)#Philosophy Lo mismo ocurre con Python. Tenía una cita similar sobre el creador de Python pero olvido dónde estaba.
Darek Nędza

Respuestas:

59

Wikipedia Ruby errores

Del artículo:

  • Los nombres que comienzan con una letra mayúscula se tratan como constantes, por lo que las variables locales deben comenzar con una letra minúscula.
  • Los caracteres $y @no indican el tipo de datos variables como en Perl, sino que funcionan como operadores de resolución de alcance.
  • Para denotar números de coma flotante, se debe seguir con un dígito cero ( 99.0) o una conversión explícita ( 99.to_f). No es suficiente agregar un punto ( 99.), porque los números son susceptibles a la sintaxis del método.
  • Evaluación booleano de datos no booleanas es estricta: 0, ""y []son todos evaluados para true. En C, la expresión se 0 ? 1 : 0evalúa como 0(es decir, falsa). En Ruby, sin embargo, cede 1, ya que todos los números se evalúan true; solo nily falseevaluar a false. Un corolario de esta regla es que los métodos de Ruby por convención, por ejemplo, búsquedas de expresiones regulares, devuelven números, cadenas, listas u otros valores que no sean falsos en caso de éxito, peronil en caso de error (por ejemplo, desajuste). Esta convención también se usa en Smalltalk, donde solo los objetos especiales truey falsese pueden usar en una expresión booleana.
  • Las versiones anteriores a la 1.9 carecen de un tipo de datos de caracteres (compárese con C, que proporciona el tipo charde caracteres). Esto puede causar sorpresas al cortar cadenas: "abc"[0]rendimientos 97(un número entero, que representa el código ASCII del primer carácter de la cadena); para obtener "a"uso"abc"[0,1] (una subcadena de longitud 1) o "abc"[0].chr.
  • La notación statement until expression, a diferencia de las declaraciones equivalentes de otros lenguajes (por ejemplo, do { statement } while (not(expression));en C / C ++ / ...), en realidad nunca ejecuta la declaración si la expresión ya existe true. Esto se debe a statement until expressionque el azúcar sintáctico ha terminado

    until expression
      statement
    end

    , cuyo equivalente en C / C ++ es while (not(expression)) statement;comostatement if expression es un equivalente a

    if expression
      statement
    end

    Sin embargo, la notación

    begin
      statement
    end until expression

    en Ruby, de hecho, ejecutará la declaración una vez incluso si la expresión ya es verdadera.

  • Dado que las constantes son referencias a objetos, cambiar a qué se refiere una constante genera una advertencia, pero la modificación del objeto en sí no. Por ejemplo, Greeting << " world!" if Greeting == "Hello"no genera ningún error ni advertencia. Esto es similar afinal variables en Java, pero Ruby también tiene la funcionalidad de "congelar" un objeto, a diferencia de Java.

Algunas características que se diferencian notablemente de otros idiomas:

  • Los operadores habituales para expresiones condicionales, andy or, no siguen las reglas normales de precedencia: andno se vincula más estrictamente que or. Ruby también tiene operadores de expresión ||y &&que funcionan como se esperaba.

  • defadentro defno hace lo que un programador de Python podría esperar:

    def a_method
        x = 7
        def print_x; puts x end
        print_x
    end

    Esto da un error por xno estar definido. Necesita utilizar un Proc.

Características del idioma

  • La omisión de paréntesis alrededor de los argumentos del método puede generar resultados inesperados si los métodos toman varios parámetros. Los desarrolladores de Ruby han declarado que la omisión de paréntesis en los métodos de múltiples parámetros puede no estar permitida en futuras versiones de Ruby; el intérprete Ruby actual (noviembre de 2007) lanza una advertencia que anima al escritor a no omitir (), para evitar el significado ambiguo del código. No usarlo ()sigue siendo una práctica común, y puede ser especialmente agradable usar Ruby como un lenguaje de programación específico de dominio legible por humanos, junto con el método llamado method_missing().
Andy
fuente
1
Ruby 1.9 también carece de tipo de datos de caracteres. En 1.8, el operador de índice devolvió un Fixnum; en 1.9, equivale a cortar una cadena de un carácter.
whitequark
38

Los novatos tendrán problemas con los métodos de igualdad :

  • a == b : comprueba si a y b son iguales. Este es el más útil.
  • a.eql? b : también comprueba si ayb son iguales, pero a veces es más estricto (podría comprobar que ayb tienen el mismo tipo, por ejemplo). Se utiliza principalmente en Hashes.
  • a.equal? b : comprueba si ayb son el mismo objeto (comprobación de identidad).
  • a === b : se usa en declaraciones de casos (lo leo como " a coincide con b ").

Estos ejemplos deberían aclarar los primeros 3 métodos:

a = b = "joe"

a==b       # true
a.eql? b   # true
a.equal? b # true (a.object_id == b.object_id)

a = "joe"
b = "joe"

a==b       # true
a.eql? b   # true
a.equal? b # false (a.object_id != b.object_id)

a = 1
b = 1.0

a==b       # true
a.eql? b   # false (a.class != b.class)
a.equal? b # false

Tenga en cuenta que == , eql? e igual? siempre debe ser simétrico: si a == b entonces b == a.

También tenga en cuenta que == y eql? ¿Están ambos implementados en la clase Object como alias para ser iguales? , entonces si crea una nueva clase y quiere == y eql? para significar algo más que una simple identidad, debe anularlos a ambos. Por ejemplo:

class Person
    attr_reader name
    def == (rhs)
      rhs.name == self.name  # compare person by their name
    end
    def eql? (rhs)
      self == rhs
    end
    # never override the equal? method!
end

El método === se comporta de manera diferente. Primero de todo, es no simétrica (una === b no no implica que b === a). Como dije, puede leer a === b como "a coincide con b". Aquí están algunos ejemplos:

# === is usually simply an alias for ==
"joe" === "joe"  # true
"joe" === "bob"  # false

# but ranges match any value they include
(1..10) === 5        # true
(1..10) === 19       # false
(1..10) === (1..10)  # false (the range does not include itself)

# arrays just match equal arrays, but they do not match included values!
[1,2,3] === [1,2,3] # true
[1,2,3] === 2       # false

# classes match their instances and instances of derived classes
String === "joe"   # true
String === 1.5     # false (1.5 is not a String)
String === String  # false (the String class is not itself a String)

La declaración del caso se basa en el método === :

case a
  when "joe": puts "1"
  when 1.0  : puts "2"
  when (1..10), (15..20): puts "3"
  else puts "4"
end

es equivalente a esto:

if "joe" === a
  puts "1"
elsif 1.0 === a
  puts "2"
elsif (1..10) === a || (15..20) === a
  puts "3"
else
  puts "4"
end

Si define una nueva clase cuyas instancias representan algún tipo de contenedor o rango (si tiene algo como un método include? O match? ), Entonces puede resultarle útil anular el método === de esta manera:

class Subnet
  [...]
  def include? (ip_address_or_subnet)
    [...]
  end
  def === (rhs)
    self.include? rhs
  end
end

case destination_ip
  when white_listed_subnet: puts "the ip belongs to the white-listed subnet"
  when black_listed_subnet: puts "the ip belongs to the black-listed subnet"
  [...]
end
2 revoluciones
fuente
1
Además: a = 'строка'; b = 'строка'; pa == b; a = a.force_encoding 'ASCII-8BIT'; b = b.force_encoding 'UTF-8'; pa == b; pa === b; p a.eql? si; p a.equal? b
Nakilon
20
  • Parche de mono . Ruby tiene clases abiertas, por lo que su comportamiento se puede cambiar dinámicamente en tiempo de ejecución ...

  • Los objetos pueden responder a métodos no definidos si method_missingo sendse han anulado. Esto explota la invocación del método basado en mensajes de Ruby. El sistema ActiveRecord de Rails utiliza esto con gran efecto.

Dan Vinton
fuente
18

El siguiente código me sorprendió. Creo que es un problema peligroso: tanto fácil de encontrar como difícil de depurar.

(1..5).each do |number|
  comment = " is even" if number%2==0
  puts number.to_s + comment.to_s
end

Esto imprime:

1
2 is even
3
4 is even
5

Pero si agrego comment =algo antes del bloque ...

comment = nil
(1..5).each do |number|
  comment = " is even" if number%2==0
  puts number.to_s + comment.to_s
end

Entonces obtengo:

1
2 is even
3 is even
4 is even
5 is even

Básicamente, cuando una variable solo se define dentro de un bloque, se destruye al final del bloque y luego se restablece nilen cada iteración. Eso es normalmente lo que esperas. Pero si la variable es define antes del bloque, entonces la variable externa se usa dentro del bloque y, por lo tanto, su valor es persistente entre iteraciones.

Una solución sería escribir esto en su lugar:

comment = number%2==0 ? " is even" : nil

Creo que mucha gente (incluyéndome a mí) tiende a escribir " a = b if c" en lugar de " a = (c ? b : nil)", porque es más legible, pero obviamente tiene efectos secundarios.

MiniQuark
fuente
4
También puede sombrear la variable de alcance externo con (1..5) do | number; comment | ..... Lea aquí stackoverflow.com/questions/1654637/…
Özgür
6
Esto me parece lógico. Este alcance es típico de otros lenguajes, es solo la sintaxis lo que es diferente.
g.
Sin embargo, puede escribir a = (b if c)para obtener el efecto deseado, sin el ternario. Esto se debe a que se b if cevalúa como nulo si ces falso.
Cameron Martin
16

Cuando se llama supersin argumentos, el método reemplazado se llama con los mismos argumentos que el método reemplazado.

class A
  def hello(name="Dan")
    puts "hello #{name}"
  end
end

class B < A
  def hello(name)
    super
  end
end

B.new.hello("Bob") #=> "hello Bob"

Para llamar realmente supersin argumentos, debe decir super().

Daniel Lucraft
fuente
3
Si B#hellotiene name = 42antes del super, entonces dice "hola 42".
Andrew Grimm
14

Los bloques y métodos devuelven el valor de la última línea de forma predeterminada. Agregar putsdeclaraciones al final con fines de depuración puede causar efectos secundarios desagradables

Andrew Grimm
fuente
11

Tuve muchos problemas para comprender las variables de clase, los atributos de clase y los métodos de clase. Este código podría ayudar a un novato:

class A
  @@classvar = "A1"
  @classattr = "A2"
  def self.showvars
    puts "@@classvar => "+@@classvar
    puts "@classattr => "+@classattr
  end
end

A.showvars
  # displays:
  # @@classvar => A1
  # @classattr => A2

class B < A
  @@classvar = "B1"
  @classattr = "B2"
end

B.showvars
  # displays:
  # @@classvar => B1
  # @classattr => B2

A.showvars
  # displays:
  # @@classvar => B1   #Class variables are shared in a class hierarchy!
  # @classattr => A2   #Class attributes are not
MiniQuark
fuente
1
Sí, las variables de clase pueden ser complicadas. Creo que los rubyistas más experimentados dirían que es aconsejable evitarlos, ya que generalmente hay otras formas de resolver un problema sin ellos. Algunos entusiastas del idioma incluso dirían que las variables de clase de Ruby están mal diseñadas a nivel de idioma.
David J.
8

una cosa que aprendí fue a usar el operador || = con cuidado. y tenga especial cuidado si se trata de valores booleanos. Usualmente usé a || = b como un catch all para dar a 'a' un valor predeterminado si todo lo demás fallaba y 'a' permanecía nulo. pero si a es falso y b es verdadero, entonces a se le asignará verdadero.

karina
fuente
Puede usar a = b if a.nil?o @a = b unless defined?(@a).
Andrew Grimm
8
  • Los bloques son realmente importantes de entender, se usan en todas partes.

  • No necesita paréntesis alrededor de los parámetros del método. Si los usa o no, depende de usted. Algunos dicen que siempre debes usarlos .

  • Utilice levantar y rescatar para el manejo de excepciones, no lanzar y atrapar.

  • Puede usar, ;pero no es necesario, a menos que desee poner varias cosas en una línea.

dylanfm
fuente
Si no planea ir más allá de Ruby 1.8.6, ignore los parens tanto como desee. De lo contrario, probablemente sea mejor usarlos.
Mike Woodhouse
7

Tuve problemas con mixins que contienen métodos de instancia y métodos de clase. Este código podría ayudar a un novato:

module Displayable
  # instance methods here
  def display
    puts name
    self.class.increment_displays
  end
  def self.included(base)
    # This module method will be called automatically
    # after this module is included in a class.
    # We want to add the class methods to the class.
    base.extend Displayable::ClassMethods
  end
  module ClassMethods
    # class methods here
    def number_of_displays
      @number_of_displays # this is a class attribute
    end
    def increment_displays
      @number_of_displays += 1
    end
    def init_displays
      @number_of_displays = 0
    end
    # this module method will be called automatically
    # after this module is extended by a class.
    # We want to perform some initialization on a
    # class attribute.
    def self.extended(base)
      base.init_displays
    end
  end
end

class Person
  include Displayable
  def name; @name; end
  def initialize(name); @name=name; end
end

puts Person.number_of_displays # => 0
john = Person.new "John"
john.display # => John
puts Person.number_of_displays # => 1
jack = Person.new "Jack"
jack.display # => Jack
puts Person.number_of_displays # => 2

Al principio, pensé que podría tener módulos con métodos de instancia y métodos de clase simplemente haciendo esto:

module Displayable
  def display
    puts name
    self.class.increment_displays
  end
  def self.number_of_displays  # WRONG!
    @number_of_displays
  end
  [...]
end

Desafortunadamente, el método number_of_displays nunca se incluirá ni ampliará porque es un "método de clase de módulo". Sólo los "métodos de instancia de módulo" pueden incluirse en una clase (como métodos de instancia) o ampliarse a una clase (como métodos de clase). Es por eso que necesita poner los métodos de instancia de su mixin en un módulo, y los métodos de clase de su mixin en otro módulo (normalmente coloca los métodos de clase en un submódulo "ClassMethods"). Gracias al método mágico incluido , puede facilitar la inclusión de métodos de instancia y métodos de clase en una simple llamada "include Displayable" (como se muestra en el ejemplo anterior).

Este mixin contará cada pantalla por clase . El contador es un atributo de clase, por lo que cada clase tendrá el suyo (su programa probablemente fallará si deriva una nueva clase de la clase Person, ya que el contador @number_of_displays para la clase derivada nunca se inicializará). Es posible que desee reemplazar @number_of_displays por @@ number_of_displays para convertirlo en un contador global. En este caso, cada jerarquía de clases tendrá su propio contador. Si desea un contador global y único, probablemente debería convertirlo en un atributo de módulo.

Todo esto definitivamente no fue intuitivo para mí cuando comencé con Ruby.

Sin embargo, todavía no puedo entender cómo hacer que algunos de estos métodos mixin sean privados o protegidos (solo el método display y number_of_displays deben incluirse como métodos públicos).

2 revoluciones
fuente
7

Preste atención a la notación de rango.

(¡Al menos, preste más atención de la que hice inicialmente!)

Hay una diferencia entre 0..10 (dos puntos) y 0...10(tres puntos).

Disfruto mucho de Ruby. Pero esta cosa de punto-punto versus punto-punto-punto me molesta. Creo que una "característica" de doble sintaxis tan sutil es:

  • fácil de escribir mal, y
  • fácil de perder con los ojos mientras mira el código

no debería ser capaz de causar errores devastadores en mis programas.

que que
fuente
1
No muy diferente de for (i=0; i<max; i++)yfor (i=0; i<=max; i++)
g.
He estado tratando de averiguar cuál es la diferencia entre 0..10 y 0 ... 10.
Luis D Urraca
6

Creo que " and" y " or" son un guiño a Perl, que es uno de los "padres" más obvios de Ruby (el otro más destacado es Smalltalk). Ambos tienen una precedencia mucho menor (menor que la asignación, de hecho, que es de donde proviene el comportamiento señalado) que &&y ||cuáles son los operadores que debería utilizar.

Otras cosas a tener en cuenta que no son obvias de inmediato:

Realmente no llamas a métodos / funciones, aunque parece de esa manera. En cambio, como en Smalltalk, envías un mensaje a un objeto. Así method_missingque realmente es más parecido message_not_understood.

some_object.do_something(args)

es equivalente a

some_object.send(:do_something, args) # note the :

Los símbolos son muy utilizados. Esas son las cosas que comienzan con :y no son inmediatamente obvias (bueno, no lo eran para mí), pero cuanto antes te familiarices con ellas, mejor.

Ruby es muy aficionado a la "tipificación de pato", siguiendo el principio de que "si camina como un pato y grazna como un pato ..." que permite la sustitución informal de objetos con un subconjunto común de métodos sin ninguna herencia explícita o relación mixta.

Mike Woodhouse
fuente
Gracias. Hay una cosa que odio del método de envío : ¡te permite llamar a métodos privados incluso fuera de la clase! Ay.
MiniQuark
1
@MiniQuark: ¡eso es lo que me encanta del método de envío!
Andrew Grimm
6

Si declara un setter (también conocido como mutador) usando attr_writero attr_accessor(o def foo=), tenga cuidado de llamarlo desde dentro de la clase. Dado que las variables se declaran implícitamente, el intérprete siempre tiene que resolver foo = bardeclarando una nueva variable llamada foo, en lugar de llamar al método self.foo=(bar).

class Thing
  attr_accessor :foo
  def initialize
    @foo = 1      # this sets @foo to 1
    self.foo = 2  # this sets @foo to 2
    foo = 3       # this does *not* set @foo
  end
end

puts Thing.new.foo #=> 2

Esto también se aplica a los objetos Rails ActiveRecord, que obtienen los accesos definidos en función de los campos de la base de datos. Dado que ni siquiera son variables de instancia de estilo @, la forma correcta de establecer esos valores individualmente es con self.value = 123o self['value'] = 123.

AlexChaffee
fuente
5

Comprender la diferencia entre la clase Hora y Fecha. Ambos son diferentes y han creado problemas al usarlos en rieles. La clase Time a veces entra en conflicto con otras bibliotecas de clases Time presentes en la biblioteca estándar ruby ​​/ rails. Personalmente, me tomó mucho tiempo comprender qué estaba sucediendo exactamente en mi aplicación rails. Más tarde, pensé que cuando lo hice

Time.new

Se refería a alguna biblioteca en un lugar que ni siquiera conocía.

Lo siento si no tengo claro lo que quiero decir exactamente. Si otros han enfrentado problemas similares, vuelva a explicar.

Chirantan
fuente
4

Uno que me sorprendió en el pasado es que la \nsecuencia de escape del carácter de nueva línea ( ), entre otras, no es compatible con cadenas entre comillas simples. La barra invertida en sí se escapa. Debe usar comillas dobles para que el escape funcione como se esperaba.

John Topley
fuente
1
¿Y eso es diferente de qué otro idioma?
Robert Gamble
Java, por ejemplo. Las comillas simples en Java solo se pueden usar para encerrar un solo carácter, no cadenas.
John Topley
1
Esto está de acuerdo con cualquier lenguaje que le permita usar comillas simples para cadenas, y es por eso que lo hacen.
singpolyma
@John: cierto, pero '\ n' en Java seguirá siendo el carácter de nueva línea.
Jorn
1
Pero en Java, las comillas simples solo crean valores de tipo char. No cadenas. Esa es la diferencia.
jmucchiello
4
x = (true and false) # x is false

0 y '' son verdaderas, como señaló.

Puede tener un método y un módulo / clase con el mismo nombre (lo que tiene sentido, porque el método en realidad se agrega a Object y, por lo tanto, tiene su propio espacio de nombres).

No hay herencia múltiple, pero con frecuencia se utilizan "módulos mixin" para agregar métodos comunes a múltiples clases.

singpolyma
fuente
0 == true // argh, ¡el compilador de c en mi cerebro está explotando!
Kenny
1
0 == verdadero da falso en Ruby. Que 0 es verdadero tiene sentido porque verdadero es un objeto en Ruby. En C 0 simplemente tiene la misma representación que falso.
Jules
En una condición en Ruby, solo falsey nilson falsos. Todos los demás son valores verdaderos.
rubyprince
4

Los métodos pueden redefinirse y convertirse en un rasguño mental hasta que descubra la causa. ( Es cierto que este error es probablemente un poco "más difícil" de detectar cuando la acción de un controlador Ruby on Rails se redefine por error ).

#demo.rb
class Demo

  def hello1
    p "Hello from first definition"
  end

  # ...lots of code here...
  # and you forget that you have already defined hello1

  def hello1
    p "Hello from second definition"
  end

end
Demo.new.hello1

Correr:

$ ruby demo.rb
=> "Hello from second definition"

Pero llámelo con las advertencias habilitadas y podrá ver el motivo:

$ ruby -w demo.rb
demo.rb:10: warning: method redefined; discarding old hello1
=> "Hello from second definition"
Zabba
fuente
Yo haría +100 el uso de advertencias si pudiera.
Andrew Grimm
3

Creo que siempre es bueno usar .length en las cosas ... dado que el tamaño es compatible con casi todo y Ruby tiene tipos dinámicos, puedes obtener resultados realmente extraños llamando .size cuando tienes el tipo incorrecto ... preferiría obtener un NoMethodError: método indefinido `length ', por lo que generalmente nunca llamo al tamaño de los objetos en Ruby.

me mordió más de una vez.

También recuerde que los objetos tienen identificadores, así que trato de no usar variables call id o object_id solo para evitar confusiones. Si necesito una identificación en un objeto de usuarios, es mejor llamarlo algo como user_id.

Solo mis dos centavos

danmayer
fuente
2

Soy nuevo en ruby, y en mi primera ronda encontré un problema con el cambio de flotadores / cadenas a un número entero. Comencé con los flotadores y codifiqué todo como f.to_int . Pero cuando continué y usé el mismo método para las cadenas, se me hizo una curva cuando se trataba de ejecutar el programa.

Aparentemente, una cadena no tiene un método to_int , pero los flotadores y los ints sí.

irb(main):003:0* str_val = '5.0'
=> "5.0"
irb(main):006:0> str_val.to_int
NoMethodError: undefined method `to_int' for "5.0":String
        from (irb):6
irb(main):005:0* str_val.to_i
=> 5


irb(main):007:0> float_val = 5.0
=> 5.0
irb(main):008:0> float_val.to_int
=> 5
irb(main):009:0> float_val.to_i
=> 5
irb(main):010:0>

El paréntesis arbitrario también me confundió al principio. Vi un código con y otro sin. Me tomó un tiempo darme cuenta de que ambos estilos son aceptados.

monkut
fuente
2

En relación con la respuesta de monkut, los to_foométodos de Ruby insinúan cuán estricta será la conversión.

Los cortos como to_i, to_sdígale que sea perezoso y conviértalos al tipo de destino incluso si no se pueden representar con precisión en ese formato. Por ejemplo:

"10".to_i == 10
:foo.to_s == "foo"

Las funciones explícitas más largas como to_int, to_ssignifican que el objeto se puede representar de forma nativa como ese tipo de datos. Por ejemplo, la Rationalclase representa todos los números racionales, por lo que se puede representar directamente como un entero Fixnum (o Bignum) llamando to_int.

Rational(20,4).to_int == 5

Si no puede llamar al método más largo, significa que el objeto no se puede representar de forma nativa en ese tipo.

Básicamente, al convertir, si eres vago con los nombres de los métodos, Ruby será vago con la conversión.

Luke
fuente
1
¿Es "perezoso" la palabra correcta aquí?
Andrew Grimm
1

No se garantiza que la iteración sobre hashes de ruby ​​ocurra en ningún orden en particular. (No es un error, es una característica)

Hash#sort es útil si necesita un pedido en particular.

Pregunta relacionada: ¿Por qué la matriz de Ruby de 1000 pares de claves y valores de hash está siempre en un orden particular?

Andrew Grimm
fuente
4
esto no es válido a partir de 1.9: "En Ruby 1.9, sin embargo, los elementos hash se iteran en su orden de inserción" del lenguaje de programación Ruby
Özgür
0

Este me hizo enojar una vez:

1/2 == 0.5 #=> false
1/2 == 0   #=> true
Andrei
fuente
Creo que esto se comportaría exactamente de la misma manera en Java, C y C ++.
Larry
Es gracioso, ni siquiera lo pensé, pero si abres irb y pruebas esto, tiene sentido: entonces (1/2) es un Fixnum y (0.5) es un Float. Y sabemos que Fixnim! = Float.
DemitryT
2
@DemitryT Creo que la razón más simple es que 1/2evalúa 0, que no es igual 0.5, independientemente del tipo. Sin embargo Rational(1, 2) == 0.5, y 1.0 == 1.
Max Nanasy
hipo del lenguaje universal aquí. esto es algo que alguien nuevo en ruby ​​Y programación debería saber.
dtc
0
1..5.each {|x| puts x}

no funciona. Tienes que poner el rango entre paréntesis, como

(1..5).each {|x| puts x}

para que no crea que estás llamando 5.each. Creo que este es un problema de precedencia, al igual que el x = true and falsegotcha.

Andrew Grimm
fuente
En su lugar, lo llamaría paréntesis. En segundo lugar, si algún código parece tener un valor de retorno / problema de precedencia, debería estar entre paréntesis de todos modos. Entonces, para mí, no hay nada especial en este "gotcha". Puede seguir escribiendo todas las "trampas" combinacionales, eso sería una pérdida de tiempo. Francamente amigo, incluso si tuvieras el resultado esperado en esto, todavía preferiría rodearlo entre paréntesis.
Özgür