it-swarm-es.tech

Rasgos ocultos de rubí

Continuando con el meme "Características ocultas de ...", compartamos las características menos conocidas pero útiles del lenguaje de programación Ruby.

Trate de limitar esta discusión con el núcleo de Ruby, sin nada de Ruby on Rails.

Ver también:

(Por favor, solo one característica oculta por respuesta).

Gracias

160
squadette

Desde Ruby 1.9 Proc # === es un alias para Proc # call, lo que significa que los objetos Proc se pueden usar en declaraciones de casos como las siguientes:

def multiple_of(factor)
  Proc.new{|product| product.modulo(factor).zero?}
end

case number
  when multiple_of(3)
    puts "Multiple of 3"
  when multiple_of(7)
    puts "Multiple of 7"
end
80
Farrel

Peter Cooper tiene una buena lista de trucos de Ruby. Quizás mi favorito es permitir que se enumeren tanto los artículos individuales como las colecciones. (Es decir, trata un objeto que no es de colección como una colección que contiene solo ese objeto). Se ve así:

[*items].each do |item|
  # ...
end
76
James A. Rosen

No sé qué tan oculto está esto, pero lo he encontrado útil cuando necesito hacer un Hash de una matriz unidimensional:

fruit = ["Apple","red","banana","yellow"]
=> ["Apple", "red", "banana", "yellow"]

Hash[*fruit]    
=> {"Apple"=>"red", "banana"=>"yellow"}
64
astronautism

Un truco que me gusta es usar el expansor splat (*) en otros objetos que no sean Arrays. Aquí hay un ejemplo en una coincidencia de expresiones regulares:

match, text, number = *"Something 981".match(/([A-z]*) ([0-9]*)/)

Otros ejemplos incluyen:

a, b, c = *('A'..'Z')

Job = Struct.new(:name, :occupation)
tom = Job.new("Tom", "Developer")
name, occupation = *tom
54
tomafro

Wow, nadie mencionó al operador de flip flop:

1.upto(100) do |i|
  puts i if (i == 3)..(i == 15)
end
52
Konstantin Haase

Una de las cosas interesantes de Ruby es que puede llamar a métodos y ejecutar código en lugares que otros idiomas desaprueban, como en el método o las definiciones de clase.

Por ejemplo, para crear una clase que tiene una superclase desconocida hasta el tiempo de ejecución, es decir, es aleatorio, podría hacer lo siguiente:

class RandomSubclass < [Array, Hash, String, Fixnum, Float, TrueClass].sample

end

RandomSubclass.superclass # could output one of 6 different classes.

Esto utiliza el método 1.9 Array#sample (solo en 1.8.7, vea Array#choice), y el ejemplo es bastante artificial, pero puede ver el poder aquí.

Otro ejemplo interesante es la capacidad de poner valores de parámetros predeterminados que no son fijos (como lo demandan otros idiomas):

def do_something_at(something, at = Time.now)
   # ...
end

Por supuesto, el problema con el primer ejemplo es que se evalúa en el momento de la definición, no en el tiempo de llamada. Entonces, una vez que se ha elegido una superclase, permanece esa superclase durante el resto del programa.

Sin embargo, en el segundo ejemplo, cada vez que llame a do_something_at, la variable at será la hora en que se llamó al método (bueno, muy cerca de él)

49
Bo Jeanes

Otra característica pequeña: convierte una Fixnum en cualquier base hasta 36:

>> 1234567890.to_s(2)
=> "1001001100101100000001011010010"

>> 1234567890.to_s(8)
=> "11145401322"

>> 1234567890.to_s(16)
=> "499602d2"

>> 1234567890.to_s(24)
=> "6b1230i"

>> 1234567890.to_s(36)
=> "kf12oi"

Y como Huw Walters ha comentado, la conversión a la inversa es igual de simple:

>> "kf12oi".to_i(36)
=> 1234567890
47
tomafro

Hashes con valores por defecto! Una matriz en este caso.

parties = Hash.new {|hash, key| hash[key] = [] }
parties["Summer party"]
# => []

parties["Summer party"] << "Joe"
parties["Other party"] << "Jane"

Muy útil en metaprogramación.

40
August Lilleaas

Descarga la fuente de Ruby 1.9, y emite make golf, entonces puedes hacer cosas como esta:

make golf

./goruby -e 'h'
# => Hello, world!

./goruby -e 'p St'
# => StandardError

./goruby -e 'p 1.tf'
# => 1.0

./goruby19 -e 'p Fil.exp(".")'
"/home/manveru/pkgbuilds/Ruby-svn/src/trunk"

Lee el golf_prelude.c para más cosas interesantes que se esconden.

39
manveru

Otra adición divertida en la funcionalidad 1.9 Proc es Proc # curry, que le permite convertir un Proc aceptando n argumentos en uno aceptando n-1. Aquí se combina con el consejo Proc # === que mencioné anteriormente:

it_is_day_of_week = lambda{ |day_of_week, date| date.wday == day_of_week }
it_is_saturday = it_is_day_of_week.curry[6]
it_is_sunday = it_is_day_of_week.curry[0]

case Time.now
when it_is_saturday
  puts "Saturday!"
when it_is_sunday
  puts "Sunday!"
else
  puts "Not the weekend"
end
38
Farrel

Operadores booleanos en valores no booleanos.

&& y ||

Ambos devuelven el valor de la última expresión evaluada.

Es por eso que el ||= actualizará la variable con el valor devuelto expresión en el lado derecho si la variable no está definida. Esto no está documentado explícitamente, pero el conocimiento común.

Sin embargo, el &&= no es tan conocido.

string &&= string + "suffix"

es equivalente a

if string
  string = string + "suffix"
end

Es muy útil para operaciones destructivas que no deberían continuar si la variable no está definida.

35
EmFi

La función Symbol # to_proc que proporciona Rails es realmente genial.

En lugar de

Employee.collect { |emp| emp.name }

Puedes escribir:

Employee.collect(&:name)
29
hoyhoy

Una última: en Ruby puedes usar cualquier personaje que quieras para delimitar cadenas. Toma el siguiente código:

message = "My message"
contrived_example = "<div id=\"contrived\">#{message}</div>"

Si no desea evitar las comillas dobles dentro de la cadena, simplemente puede usar un delimitador diferente:

contrived_example = %{<div id="contrived-example">#{message}</div>}
contrived_example = %[<div id="contrived-example">#{message}</div>]

Además de evitar tener que escapar de los delimitadores, puede utilizar estos delimitadores para obtener mejores cadenas multilínea:

sql = %{
    SELECT strings 
    FROM complicated_table
    WHERE complicated_condition = '1'
}
28
tomafro

Me parece que el uso del comando define_method para generar dinámicamente los métodos es bastante interesante y no tan conocido. Por ejemplo:

((0..9).each do |n|
    define_method "press_#{n}" do
      @number = @number.to_i * 10 + n
    end
  end

El código anterior utiliza el comando 'define_method' para crear dinámicamente los métodos "press1" a "press9". En lugar de escribir los 10 métodos que esencialmente contienen el mismo código, el comando de definir método se usa para generar estos métodos sobre la marcha, según sea necesario.

26

Utilice un objeto Range como una lista perezosa infinita:

Inf = 1.0 / 0

(1..Inf).take(5) #=> [1, 2, 3, 4, 5]

Más información aquí: http://banisterfiend.wordpress.com/2009/10/02/wtf-infinite-ranges-in-Ruby/

26
horseyguy

módulo_función

Los métodos del módulo que se declaran como module_function crearán copias de sí mismos como private instance en la clase que incluye el módulo:

module M
  def not!
    'not!'
  end
  module_function :not!
end

class C
  include M

  def fun
    not!
  end
end

M.not!     # => 'not!
C.new.fun  # => 'not!'
C.new.not! # => NoMethodError: private method `not!' called for #<C:0x1261a00>

Si utiliza module_function sin ningún argumento, entonces cualquier método de módulo que venga después de la instrucción module_function se convertirá automáticamente en un módulo de funciones.

module M
  module_function

  def not!
    'not!'
  end

  def yea!
    'yea!'
  end
end


class C
  include M

  def fun
    not! + ' ' + yea!
  end
end
M.not!     # => 'not!'
M.yea!     # => 'yea!'
C.new.fun  # => 'not! yea!'
23
newtonapple

Inyectar corto, como tal:

Suma de rango:

(1..10).inject(:+)
=> 55
23
user130730

Advertencia: este artículo fue votado # 1Hack más horrible de 2008, así que utilícelo con cuidado. En realidad, evítalo como la plaga, pero ciertamente es Hidden Ruby.

Los superadores agregan nuevos operadores a Ruby

¿Alguna vez ha deseado un operador de protocolo de enlace super secreto para alguna operación única en su código? ¿Te gusta jugar al golf de código? Intente operadores como - ~ + ~ - o <--- Este último se usa en los ejemplos para invertir el orden de un elemento.

No tengo nada que ver con el Proyecto de Superators más allá de admirarlo.

21
Captain Hammer

Llego tarde a la fiesta, pero:

Puede tomar fácilmente dos matrices de igual longitud y convertirlas en un hash con una matriz que proporciona las claves y la otra los valores:

a = [:x, :y, :z]
b = [123, 456, 789]

Hash[a.Zip(b)]
# => { :x => 123, :y => 456, :z => 789 }

(Esto funciona porque Array # Zip "comprime" los valores de los dos arreglos:

a.Zip(b)  # => [[:x, 123], [:y, 456], [:z, 789]]

Y Hash [] puede tomar tal matriz. He visto a la gente hacer esto también:

Hash[*a.Zip(b).flatten]  # unnecessary!

Lo que produce el mismo resultado, pero el splat y el aplanamiento son totalmente innecesarios, tal vez no fueron en el pasado?

19
Jordan Running

Hachas auto vivificantes en Ruby

def cnh # silly name "create nested hash"
  Hash.new {|h,k| h[k] = Hash.new(&h.default_proc)}
end
my_hash = cnh
my_hash[1][2][3] = 4
my_hash # => { 1 => { 2 => { 3 =>4 } } }

Esto puede ser muy útil.

19
Trevoke

Destructurando una matriz

(a, b), c, d = [ [:a, :b ], :c, [:d1, :d2] ]

Dónde:

a #=> :a
b #=> :b
c #=> :c
d #=> [:d1, :d2]

Usando esta técnica, podemos usar una asignación simple para obtener los valores exactos que queremos de una matriz anidada de cualquier profundidad.

16
horseyguy

Class.new()

Crear una nueva clase en tiempo de ejecución. El argumento puede ser una clase de la cual derivarse, y el bloque es el cuerpo de la clase. También es posible que desee consultar const_set/const_get/const_defined? para registrar correctamente su nueva clase, de modo que inspect imprima un nombre en lugar de un número.

No es algo que necesites todos los días, pero es bastante útil cuando lo haces.

15
Justin Love

crear una matriz de números consecutivos:

x = [*0..5]

establece x en [0, 1, 2, 3, 4, 5]

13
horseyguy

Mucha de la magia que ves en Rubyland tiene que ver con la metaprogramación, que es simplemente escribir código que escribe código para ti. Los attr_accessor, attr_reader y attr_writer de Ruby son metaprogramas simples, ya que crean dos métodos en una línea, siguiendo un patrón estándar. Rails realiza una gran cantidad de metaprogramaciones con sus métodos de gestión de relaciones como has_one y belongs_to.

Pero es bastante simple crear sus propios trucos de metaprogramación utilizando class_eval para ejecutar código escrito de forma dinámica.

El siguiente ejemplo permite que un objeto envoltorio reenvíe ciertos métodos a un objeto interno:

class Wrapper
  attr_accessor :internal

  def self.forwards(*methods)
    methods.each do |method|
      define_method method do |*arguments, &block|
        internal.send method, *arguments, &block
      end
    end
  end

  forwards :to_i, :length, :split
end

w = Wrapper.new
w.internal = "12 13 14"
w.to_i        # => 12
w.length      # => 8
w.split('1')  # => ["", "2 ", "3 ", "4"]

El método Wrapper.forwards toma símbolos para los nombres de los métodos y los almacena en la matriz methods. Luego, para cada uno de los dados, usamos define_method para crear un nuevo método cuyo trabajo es enviar el mensaje, incluidos todos los argumentos y bloques.

Un gran recurso para los problemas de metaprogramación es Por qué el Lucky Stiff "está viendo claramente la metaprogramación" .

13
TALlama

use cualquier cosa que responda a ===(obj) para las comparaciones de casos:

case foo
when /baz/
  do_something_with_the_string_matching_baz
when 12..15
  do_something_with_the_integer_between_12_and_15
when lambda { |x| x % 5 == 0 }
  # only works in Ruby 1.9 or if you alias Proc#call as Proc#===
  do_something_with_the_integer_that_is_a_multiple_of_5
when Bar
  do_something_with_the_instance_of_Bar
when some_object
  do_something_with_the_thing_that_matches_some_object
end

Module (y por lo tanto Class), Regexp, Date, y muchas otras clases definen un método de instancia: === (otro), y todos pueden ser utilizados.

Gracias a Farrel por el recordatorio de que Proc#call tiene un alias como Proc#=== en Ruby 1.9.

12
James A. Rosen

El binario "Ruby" (al menos MRI) es compatible con muchos de los conmutadores que hicieron a Perl one-liners bastante popular.

Los significativos:

  • -n Establece un bucle externo con solo "gets" - que mágicamente funciona con el nombre de archivo dado o STDIN, configurando cada línea de lectura en $ _
  • -p Similar a -n pero con un puts automático al final de cada iteración de bucle
  • -a Llamada automática a .split en cada línea de entrada, almacenada en $ F
  • -i Archivos de entrada de edición en contexto
  • -l Llamada automática a .chomp en entrada
  • -e Ejecutar un pedazo de código
  • -c verifica el código fuente
  • -w con advertencias

Algunos ejemplos:

# Print each line with its number:
Ruby -ne 'print($., ": ", $_)' < /etc/irbrc

# Print each line reversed:
Ruby -lne 'puts $_.reverse' < /etc/irbrc

# Print the second column from an input CSV (dumb - no balanced quote support etc):
Ruby -F, -ane 'puts $F[1]' < /etc/irbrc

# Print lines that contain "eat"
Ruby -ne 'puts $_ if /eat/i' < /etc/irbrc

# Same as above:
Ruby -pe 'next unless /eat/i' < /etc/irbrc

# Pass-through (like cat, but with possible line-end munging):
Ruby -p -e '' < /etc/irbrc

# Uppercase all input:
Ruby -p -e '$_.upcase!' < /etc/irbrc

# Same as above, but actually write to the input file, and make a backup first with extension .bak - Notice that inplace edit REQUIRES input files, not an input STDIN:
Ruby -i.bak -p -e '$_.upcase!' /etc/irbrc

Siéntase libre de buscar en Google "Ruby one-liners" y "Perl one-liners" para obtener más ejemplos prácticos y útiles. Básicamente, te permite usar Ruby como un reemplazo bastante poderoso para awk y sed.

11
minaguib

El método send () es un método de propósito general que se puede usar en cualquier clase u objeto en Ruby. Si no se reemplaza, send () acepta una cadena y llama al nombre del método cuya cadena se pasa. Por ejemplo, si el usuario hace clic en el botón "Clr", la cadena "press_clear" se enviará al método send () y se llamará al método "press_clear". El método send () permite una forma divertida y dinámica de llamar a funciones en Ruby.

 %w(7 8 9 / 4 5 6 * 1 2 3 - 0 Clr = +).each do |btn|
    button btn, :width => 46, :height => 46 do
      method = case btn
        when /[0-9]/: 'press_'+btn
        when 'Clr': 'press_clear'
        when '=': 'press_equals'
        when '+': 'press_add'
        when '-': 'press_sub'
        when '*': 'press_times'
        when '/': 'press_div'
      end

      number.send(method)
      number_field.replace strong(number)
    end
  end

Hablo más sobre esta característica en Blogging Shoes: The Simple-Calc Application

10
private unless Rails.env == 'test'
# e.g. a bundle of methods you want to test directly

Parece un buen/interesante (y en algunos casos) agradable/útil truco/característica de Ruby.

9
Szymon Jeż

Fixnum#to_s(base) puede ser realmente útil en algunos casos. Uno de estos casos es generar tokens aleatorios (pseudo) únicos al convertir un número aleatorio en una cadena usando la base de 36.

Ficha de longitud 8:

Rand(36**8).to_s(36) => "fmhpjfao"
Rand(36**8).to_s(36) => "gcer9ecu"
Rand(36**8).to_s(36) => "krpm0h9r"

Ficha de longitud 6:

Rand(36**6).to_s(36) => "bvhl8d"
Rand(36**6).to_s(36) => "lb7tis"
Rand(36**6).to_s(36) => "ibwgeh"
9
sickill

Engañar a alguna clase o módulo diciéndole que ha requerido algo que realmente no ha requerido:

$" << "something"

Esto es útil, por ejemplo, cuando se requiere A que a su vez requiere B pero no necesitamos B en nuestro código (y A tampoco lo usará a través de nuestro código):

Por ejemplo, el bdrb_test_helper requires'test/spec' de Backgroundrb, pero no lo usa en absoluto, así que en su código:

$" << "test/spec"
require File.join(File.dirname(__FILE__) + "/../bdrb_test_helper")
9
olegueret

Definir un método que acepte cualquier número de parámetros y simplemente los descarte todos

def hello(*)
    super
    puts "hello!"
end

El método anterior hello solo necesita puts"hello" en la pantalla y llamar a super - pero como la superclase hello define los parámetros, también tiene que hacerlo. ellos un nombre.

9
horseyguy

Para combinar múltiples expresiones regulares con |, puede usar

Regexp.union /Ruby\d/, /test/i, "cheat"

para crear un Regexp similar a:

/(Ruby\d|[tT][eE][sS][tT]|cheat)/
8
J-_-L

Encuentro esto útil en algunos guiones. Posibilita el uso directo de variables de entorno, como en los scripts de Shell y Makefiles. Las variables de entorno se utilizan como reserva para constantes de Ruby no definidas.

>> class <<Object
>>  alias :old_const_missing :const_missing
>>  def const_missing(sym)
>>   ENV[sym.to_s] || old_const_missing(sym)
>>  end
>> end
=> nil

>> puts Shell
/bin/zsh
=> nil
>> TERM == 'xterm'
=> true
8
Ropez

¿Qué tal abrir un archivo basado en ARGV [0]?

readfile.rb:

$<.each_line{|l| puts l}

Ruby readfile.rb testfile.txt

Es un gran atajo para escribir guiones únicos. Hay una gran cantidad de variables predefinidas que la mayoría de las personas no conocen. Utilícelos sabiamente (lea: no ensucie una base de código que planea mantener con ellos, puede causar problemas).

8
Scott Holden

Soy un fan de:

%w{An Array of strings} #=> ["An", "Array", "of", "Strings"]

Es algo gracioso la frecuencia con la que es útil.

5
Judson

Múltiples valores de retorno

def getCostAndMpg
    cost = 30000  # some fancy db calls go here
    mpg = 30
    return cost,mpg
end
AltimaCost, AltimaMpg = getCostAndMpg
puts "AltimaCost = #{AltimaCost}, AltimaMpg = #{AltimaMpg}"

Asignación paralela

i = 0
j = 1
puts "i = #{i}, j=#{j}"
i,j = j,i
puts "i = #{i}, j=#{j}"

Atributos Virtuales

class Employee < Person
  def initialize(fname, lname, position)
    super(fname,lname)
    @position = position
  end
  def to_s
     super + ", #@position"
  end
  attr_writer :position
  def etype
     if @position == "CEO" || @position == "CFO"
         "executive"
     else
         "staff"
     end
  end
end
employee = Employee.new("Augustus","Bondi","CFO")
employee.position = "CEO"
puts employee.etype    =>  executive
employee.position = "Engineer"
puts employee.etype    =>  staff

method_missing - una idea maravillosa

(En la mayoría de los idiomas, cuando no se puede encontrar un método y se produce un error y su programa se detiene. En Ruby, realmente puede detectar esos errores y quizás hacer algo inteligente con la situación)

class MathWiz
  def add(a,b) 
    return a+b
  end
  def method_missing(name, *args)
    puts "I don't know the method #{name}"
  end
end
mathwiz = MathWiz.new
puts mathwiz.add(1,4)
puts mathwiz.subtract(4,2)

5

No sé restar el método.

nulo

5
Ramiz Uddin

La punta de James A. Rosen es genial ([* items] .each), pero me parece que destruye los hashes:

irb(main):001:0> h = {:name => "Bob"}
=> {:name=>"Bob"}
irb(main):002:0> [*h]
=> [[:name, "Bob"]]

Prefiero esta forma de manejar el caso cuando acepto una lista de cosas para procesar, pero soy indulgente y permito que la persona que llama proporcione una:

irb(main):003:0> h = {:name => "Bob"}
=> {:name=>"Bob"}
irb(main):004:0> [h].flatten
=> [{:name=>"Bob"}]

Esto se puede combinar con una firma de método como muy bien:

def process(*entries)
  [entries].flatten.each do |e|
    # do something with e
  end
end
4
minaguib

Acabo de love la palabra clave en línea rescate como esto:
EJEMPLO EDITADO:

@user #=> nil (but I did't know)
@user.name rescue "Unknown"
link_to( d.user.name, url_user( d.user.id, d.user.name)) rescue 'Account removed'

Esto evita romper mi aplicación y es mucho mejor que la característica lanzada en Rails .try ()

4
Fabiano Soriani

Llamar a un método definido en cualquier lugar de la cadena de herencia, incluso si se invalida

Los objetos de ActiveSupport a veces se enmascaran como objetos integrados.

 requiere 'active_support' 
 days = 5.days 
 days.class # => Fixnum 
 days.is_a? (Fixnum) # => true 
 Fixnum === days # => false (¿huh? ¿Qué es lo que realmente eres?) 
 Object.instance_method (: class) .bind (days) .call # => ActiveSupport :: Duration (¡aha!) 
 ActiveSupport :: Duration === days # => true 

Lo anterior, por supuesto, se basa en el hecho de que active_support no redefine el Object # instance_method, en cuyo caso realmente estaríamos en un arroyo. Una vez más, siempre podríamos guardar el valor de retorno de Object.instance_method (: class) antes de que se cargue cualquier biblioteca de terceros.

Object.instance_method (...) devuelve un UnboundMethod que luego puede enlazar a una instancia de esa clase. En este caso, puede vincularlo a cualquier instancia de Objeto (subclases incluidas).

Si la clase de un objeto incluye módulos, también puede usar UnboundMethod desde esos módulos.

 módulo Mod 
 def var_add (más); @ var + más; end 
 end 
 clase Cla 
 incluye Mod 
 def initialize (var); @ var = var; end 
 # override 
 def var_add (más); @ var + más + más; end 
 end 
 cla = Cla.new ('abcdef') 
 cla.var_add ('ghi') # => "abcdefghighi" 
 Mod.instance_method ( : var_add) .bind (cla) .call ('ghi') # => "abcdefghi" 

Esto incluso funciona para los métodos singleton que anulan un método de instancia de la clase a la que pertenece el objeto.

 clase Foo 
 def mymethod; 'original'; end 
 end 
 foo = Foo.new 
 foo.mymethod # => 'original' 
 def foo.mymethod; 'semifallo'; end 
 foo.mymethod # => 'singleton' 
 Foo.instance_method (: mymethod) .bind (foo) .call # => 'original' 
 
 # También puede llamar al método #instance en clases singleton: 
 Class << foo; yo; end.instance_method (: mymethod) .bind (foo) .call # => 'singleton' 
4
Kelvin

Hay algunos aspectos de los símbolos literales que la gente debería saber. Un caso resuelto por literales de símbolos especiales es cuando necesita crear un símbolo cuyo nombre cause un error de sintaxis por alguna razón con la sintaxis literal de símbolo normal:

:'class'

También puedes hacer interpolación de símbolos. En el contexto de un accessor, por ejemplo:

define_method :"#{name}=" do |value|
  instance_variable_set :"@#{name}", value
end
3
Tom

each_with_index method para cualquier objeto enumerable (array, hash, etc.) ¿quizás?

myarray = ["la", "li", "lu"]
myarray.each_with_index{|v,idx| puts "#{idx} -> #{v}"}

#result:
#0 -> la
#1 -> li
#2 -> lu

Tal vez sea más conocido que otras respuestas, pero no tan conocido por todos los programadores de Ruby :)

3
mhd
class A

  private

  def my_private_method
    puts 'private method called'
  end
end

a = A.new
a.my_private_method # Raises exception saying private method was called
a.send :my_private_method # Calls my_private_method and prints private method called'
2
Chirantan

Ruby tiene un mecanismo call/cc que permite a uno saltar libremente hacia arriba y hacia abajo de la pila.

Un ejemplo simple sigue. Ciertamente, esta no es la forma en que se podría multiplicar una secuencia en Ruby, pero muestra cómo se podría usar call/cc para llegar a la pila y provocar un cortocircuito en un algoritmo. En este caso, estamos multiplicando recursivamente una lista de números hasta que hayamos visto todos los números o veamos cero (los dos casos donde conocemos la respuesta). En el caso cero, podemos ser arbitrariamente profundos en la lista y terminar.

#!/usr/bin/env Ruby

def rprod(k, rv, current, *nums)
  puts "#{rv} * #{current}"
  k.call(0) if current == 0 || rv == 0
  nums.empty? ? (rv * current) : rprod(k, rv * current, *nums)
end

def prod(first, *rest)
  callcc { |k| rprod(k, first, *rest) }
end

puts "Seq 1:  #{prod(1, 2, 3, 4, 5, 6)}"
puts ""
puts "Seq 2:  #{prod(1, 2, 0, 3, 4, 5, 6)}"

Puedes ver la salida aquí:

http://codepad.org/Oh8ddh9e

Para un ejemplo más complejo con continuaciones que se mueven en la otra dirección en la pila, lea la fuente en Generador .

2
Dustin

Acabo de leer todas las respuestas ... una omisión notable fue la tarea de desestructuración:

> (a,b),c = [[1,2],3]
=> [[1,2],3]
> a
=> 1

También funciona para parámetros de bloque. Esto es útil cuando tiene matrices anidadas, cada elemento de las cuales representa algo distinto. En lugar de escribir código como "array [0] [1]", puede dividir esa matriz anidada y dar un nombre descriptivo a cada elemento, en una sola línea de código.

2
Alex D
@user #=> nil (but I did't know)
@user.name rescue "Unknown"
2
haoqi

El atajo de sprintf

Mi función favorita de Ruby. La sintaxis es format_string % argument

"%04d"  % 1         # => "0001"
"%0.2f" % Math::PI  # => "3.14"

Funciona también para matrices (format_string % array_of_arguments)

"%.2f %.3f %.4f" % ([Math::PI]*3) 
# => "3.14 3.142 3.1416"
1
iblue