Szukaj…


Wprowadzenie

Funkcje w Rubim zapewniają zorganizowany kod wielokrotnego użytku, aby wykonać zestaw działań. Funkcje upraszczają proces kodowania, zapobiegają nadmiarowej logice i ułatwiają śledzenie kodu. W tym temacie opisano deklarację i wykorzystanie funkcji, argumentów, parametrów, instrukcji return i zakresu w Ruby.

Uwagi

Metoda to nazwany blok kodu, powiązany z jednym lub większą liczbą obiektów i ogólnie identyfikowany przez listę parametrów oprócz nazwy.

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

Wywołanie metody określa nazwę metody, obiekt, na który ma zostać wywołany (czasami nazywany odbiornikiem), oraz zero lub więcej wartości argumentów przypisanych do nazwanych parametrów metody. Wartość ostatniego wyrażenia ocenianego w metodzie staje się wartością wyrażenia wywołania metody.

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

Kiedy odbiorca nie jest jawny, jest self .

self
# => main

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

Jak wyjaśniono w książce Ruby Programming Language , wiele języków rozróżnia funkcje, które nie mają powiązanego obiektu, oraz metody, które są wywoływane na obiekcie odbiorcy. Ponieważ Ruby jest językiem czysto obiektowym, wszystkie metody są prawdziwymi metodami i są powiązane z co najmniej jednym obiektem.

Przegląd parametrów metody

Rodzaj Podpis metody Przykład połączenia Przydziały
R equired 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

Te typy argumentów można łączyć praktycznie w dowolny sposób, jaki można sobie wyobrazić w celu tworzenia funkcji variadic. Minimalna liczba argumentów funkcji będzie równa liczbie wymaganych argumentów w podpisie. Dodatkowe argumenty zostaną przypisane najpierw do parametrów domyślnych, a następnie do parametru *rest .

Rodzaj Podpis metody Przykład połączenia Przydziały
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}

Jeden wymagany parametr

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

say_hello_to('Charles')    # Hello Charles

Wiele wymaganych parametrów

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

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

Parametry domyślne

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

make_animal_sound('Mooo') # Mooo
make_animal_sound         # Cuack

Możliwe jest dołączenie wartości domyślnych dla wielu argumentów:

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

make_animal_sound('Mooo') # Spinal Tap cow

Jednak nie jest możliwe dostarczenie drugiego bez dostarczenia pierwszego. Zamiast używać parametrów pozycyjnych, wypróbuj parametry słów kluczowych:

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

make_animal_sound(volume: 1) # Duck whisper

Lub parametr skrótu przechowujący opcje:

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

make_animal_sound(:sound => 'Mooo') 

Domyślne wartości parametrów można ustawić za pomocą dowolnego wyrażenia ruby. Wyrażenie będzie działać w kontekście metody, więc możesz nawet zadeklarować tutaj zmienne lokalne. Uwaga: nie przejdzie przeglądu kodu. Dzięki uprzejmości kaiusa za zwrócenie na to uwagi .

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)

Opcjonalne parametry (operator ikony)

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!

Zauważ, że welcome_guests(['Rob', 'Sally', 'Lucas']) wyświetli Welcome ["Rob", "Sally", "Lucas"]!
Zamiast tego, jeśli masz listę, możesz zrobić welcome_guests(*['Rob', 'Sally', 'Lucas']) a to zadziała jako welcome_guests('Rob', 'Sally', 'Lucas') .

Wymagany domyślny opcjonalny miks parametrów

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

Zadzwoń w następujący sposób:

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

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

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

Definicje metod są wyrażeniami

Definiowanie metody w Ruby 2.x zwraca symbol reprezentujący nazwę:

class Example
  puts def hello
  end
end

#=> :hello

Pozwala to na ciekawe techniki metaprogramowania. Na przykład metody można łączyć innymi metodami:

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.

Przechwytywanie niezadeklarowanych argumentów słów kluczowych (podwójna ikona)

Operator ** działa podobnie do operatora * , ale dotyczy parametrów słów kluczowych.

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

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

W powyższym przykładzie, jeśli nie zostanie użyta **other_options ArgumentError: unknown keyword: foo, bar zostanie podniesione ArgumentError: unknown keyword: foo, bar błąd 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

Jest to przydatne, gdy masz skrót opcji, które chcesz przekazać do metody i nie chcesz filtrować kluczy.

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

Możliwe jest również rozpakowanie skrótu za pomocą operatora ** . Pozwala to na dostarczenie słowa kluczowego bezpośrednio do metody oprócz wartości z innych skrótów:

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

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

Uleganie blokom

Możesz wysłać blok do metody, która może wywołać ten blok wiele razy. Można to zrobić wysyłając proc / lambda lub podobny, ale jest to łatwiejsze i szybsze z 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

Zauważ, że { puts ... } nie znajduje się w nawiasach, domyślnie następuje. Oznacza to również, że możemy mieć tylko jeden blok yield . Możemy przekazać argumenty do 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

Z wydajnością możemy łatwo tworzyć iteratory lub dowolne funkcje działające na innym kodzie:

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

W rzeczywistości z yield rzeczy takie jak foreach each times generowane w klasach.

Jeśli chcesz się dowiedzieć, czy dostałeś blok, czy nie, użyj 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

W tym przykładzie założono, że klasa Employees ma listę @employees którą można iterować z each z each aby uzyskać obiekty o nazwach pracowników przy użyciu metody name . Jeśli podano blok, a następnie będziemy yield nazwę do bloku, w przeciwnym razie po prostu wcisnąć go do tablicy, że wracamy.

Argumenty krotkie

Metoda może pobrać parametr tablicy i natychmiast go zniszczyć na nazwane zmienne lokalne. Znaleziono na blogu Mathiasa Meyera .

def feed( amount, (animal, food) )

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

end

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

Definiowanie metody

Metody są definiowane za pomocą słowa kluczowego def , po którym następuje nazwa metody i opcjonalna lista nazw parametrów w nawiasach. Kod Ruby między def a end reprezentuje treść metody.

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

Wywołanie metody określa nazwę metody, obiekt, na który ma zostać wywołany (czasami nazywany odbiornikiem), oraz zero lub więcej wartości argumentów przypisanych do nazwanych parametrów metody.

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

Kiedy odbiorca nie jest jawny, jest self .

Nazwy parametrów mogą być używane jako zmienne w treści metody, a wartości tych nazwanych parametrów pochodzą od argumentów do wywołania metody.

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

Użyj funkcji jako bloku

Wiele funkcji w Ruby akceptuje blok jako argument. Na przykład:

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

Jeśli masz już funkcję, która robi to, co chcesz, możesz przekształcić ją w blok za pomocą &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
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow