¿Cuáles son algunos trucos geniales de Ruby?

1) Considere ‘imprimir’ sobre ‘pone’ para fines de rendimiento:

Considere el siguiente código:

require 'benchmark' n = 50_000 Benchmark.bmbm(15) do |r| r.report("original version 2 calls") do for i in 1..n do $stderr.print i, "and" $stderr.puts i end end r.report("single puts call") do for i in 1..n do $stderr.puts "#{i} and #{i}" end end r.report("single print call") do for i in 1..n do $stderr.print "#{i} and #{i}\n" end end end 

Cuando ejecuto este script, encontré los siguientes resultados de la evaluación comparativa:

  akash $ ruby ​​puts_print.rb 2> / dev / null
 Ensayo ------------------------------------------------- -----------
 la versión original 2 llama 0.370000 0.260000 0.630000 (0.639098)
 Llamadas simples 0.250000 0.140000 0.390000 (0.397769)
 llamada de impresión única 0.170000 0.070000 0.240000 (0.256804)
 -------------------------------------------------- - total: 1.260000seg

                              sistema de usuario total real
 la versión original 2 llama 0.380000 0.270000 0.650000 (0.658632)
 Llamadas simples 0.240000 0.140000 0.380000 (0.390004)
 llamada de impresión única 0.170000 0.070000 0.240000 (0.250661)

Los resultados anteriores muestran claramente una mejora en la reducción de llamadas a funciones y en el uso de ‘imprimir’ sobre ‘poner’.

2) Evitar objetos temporales:
Considere seguir el código ruby, ¿cómo podemos hacerlo rápido?

 #!/usr/bin/env ruby require 'benchmark' a = "" 1_000_00.times { a += "." } 

Si observa la línea de trabajo principal “a + =”. “”, Esta línea se expandirá como

 a = a + "." 

Lo que significa que cada vez que estamos haciendo esta operación, estamos creando objetos temporales con valor (a + “.”) Y luego asignándolos de nuevo a a. La creación de esta variable temporal nos está costando la mayor parte del tiempo y el espacio. Si podemos evitar el uso de esta variable temporal, podemos hacer las cosas más rápido. Considera lo siguiente:

 #!/usr/bin/env ruby require 'benchmark' a = "" 1_000_00.times { a << "." } 

El código anterior está haciendo lo mismo que hemos hecho anteriormente, pero se ejecuta mucho más rápido. Si lo perfilamos utilizando benchmark o ruby-prof, podemos ver los beneficios de evitar objetos temporales. A continuación se muestran los resultados de referencia de los 2 enfoques anteriores, observe que el segundo enfoque es casi 100 veces más rápido:

  Ensayo ---------------------------------------
 + = 2.740000 0.460000 3.200000 (3.250396)
 << 0.040000 0.000000 0.040000 (0.037068)
 ------------------------------ total: 3.240000seg

      sistema de usuario total real
 + = 2.760000 0.510000 3.270000 (3.352737)
 << 0.040000 0.000000 0.040000 (0.036535)

3) Usar funciones destructivas:
Considere seguir las llamadas a funciones, ¿podemos hacerlo más rápido?

 "akashagrawal".gsub "akashagrawal".sub "akash" + "agrawal" [1, 2].collect [1, 2].compact [1, 2].uniq [1 , [2, 3]].flatten [1] + [2] 

Todas y cada una de las líneas del código anterior crean un objeto temporal y luego asignan el mismo a ese objeto. Si usamos la versión destructiva correspondiente de arriba (dada a continuación), podemos ahorrar mucho tiempo.

 "akashagrawal".gsub! "akashagrawal".sub! "akash" << "agrawal" [1, 2].collect! [1, 2].compact! [1, 2].uniq! [1 , [2, 3]].flatten! [1].concat[2] 

Dejo el ejercicio de evaluación comparativa del uso de la función destructiva frente a la no destructiva para los lectores.

No use en exceso las funciones destructivas, ya que “la optimización prematura es la raíz de todo mal”.

4) Iterando: 3 formas como se indica a continuación.

 for i in 1..n 1.upto(n) do |i| n.times do |i| 

Por lo general, es el más rápido. Pero confíe en sus resultados de evaluación comparativa.

5) coincidencia de cadenas:
Siempre use la combinación de expresiones regulares siempre que sea posible

Peor:

 for i in 1..n do File.open(file).each do |line| if line.match('/bin/bash') then $stderr.print line.split(':')[0] $stderr.puts " uses /bin/bash" end end end 

Es mejor usar expresiones regulares ya que la coincidencia de cadenas es muy costosa

  for i in 1..n do re = /\/bin\/bash/ File.open(file).each do |line| if line.match(re) then $stderr.print line.split(':')[0] $stderr.puts "uses /bin/bash" end end end 

Aún mejor porque usar el método de coincidencia es más lento que usar = ~, pero no confíes en mi palabra, perfílalo.

 for i in 1..n do File.open(file).each do |line| if line =~ %r{/bin/bash} then $stderr.print #{line.split(':')[0] $stderr.puts "uses /bin/bash" end end 

6) hash [“cadena”] vs hash [: símbolo]
hash["my_key_name"] = 1
Esto es realmente muy caro que
hash[:my_key_name] = 1

Cada invocación de “clave” es una asignación de memoria individual. UNA cadena explícitamente para esa variable.

hash[:mykey] = 3 => cuesta muy poca memoria y tiene aproximadamente el doble de rendimiento que la versión String. La comparación de cadenas es aproximadamente 200-300% tan cara como usar un símbolo.

:symbol costo de :symbol costo en la tabla de símbolos global, no se preocupe, a menos que lo abuse.

7) Concatenación

 a = "" 1_000_000.times { a += "." } 

es mucho más lento que

 a = "" 1_000_000.times { a << "." } 

Porque la primera versión usa variables intermedias en cada iteración.

8) Las llamadas al método son caras
Utilizar
if !conditions
o
unless conditions

en lugar de

if conditions.nil?

9) Use tareas paralelas
LENTO:

 A = "slide" B = "Share" C = "quora" 

RÁPIDO:

A, B, C = "slide", "Share", "quora"

10) Utilice la memorización

 Class user attr_accessor :first_name :last_name def full_name @full_name ||= "#{first_name} #{last_name}" end end 

11) Elija un entorno inteligente

 def foo if env == 'production' #do this else #do that end end 

La función anterior verifica ‘env’ cada vez que se llama a la función.

 if env == 'production' def foo #do this end else def foo #do that end end 

La definición de función anterior guarda esa verificación en cada llamada de función.

12) Escriba el código ‘C’ dentro de ruby ​​usando la gema ‘rubyinline’

Esto es increíble y su código ruby ​​vuela a tiempo. Para obtener más información, visite http://www.rubyinside.com/write-…

En Ruby, cada método aceptará un bloque como último argumento. También es posible omitir el argumento de bloque de los métodos Enumerable para recuperar un enumerador que permite la iteración [1].

 enumerator = [3, 2, 1].each enumerator.next #=> 3 enumerator.peek #=> 2 enumerator.rewind 

Para los enumeradores finitos, Ruby generará una excepción StopIteration si solicita el siguiente objeto, más allá del último. Sin embargo, esta excepción se rescata automáticamente por bucle:

 loop do puts enumerator.next end puts "Blastoff!" 

Ruby también permite enumeradores infinitos . Esta puede ser una estructura útil para representar flujos de datos, notas musicales en un juego o el color de fondo para las filas de una tabla:

 infinite_zebra = ["black", "white"].cycle infinite_zebra.next #=> "black" infinite_zebra.next #=> "white" infinite_zebra.next #=> "black" 

Desde la versión 2, Ruby admite enumeradores perezosos. Entonces, si quisieras encontrar los primeros cinco cuadrados pares e perfectos de más de mil, podrías escribir:

 lazy_integers = (1..Float::INFINITY).lazy lazy_integers.collect { |x| x ** 2 }. select { |x| x.even? }. reject { |x| x < 1000 }. first(5) #=> [1024, 1156, 1296, 1444, 1600] 

Sin una evaluación perezosa, este programa nunca terminaría. (Se quedaría atascado tratando de cuadrar todos los enteros hasta el infinito). Al hacerlo flojo, canalizará cada entero a través de las funciones. Primero, 1 es cuadrado. Eso es igual a 1, pero no es uniforme, así que volvemos al principio. Luego, 2 es cuadrado. Eso es igual a 4, que es par, pero rechazado por ser menos de mil. Etcétera, hasta que cinco números atraviesen la tubería y salgan por el otro lado.

Del mismo modo, si quisieras encontrar los primeros cinco primos gemelos, podrías escribir:

 require "prime" lazy_primes = Prime.lazy lazy_primes.select { |x| (x - 2).prime? }. collect { |x| [x - 2, x] }. first(5) #=> [[3, 5], [5, 7], [11, 13], [17, 19], [29, 31]] 

O si desea encontrar los próximos diez viernes 13, podría escribir:

 require "date" lazy_dates = (Date.today..Date.new(9999)).lazy lazy_dates.select { |d| d.friday? && d.day == 13 }. first(10) 

O si desea encontrar una cadena en un archivo de texto sin leer todo el archivo en la memoria:

 lazy_file = File.readlines("/path/to/file").lazy lazy_file.detect { |x| x =~ /regexp/ } 

Como puede ver en estos ejemplos amplios y variados, los enumeradores perezosos de Ruby son útiles en muchas situaciones, ya que producen código que es elegante y eficiente . También componen muy bien con algunas de mis otras características favoritas de Ruby: bloques, rangos y el hecho de que todo es un objeto. En la mayoría de los otros idiomas, la iteración se realiza con una declaración de palabra clave (por ejemplo, for ), pero en Ruby, incluso la iteración se puede hacer solo con objetos, lo que lo convierte en el lenguaje orientado a objetos “más puro”.

[1] La idea de iteradores fue desarrollada originalmente por la ganadora del Premio Turing Barbara Liskov, presentada en su lenguaje de programación CLU en 1974.

¿Alguna vez se preguntó si tiene ese comando de servidor de línea única equivalente a Python en Ruby ?

Bueno, solo abra su terminal, vaya al directorio en el que desea ejecutarlo y ejecute esto:

ruby -run -e httpd . -p 5000

Este comando es bastante comprensible: debe activar un servidor web que se ejecuta en su máquina en el puerto 5000.

Tenga en cuenta que NO se necesitan instalaciones de gemas adicionales para que esto funcione. Creo que se ejecuta en webrick (que viene junto con la instalación estándar de ruby).

No hace falta mencionar que también puede monitorear las solicitudes, y creo que esto será útil para probar sus archivos html estáticos, compartir archivos rápidamente (y de manera sucia), etc.

Fuente original:
conversación de Twitter n0kada y tenderlove

Algunas maneras geniales de usar el soporte de Ruby para el paradigma de programación funcional:

1. “cada” (esto, por supuesto, es algo muy básico)

En lugar del estilo imperativo regular para … los siguientes bucles e iteradores para las colecciones, “cada” puede ayudarlo a escribir más código de estilo funcional.

Estilo imperativo para el siguiente ciclo:

 for ctr in 1..10 puts ctr end 

Usando cada uno:
(1..10).each{ |x| puts x }
los dos fragmentos anteriores muestran 1..10
La iteración de estilo imperativo sobre una colección se verá como …

  while (.. iterator.hasNext ...)
    elemento de impresión * 2
 fin 

Usando cada uno:
[1,2,3,4,5,6,7,8,9,10].each{ |x| puts 2*x }
Ambos duplican los números en una lista

2. Filtro

select y find_all son técnicas de Ruby para ayudarlo a implementar una función de filtro de orden superior, pasando una condición de predicado.

(1..20).select{ |x| x%2 == 0 }
=> Salida: 2,4,6,8,10….

find_all logra propósitos similares

3. Lambda

que se asigna (más o menos) a la lambda del cálculo lambda de la Teoría de la Computación (que desafortunadamente no he estudiado profundamente).
Ayuda a escribir funciones anónimas .

 sum =-> { |x,y| x+y } diff =-> { |x,y| xy } def double(x, y, function) 2 * function.call(sum, diff) end double(5, 10, sum) #=> Output : 2 * (5+10) = 30 double(2, 1, diff) #=> Output: 2 * (2-1) = 2 

Mucho mejor que tener que escribir 2 funciones separadas para doubleSum y doubleDiff….

4. objetos inmutables
Deje de invocar funciones que terminan con ! firmar
p.ej str.strip! o str.chomp! porque mutan el objeto original.

Otros conceptos importantes:

5. Cierres

Se puede implementar usando lambda o proc; se unen a los valores del entorno en el momento en que se crean.

6. Curry

Utilizando .curry ya sea en procs o lambdas.

Comenzando con una función que toma un cierto número de parámetros, genere una o más funciones que tengan uno o más valores de parámetros ya completados.

También busque funciones parciales

7. La palabra clave Inject.

Para ir a través de una colección y devolver un resultado basado en todo lo que se ve en la colección.
Ejemplo simple y estándar: suma de números en una matriz.

[1, 2, 3, 4].inject(0) { |result, element| result + element }

Ejemplo más complicado:
Esto podría usarse para encontrar la cadena modal en una lista de cadenas, es decir, la cadena que ocurre con mayor frecuencia

 list_of_strings.inject(Hash.new(0)) { |result, str| result[str]+=1 result }.inject(Struct.new(:str, :freq).new("", 0)) { |result, (k,v)| if v > result.freq result.str = k result.freq = v end result }.str If we started off with [code ruby]list_of_strings=["aa","aa","aa","b","c","d"] 

La declaración volvería "aa" porque ocurre con más frecuencia que cualquier otra cadena en la matriz.

Por supuesto, la afirmación anterior es bastante ofuscada y definitivamente no es elegante, pero fue más para transmitir la idea de que inyectar puede usarse como un componente importante al tratar de alejarse de los estilos imperativos.

Ruby está muy orientado a objetos. Cada valor es un objeto, incluidas las clases y todas las instancias de tipos que la mayoría de los otros lenguajes son primitivos, como enteros, cadenas, matrices, etc. Debido a esto, la mayoría de las llamadas normales son esencialmente azúcar sintáctica para invocar el método en el objeto.

  >> 1 + 1
 => 2
 >> 1. + (1)
 => 2

  >> ["a", "b", "c", "d"]. [] (0)
 => "a"

Ruby también le permite anular todos los métodos que se incluyen:

  >> clase Fixnum
 >> def + (arg)
 >> volver self - arg
 >> fin
 >> fin
 => nulo

 >> 1 + 1
 => 0

Por supuesto, reemplazar el Fixnum plus probablemente no sea la mejor idea (salvo cambiar todos los métodos básicos para bromear con amigos), pero la idea se puede aplicar a cualquier cosa.

define_method e instance_eval son bastante dulces. Entre otras cosas, las uso para crear pequeñas DSL de conveniencia.

Por ejemplo, imagine que tiene user.messages , que devuelve una matriz de objetos Message , y desea capturar todos los mensajes del usuario con otro usuario. Puedes hacer un DSL ingenioso para eso:

  Usuario de clase
 ...
   mensajes de def
     @messages = some_db_query

     # agrega un método a la matriz
     @ messages.instance_eval do
       def with_user (otro_usuario)
         mensajes = []
         self.each do | mensaje |
           mensajes << mensaje si mensaje.usuario_id == otro_usuario.id
         fin
         mensajes
       fin
     fin

     @messages
   fin
 ...

Y ahora puede hacer esto: user.messages.with_user(other_user) .

(No estoy diciendo que así es como debe implementar dicho caso de uso, pero ilustra el truco)

No tanto el lenguaje como la herramienta, pero lo estoy publicando porque lo encuentro muy útil y no se habla tanto de eso.

En la consola irb and rails, el carácter ‘_’ es el último resultado devuelto.

Supongamos que tiene una cadena larga y compleja de rubí que evalúa, pero se olvidó de establecerla en una variable al principio, solo haga “a = _” y tendrá el último resultado almacenado en ‘a’.

Ejemplo simplificado:

 rails c User.first a = _ # a = first user! 

No sé si podríamos decir que son trucos de Ruby, pero agregaré mis 2 centavos sobre cómo escribir un código Ruby más hermoso, simple e idiomático .

Usando Map en lugar de cada iterador

Cada iterador:

  user_ids = []
 users.each {| user |  user_ids << user.id}

Método de mapa:

  user_ids = users.map {| user |  user.id}

O mejor y más rápido:

  user_ids = users.map (&: id)

Usando Muestra en lugar de métodos rand y shuffle:

Método Rand:

  [1, 2, 3] [rand (3)]

Método aleatorio:

  [1, 2, 3] .shuffle.first

Muestra, simplemente hermosa e intuitiva:

  [1, 2, 3] .muestra

Retorno implícito:

Creo que es una característica conocida de Ruby, pero podemos devolver valores en métodos sin la declaración de devolución

Con declaración de devolución

  def get_user_ids (usuarios)
  return users.map (&: id)
 fin

Con retorno implícito

  def get_user_ids (usuarios)
  users.map (&: id)
 fin

Si la declaración:

Realmente me gusta esta característica de la sintaxis de Ruby. Usando la declaración if después del código

  def hey_ho?
  cierto
 fin

 pone "vamos" si hey_ho?

¡Se siente tan natural!

Un artículo realmente agradable sobre Ruby idiomático tiene una versión completa de cómo escribir Ruby de una manera más idiomática.

Espero que la gente escriba un código Ruby más hermoso e idiomático. Realmente creo que ayuda a escribir un mejor software y productos digitales.

Otro no relacionado con el idioma, pero creo que alguien encontrará esto útil:

Intente usar la palanca REPL en lugar del paquete irb. Es mucho más potente y tiene un excelente resaltado de sintaxis.

Puede abusar de los argumentos predeterminados de los métodos como lo describí en Hacking Ruby.

 def fib(n, t = n < 2 ? n : fib(n-1) + fib(n-2)) t; end 

No es tan útil, pero es genial.

Me pregunto por qué alguien no ha mencionado ya el “operador T-Square” a ||= b .
Eso está listo a=b si a es nil o false Este patrón de código es exclusivo de Ruby y lo hace aún más increíble.

1) Extraer dígitos de una cadena :

“ab123cd” [/ \ d + /]

=> “123”

2) Mayúscula una letra usando un operador XOR bit a bit :

(“b” .ord ^ 32) .chr

=> “B”

3) Forme una cadena utilizando valores ASCII y el operador “pala” :

“” << 97 << 98 << 99

=> “Abc”

Más trucos geniales aquí:

11 trucos de rubí que no has visto antes – Bytes negros

¡Disfrutar!

Como está buscando cosas menos conocidas, eche un vistazo a los literales ruby, la sintaxis% y sus modificadores: http://en.wikibooks.org/wiki/Rub

Los dos que encontrará con mayor frecuencia son% x para la ejecución del sistema (y capturar la salida) y% w para crear una matriz a partir de palabras separadas por espacios en blanco.

Solo estoy agregando algunas cosas más geniales que puedes hacer con ruby ​​…

1. Coolway para usar requiere.
%w{ rubygems sinatra haml parseconfig json }.each { |x| require x }

2. Rastrear todo “include

 module Handle //blah blah.. end class Aaa include Handle end class Bbb include Handle end #Now track all the class that use the module "Handle" Handle.constants.select do |c| if Class === Handle.const_get(c) pc // do something end end 

3. Procs y Lambdas: la mejor parte de los idiomas.

Copie la sintaxis súper genial de la definición de hash y matriz

 class MyClass def self.[] enumeration # do something useful # enumeration may be a hash-like (two values yielded), or array-like (one value yielded) end end MyClass[a: 2, b: 3] MyClass[*(1...3)] # etc. 

Entrada: [[1,2,3], [4,5,6], [7,8,9]]

Salida: [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

matriz = [[1,2,3], [4,5,6], [7,8,9]]

matriz [0] .zip (matriz [1], matriz [2])

Pato escribiendo

[1, 2, 3] .map (&: to_s) # => [“1”, “2”, “3”]

La razón por la que esto funciona también es muy sutil.

#encoding: utf-8

¡Agregue la línea anterior a la primera línea de su código ruby ​​para superar los errores de codificación extraños que no se pueden resolver con el método force_encoding ()!

Me encanta el atajo String # [] para extraer datos (expresión regular) sin usar el método “match”.