Ricerca…


introduzione

Le funzioni in Ruby forniscono un codice organizzato e riutilizzabile per preformare un insieme di azioni. Le funzioni semplificano il processo di codifica, impediscono la logica ridondante e rendono il codice più facile da seguire. Questo argomento descrive la dichiarazione e l'utilizzo di funzioni, argomenti, parametri, dichiarazioni di rendimento e ambito in Ruby.

Osservazioni

Un metodo è un blocco di codice denominato, associato a uno o più oggetti e generalmente identificato da un elenco di parametri oltre al nome.

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

Un richiamo di metodo specifica il nome del metodo, l'oggetto su cui deve essere richiamato (talvolta chiamato il ricevente) e zero o più valori dell'argomento che sono assegnati ai parametri del metodo named. Il valore dell'ultima espressione valutata nel metodo diventa il valore dell'espressione di chiamata del metodo.

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

Quando il ricevitore non è esplicito, è un self .

self
# => main

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

Come spiegato nel libro Ruby Programming Language , molte lingue distinguono tra funzioni, che non hanno oggetti associati e metodi, che sono invocati su un oggetto ricevente. Poiché Ruby è un linguaggio puramente orientato agli oggetti, tutti i metodi sono metodi veri e sono associati ad almeno un oggetto.

Panoramica dei parametri del metodo

genere Metodo Signature Esempio di chiamata assegnazioni
R equo 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 eyword def fn(a:0,b:1) fn(a:2,b:3) a=2, b=3

Questi tipi di argomenti possono essere combinati praticamente in ogni modo possibile per creare funzioni variadiche. Il numero minimo di argomenti per la funzione sarà uguale alla quantità di argomenti richiesti nella firma. Gli argomenti extra verranno assegnati prima ai parametri predefiniti, quindi al parametro *rest .

genere Metodo Signature Esempio di chiamata assegnazioni
R, S, 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}

Singolo parametro richiesto

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

say_hello_to('Charles')    # Hello Charles

Più parametri richiesti

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

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

Parametri di default

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

make_animal_sound('Mooo') # Mooo
make_animal_sound         # Cuack

È possibile includere i valori predefiniti per più argomenti:

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

make_animal_sound('Mooo') # Spinal Tap cow

Tuttavia, non è possibile fornire il secondo senza fornire anche il primo. Invece di utilizzare i parametri posizionali, prova i parametri delle parole chiave:

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

make_animal_sound(volume: 1) # Duck whisper

O un parametro hash che memorizza le opzioni:

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

make_animal_sound(:sound => 'Mooo') 

I valori dei parametri predefiniti possono essere impostati da qualsiasi espressione di ruby. L'espressione verrà eseguita nel contesto del metodo, quindi puoi anche dichiarare le variabili locali qui. Nota, non passerà attraverso la revisione del codice. Per gentile concessione di caius per averlo indicato .

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)

Parametro opzionale (operatore 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!

Nota che welcome_guests(['Rob', 'Sally', 'Lucas']) mostrerà Welcome ["Rob", "Sally", "Lucas"]!
Invece, se hai una lista, puoi fare welcome_guests(*['Rob', 'Sally', 'Lucas']) e funzionerà come welcome_guests('Rob', 'Sally', 'Lucas') .

Mix di parametri facoltativo predefinito richiesto

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

Chiama come segue:

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

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

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

Le definizioni di metodo sono espressioni

La definizione di un metodo in Ruby 2.x restituisce un simbolo che rappresenta il nome:

class Example
  puts def hello
  end
end

#=> :hello

Ciò consente interessanti tecniche di metaprogrammazione. Ad esempio, i metodi possono essere impacchettati con altri metodi:

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.

Catturare argomenti di parole chiave non dichiarate (double splat)

L'operatore ** funziona in modo simile all'operatore * ma si applica ai parametri delle parole chiave.

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

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

Nell'esempio precedente, se non si utilizza **other_options viene **other_options un ArgumentError: unknown keyword: foo, bar verrebbe generato ArgumentError: unknown keyword: foo, bar errore nella 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

Questo è utile quando hai un hash di opzioni che vuoi passare ad un metodo e non vuoi filtrare le chiavi.

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!" }

È anche possibile decomprimere un hash utilizzando l'operatore ** . Ciò consente di fornire parole chiave direttamente a un metodo oltre ai valori di altri hash:

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

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

Cedendo ai blocchi

Puoi inviare un blocco al tuo metodo e può chiamare quel blocco più volte. Questo può essere fatto inviando un proc / lambda o tale, ma è più facile e veloce con 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

Nota che { puts ... } non è all'interno delle parentesi, viene implicitamente dopo. Questo significa anche che possiamo avere solo un blocco di yield . Possiamo passare argomenti alla 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 yield possiamo facilmente fare iteratori o funzioni che funzionano su altro codice:

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

In effetti, è con il yield che cose come foreach , each e le times sono generalmente implementate nelle classi.

Se vuoi scoprire se ti è stato dato un blocco o no, usa 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

In questo esempio si presuppone che la classe Employees abbia un elenco @employees che può essere iterato con each per ottenere oggetti che hanno nomi di dipendenti che utilizzano il metodo name . Se ci viene dato un blocco, poi ci yield il nome al blocco, altrimenti basta spingerlo a una matrice che ci ritorno.

Tuple Arguments

Un metodo può prendere un parametro di matrice e destrutturarlo immediatamente in variabili locali con nome. Trovato sul blog di Mathias Meyer .

def feed( amount, (animal, food) )

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

end

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

Definire un metodo

I metodi sono definiti con la parola chiave def , seguita dal nome del metodo e da un elenco opzionale di nomi parametro tra parentesi. Il codice Ruby tra def e end rappresenta il corpo del metodo.

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

Un richiamo di metodo specifica il nome del metodo, l'oggetto su cui deve essere richiamato (talvolta chiamato il ricevente) e zero o più valori dell'argomento che sono assegnati ai parametri del metodo named.

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

Quando il ricevitore non è esplicito, è un self .

I nomi dei parametri possono essere utilizzati come variabili all'interno del corpo del metodo e i valori di questi parametri denominati derivano dagli argomenti in una chiamata di metodo.

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

Utilizzare una funzione come blocco

Molte funzioni in Ruby accettano un blocco come argomento. Per esempio:

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

Se hai già una funzione che fa quello che vuoi, puoi trasformarlo in un blocco 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
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow