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?
true and false
vuelve verdadero?Respuestas:
Wikipedia Ruby errores
Del artículo:
$
y@
no indican el tipo de datos variables como en Perl, sino que funcionan como operadores de resolución de alcance.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.0
,""
y[]
son todos evaluados paratrue
. En C, la expresión se0 ? 1 : 0
evalúa como0
(es decir, falsa). En Ruby, sin embargo, cede1
, ya que todos los números se evalúantrue
; solonil
yfalse
evaluar afalse
. 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 especialestrue
yfalse
se pueden usar en una expresión booleana.char
de caracteres). Esto puede causar sorpresas al cortar cadenas:"abc"[0]
rendimientos97
(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 existetrue
. Esto se debe astatement until expression
que el azúcar sintáctico ha terminado, cuyo equivalente en C / C ++ es
while (not(expression)) statement;
comostatement if expression
es un equivalente aSin embargo, la notación
en Ruby, de hecho, ejecutará la declaración una vez incluso si la expresión ya es verdadera.
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,
and
yor
, no siguen las reglas normales de precedencia:and
no se vincula más estrictamente queor
. Ruby también tiene operadores de expresión||
y&&
que funcionan como se esperaba.def
adentrodef
no hace lo que un programador de Python podría esperar:Esto da un error por
x
no estar definido. Necesita utilizar unProc
.Características del idioma
()
, 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 llamadomethod_missing()
.fuente
Los novatos tendrán problemas con los métodos de igualdad :
Estos ejemplos deberían aclarar los primeros 3 métodos:
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:
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:
La declaración del caso se basa en el método === :
es equivalente a esto:
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:
fuente
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_missing
osend
se 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.fuente
El siguiente código me sorprendió. Creo que es un problema peligroso: tanto fácil de encontrar como difícil de depurar.
Esto imprime:
Pero si agrego
comment =
algo antes del bloque ...Entonces obtengo:
Básicamente, cuando una variable solo se define dentro de un bloque, se destruye al final del bloque y luego se restablece
nil
en 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:
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.fuente
a = (b if c)
para obtener el efecto deseado, sin el ternario. Esto se debe a que seb if c
evalúa como nulo sic
es falso.Cuando se llama
super
sin argumentos, el método reemplazado se llama con los mismos argumentos que el método reemplazado.Para llamar realmente
super
sin argumentos, debe decirsuper()
.fuente
B#hello
tienename = 42
antes delsuper
, entonces dice "hola 42".Los bloques y métodos devuelven el valor de la última línea de forma predeterminada. Agregar
puts
declaraciones al final con fines de depuración puede causar efectos secundarios desagradablesfuente
La herencia no juega ningún papel en la determinación de la visibilidad del método en Ruby.
fuente
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:
fuente
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.
fuente
a = b if a.nil?
o@a = b unless defined?(@a)
.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.fuente
Tuve problemas con mixins que contienen métodos de instancia y métodos de clase. Este código podría ayudar a un novato:
Al principio, pensé que podría tener módulos con métodos de instancia y métodos de clase simplemente haciendo esto:
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).
fuente
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) y0...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:
no debería ser capaz de causar errores devastadores en mis programas.
fuente
for (i=0; i<max; i++)
yfor (i=0; i<=max; i++)
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_missing
que realmente es más parecidomessage_not_understood
.es equivalente a
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.
fuente
Si declara un setter (también conocido como mutador) usando
attr_writer
oattr_accessor
(odef foo=
), tenga cuidado de llamarlo desde dentro de la clase. Dado que las variables se declaran implícitamente, el intérprete siempre tiene que resolverfoo = bar
declarando una nueva variable llamada foo, en lugar de llamar al métodoself.foo=(bar)
.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 = 123
oself['value'] = 123
.fuente
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.
fuente
Uno que me sorprendió en el pasado es que la
\n
secuencia 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.fuente
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.
fuente
false
ynil
son falsos. Todos los demás son valores verdaderos.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 ).
Correr:
Pero llámelo con las advertencias habilitadas y podrá ver el motivo:
fuente
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
fuente
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í.
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.
fuente
En relación con la respuesta de monkut, los
to_foo
métodos de Ruby insinúan cuán estricta será la conversión.Los cortos como
to_i
,to_s
dí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:Las funciones explícitas más largas como
to_int
,to_s
significan que el objeto se puede representar de forma nativa como ese tipo de datos. Por ejemplo, laRational
clase representa todos los números racionales, por lo que se puede representar directamente como un entero Fixnum (o Bignum) llamandoto_int
.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.
fuente
Desde In Ruby, ¿por qué no
foo = true unless defined?(foo)
hace la tarea?Porque
foo
se define comonil
cuandodefined?(foo)
se llama.fuente
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?
fuente
Este me hizo enojar una vez:
fuente
1/2
evalúa0
, que no es igual0.5
, independientemente del tipo. Sin embargoRational(1, 2) == 0.5
, y1.0 == 1
.no funciona. Tienes que poner el rango entre paréntesis, como
para que no crea que estás llamando
5.each
. Creo que este es un problema de precedencia, al igual que elx = true and false
gotcha.fuente