Buscar..


Introducción

Las funciones en Ruby proporcionan un código organizado y reutilizable para realizar un conjunto de acciones. Las funciones simplifican el proceso de codificación, evitan la lógica redundante y hacen que el código sea más fácil de seguir. Este tema describe la declaración y la utilización de funciones, argumentos, parámetros, declaraciones de rendimiento y alcance en Ruby.

Observaciones

Un método es un bloque de código con nombre, asociado con uno o más objetos y generalmente identificado por una lista de parámetros además del nombre.

def hello(name)
  "Hello, #{name}"
end

La invocación de un método especifica el nombre del método, el objeto sobre el que se invoca (a veces se denomina receptor) y cero o más valores de argumento que se asignan a los parámetros del método nombrado. El valor de la última expresión evaluada en el método se convierte en el valor de la expresión de invocación del método.

hello("World")
# => "Hello, World"

Cuando el receptor no es explícito, es self .

self
# => main

self.hello("World")
# => "Hello, World"

Como se explica en el libro de lenguaje de programación Ruby , muchos idiomas distinguen entre funciones, que no tienen un objeto asociado, y métodos, que se invocan en un objeto receptor. Debido a que Ruby es un lenguaje puramente orientado a objetos, todos los métodos son verdaderos métodos y están asociados con al menos un objeto.

Resumen de los parámetros del método

Tipo Método Firma Ejemplo de llamada Asignaciones
R equipara def fn(a,b,c) fn(2,3,5) a=2, b=3, c=5
V ariadic def fn(*rest) fn(2,3,5) rest=[2, 3, 5]
D efault def fn(a=0,b=1) fn(2,3) a=2, b=3
K palabra clave def fn(a:0,b:1) fn(a:2,b:3) a=2, b=3

Estos tipos de argumentos pueden combinarse en prácticamente cualquier forma que pueda imaginar para crear funciones variables. El número mínimo de argumentos para la función será igual a la cantidad de argumentos requeridos en la firma. Los argumentos adicionales se asignarán a los parámetros predeterminados primero, luego al *rest parámetros.

Tipo Método Firma Ejemplo de llamada Asignaciones
R, D, V, R def fn(a,b=1,*mid,z) fn(2,97) a=2, b=1, mid=[], z=97
fn(2,3,97) a=2, b=3, mid=[], z=97
fn(2,3,5,97) a=2, b=3, mid=[5], z=97
fn(2,3,5,7,97) a=2, b=3, mid=[5,7], z=97
R, K, K def fn(a,g:6,h:7) fn(2) a=2, g=6, h=7
fn(2,h:19) a=2, g=6, h=19
fn(2,g:17,h:19) a=2, g=17, h=19
VK def fn(**ks) fn(a:2,g:17,h:19) ks={a:2, g:17, h:19}
fn(four:4,five:5) ks={four:4, five:5}

Solo parámetro requerido

def say_hello_to(name)
    puts "Hello #{name}"
end

say_hello_to('Charles')    # Hello Charles

Múltiples parámetros requeridos

def greet(greeting, name)
    puts "#{greeting} #{name}"
end

greet('Hi', 'Sophie')    # Hi Sophie

Parámetros por defecto

def make_animal_sound(sound = 'Cuack')
    puts sound
end

make_animal_sound('Mooo') # Mooo
make_animal_sound         # Cuack

Es posible incluir valores predeterminados para múltiples argumentos:

def make_animal_sound(sound = 'Cuack', volume = 11)
    play_sound(sound, volume)
end

make_animal_sound('Mooo') # Spinal Tap cow

Sin embargo, no es posible suministrar el segundo sin suministrar también el primero. En lugar de usar parámetros posicionales, pruebe los parámetros de palabras clave:

def make_animal_sound(sound: 'Cuack', volume: 11)
    play_sound(sound, volume)
end

make_animal_sound(volume: 1) # Duck whisper

O un parámetro hash que almacena opciones:

def make_animal_sound(options = {})
    options[:sound]  ||= 'Cuak'
    options[:volume] ||= 11
    play_sound(sound, volume)
end

make_animal_sound(:sound => 'Mooo') 

Los valores predeterminados de los parámetros pueden ser establecidos por cualquier expresión ruby. La expresión se ejecutará en el contexto del método, por lo que incluso puede declarar variables locales aquí. Tenga en cuenta, no obtendrá a través de la revisión de código. Cortesía de Caius por señalar esto .

def make_animal_sound( sound = ( raise 'TUU-too-TUU-too...' ) ); p sound; end

make_animal_sound 'blaaaa' # => 'blaaaa'
make_animal_sound          # => TUU-too-TUU-too... (RuntimeError)

Parámetros opcionales (operador splat)

def welcome_guests(*guests)
    guests.each { |guest| puts "Welcome #{guest}!" }
end

welcome_guests('Tom')    # Welcome Tom!
welcome_guests('Rob', 'Sally', 'Lucas') # Welcome Rob!
                                        # Welcome Sally!
                                        # Welcome Lucas!

Tenga en cuenta que welcome_guests(['Rob', 'Sally', 'Lucas']) emitirán Welcome ["Rob", "Sally", "Lucas"]!
En cambio, si tiene una lista, puede hacer welcome_guests(*['Rob', 'Sally', 'Lucas']) y eso funcionará como welcome_guests('Rob', 'Sally', 'Lucas') .

Mezcla opcional de parámetros por defecto requerida

def my_mix(name,valid=true, *opt)
    puts name
    puts valid
    puts opt
end

Llame de la siguiente manera:

my_mix('me')
# 'me'
# true
# []

my_mix('me', false)
# 'me'
# false
# []

my_mix('me', true, 5, 7) 
# 'me'
# true
# [5,7]

Las definiciones de los métodos son expresiones

La definición de un método en Ruby 2.x devuelve un símbolo que representa el nombre:

class Example
  puts def hello
  end
end

#=> :hello

Esto permite interesantes técnicas de metaprogramación. Por ejemplo, los métodos pueden ser envueltos por otros métodos:

class Class
  def logged(name)
    original_method = instance_method(name)
    define_method(name) do |*args|
      puts "Calling #{name} with #{args.inspect}."
      original_method.bind(self).call(*args)
      puts "Completed #{name}."
    end
  end
end

class Meal
  def initialize
    @food = []
  end
  
  logged def add(item)
    @food << item
  end
end

meal = Meal.new
meal.add "Coffee"
# Calling add with ["Coffee"].
# Completed add.

Capturando argumentos de palabras clave no declarados (doble splat)

El operador ** funciona de manera similar al operador * , pero se aplica a los parámetros de palabras clave.

def options(required_key:, optional_key: nil, **other_options)
  other_options
end

options(required_key: 'Done!', foo: 'Foo!', bar: 'Bar!')
#> { :foo => "Foo!", :bar => "Bar!" }

En el ejemplo anterior, si no se utilizan las **other_options se **other_options un ArgumentError: unknown keyword: foo, bar error de ArgumentError: unknown keyword: foo, bar .

def without_double_splat(required_key:, optional_key: nil)
  # do nothing
end

without_double_splat(required_key: 'Done!', foo: 'Foo!', bar: 'Bar!')
#> ArgumentError: unknown keywords: foo, bar

Esto es útil cuando tiene un hash de opciones que quiere pasar a un método y no quiere filtrar las claves.

def options(required_key:, optional_key: nil, **other_options)
  other_options
end

my_hash = { required_key: true, foo: 'Foo!', bar: 'Bar!' }

options(my_hash)
#> { :foo => "Foo!", :bar => "Bar!" }

También es posible desempaquetar un hash usando el operador ** . Esto le permite suministrar palabras clave directamente a un método además de los valores de otros hashes:

my_hash = { foo: 'Foo!', bar: 'Bar!' }

options(required_key: true, **my_hash)
#> { :foo => "Foo!", :bar => "Bar!" }

Cediendo a bloques

Puede enviar un bloque a su método y puede llamar a ese bloque varias veces. Esto se puede hacer enviando un proc / lambda o similar, pero es más fácil y más rápido con el yield :

def simple(arg1,arg2)
  puts "First we are here:  #{arg1}"
  yield
  puts "Finally we are here:  #{arg2}"
  yield
end
simple('start','end') { puts "Now we are inside the yield" }

#> First we are here:  start
#> Now we are inside the yield
#> Finally we are here:  end
#> Now we are inside the yield

Tenga en cuenta que { puts ... } no está dentro de los paréntesis, implícitamente viene después. Esto también significa que solo podemos tener un bloque de yield . Podemos pasar argumentos al yield :

def simple(arg)
  puts "Before yield"
  yield(arg)
  puts "After yield"
end
simple('Dave') { |name| puts "My name is #{name}" }

#> Before yield
#> My name is Dave
#> After yield

Con el rendimiento podemos hacer fácilmente iteradores o cualquier función que funcione en otro código:

def countdown(num)
  num.times do |i|
    yield(num-i)
  end
end

countdown(5) { |i| puts "Call number #{i}" }

#> Call number 5
#> Call number 4
#> Call number 3
#> Call number 2
#> Call number 1

De hecho, es con yield que cosas como foreach , each y each times se implementan generalmente en las clases.

Si desea averiguar si le han dado un bloqueo o no, use block_given? :

class Employees
  def names
    ret = []
    @employees.each do |emp|
      if block_given?
        yield(emp.name)
      else
        ret.push(emp.name) 
      end
    end

    ret
  end
end

Este ejemplo asume que la clase Employees tiene una lista de @employees que se puede iterar con each para obtener objetos que tienen nombres de empleados usando el método del name . Si nos dan un bloque, entonces vamos a yield el nombre al bloque, de lo contrario, simplemente empujar a una matriz que volvamos.

Argumentos de la tupla

Un método puede tomar un parámetro de matriz y destruirlo inmediatamente en variables locales nombradas. Encontrado en el blog de Mathias Meyer .

def feed( amount, (animal, food) )

    p "#{amount} #{animal}s chew some #{food}"

end

feed 3, [ 'rabbit', 'grass' ] # => "3 rabbits chew some grass"

Definiendo un método

Los métodos se definen con la palabra clave def , seguidos del nombre del método y una lista opcional de nombres de parámetros entre paréntesis. El código de Ruby entre def y end representa el cuerpo del método.

def hello(name)
  "Hello, #{name}"
end

La invocación de un método especifica el nombre del método, el objeto sobre el que se invoca (a veces se denomina receptor) y cero o más valores de argumento que se asignan a los parámetros del método nombrado.

hello("World")
# => "Hello, World"

Cuando el receptor no es explícito, es self .

Los nombres de los parámetros se pueden usar como variables dentro del cuerpo del método, y los valores de estos parámetros nombrados provienen de los argumentos a la invocación de un método.

hello("World")
# => "Hello, World"
hello("All")
# => "Hello, All"

Usa una función como bloque.

Muchas funciones en Ruby aceptan un bloque como argumento. P.ej:

[0, 1, 2].map {|i| i + 1}
 => [1, 2, 3]

Si ya tiene una función que hace lo que quiere, puede convertirla en un bloque usando &method(:fn) :

def inc(num)
   num + 1
end

[0, 1, 2].map &method(:inc)
 => [1, 2, 3]


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow