среда, 14 марта 2012 г.

Ruby Methods

1. Методы определяются ключевыми словами def... end. По названии понятно что метод должен принадлежать какому обьекту. Но мы можем определять метод, не заботясь о том, чтобы его окружало обьявление какого-то класса(тоесть писать функциональную программу), по той причине, что в руби все обьекты, и само приложение тоже является обьектом, поэтому обьявленные вне какого-либо класса методы просто попадают в обьет программы.


2. Методы-предикаты - это методы которые возвращают булиновое значение. В Java такие методы имеют приставку is: isEnabled. В руби же принято ставить таким методам знак вопроса в конце
if arr.empty?

...

end


3. Методы со знаком восклицания. Суть их в том, что они применяют какие-то изменения непосредственно на собственный обьект, а не на его клон который возвращается из них return-ом.



1. Методы можно вызывать без скобок вокруг параметров, если на результат не нужно применить его метод:

results = method_name parameter1, parameter2         
#but
results = method_name(parameter1, parameter2).reverse

2. Возращение из метода.
Это может быть явный return или же результат последней строки метода без этого ключевого слова.


3. * (asterisk)
Первый случай(когда он стоит перед последним параметром функции):

def func(arg1, arg2, *others)

  ... # функция с переменным числом аргументов, которые доступны

  ... #внутри функции через массив others

def 



Второй:

args = ['a', 'b', 'c']

my_func(*args) # equal to my_func('a', 'b', 'c') 



4. & (ampersand)
Может стоять возле последнего параметра функции и означает, что функция в этом месте ожидает блок кода.

 Понимание Procs, blocks и methods.
Methods - это обьект-функция который принадледит какому-то классу, тоесть инстанция такого метода умеет реагировать на отправку ему события с таким именем.

Procs - это обьект-функция(fanctor), который принадлежит переменной. В совокупности со скоупом в который он входит мы получаем замыкание.

Blocks - это Прос в зачатосном состоянии, тоесть он не принадлежит в поточным помент не одной переменной.

Просы можно создавать двумя способами, первый более устаревший, и по скольку между ними все-таки есть определенные отличия, которые я упомяну чуть ниже, предпочтительнее использовать второй способ. 1-й:
pnew = Proc.new {|x, y| puts x + y}
pnew.call(2, 4)
2-й:
lamb = lambda {|x, y| puts x + y}
lamb.call(2, 4)
Второй способ - это использование ядрового метода lambda, который внтри делает тоже самое что в первом методе. Но все-таки есть отличия:

  1. При несоответсвии к-ва методов при вызове фанктора произойдет ошибка(в случае прямого создания проса, лишние аргументы проигнорируются)
  2. При вызове проса, созданном по-старинке, если в нем есть return, то он бедет ретурном и для метода который обворачивает вызов фанктора. Лямбда же создает Прос так, что ретурн будет касаться только его. Вот именно эта характеристика приводит к тому, что пользоваться стоит только лямбда-созданием.
Фанкторы в руби -это обьекты первого-класса, они умеют создаваться во время исполнения, сохраняться в структурах, передаваться параметром функицям, возвращаться из ф-й.

Блоки не могут существовать сами по себе. Они могут передаваться последним параметром в фунцию, даже если явно не были указаны последним параметром(явно они указываться с помощью амперсанда) при декларации функции. Руби автоматически создает прос из такого блока без имени, а к нему пожно обратиться в функции через оператор yield.
Если же нам нужно передать в функцию не один фанктор, а неслько, тогда мы должны передать фанкторы как обычные переменные:
def do_twice(what1, what2, what3)
  2.times do
    what1.call
    what2.call
    what3.call
  end
end
 
do_twice( lambda {print "Hola, "},
          lambda {print "querido "},
          lambda {print "amigo\n"})

 Важно помнить что в случае & возле последнего параметра функции в декларации, или просто без указания этого последнего параметра, функция ждет именно блок, но никак не прос:

  def contrived(a, &f)
      # the block can be accessed through f
      f.call(a)
 
      # but yield also works !
      yield(a)
  end
 
  # this works
  contrived(25) {|x| puts x}
 
  # this raises ArgumentError, because &f 
  # isn't really an argument - it's only there 
  # to convert a block
  contrived(25, lambda {|x| puts x})

Но что делать если у нас есть прос, который нужно передать в такой метод, который ожидает блок? Все просто нужно прос преобразовать до первозданного вида - к блоку. Это опять же делается с помощью амперсанда, но уже не в декларации, а непосредственно возле аргумента в момент вызова:
print "(t)imes or (p)lus: "
  times = gets
  print "number: "
  number = Integer(gets)
  if times =~ /^t/
      calc = lambda {|n| n*number }
  else
      calc = lambda {|n| n+number }
  end
  puts((1..10).collect(&calc).join(", "))

Если кстати мы пытаемся передать вместо блока в функцию какой-нибудь обьект, то руби помытается выполнить метод to_proc - something.send(:to_proc)

Динамические методы обьектов
Создаются двумя способами.
1-й. В этом случае переменные поточного скоупа(доступные в момент создания метода), также доступны на момент вызова метода:

a = 'b'
def a.some_method
  'within a singleton method just for a'
end
a.some_method # => 'within a singleton method just for a'
2-й. В этом случае переменные не доступны:
a = 'b'
a.class.send(:define_method, :some_method) { # only available to classes, unfortunately
  'within a block method'
}
a.some_method

Комментариев нет:

Отправить комментарий