What does ‘def function=(param)’ mean in ruby? [duplicate]
I am sorry if this is a noob question, but I can’t seem to find the answer neither in stackoverflow, nor anywhere else. The code is:
def full_name=(new_full_name) # stuff end
When I define it in the console, and try to evoke it:
full_name # nil full_name # [hangs, needs ^C] full_name= arg # works like normal function full_name # works again with no arguments, as if I passed the previous argument
So, what is going on? Thanks.
asked Sep 21, 2014 at 14:47
1,522 14 14 silver badges 25 25 bronze badges
Question: what does def foo(param) mean in Ruby?
Sep 21, 2014 at 21:29
‘define function with name foo that accepts one parameter with name param’. What is your point?
Sep 22, 2014 at 11:00
Okay, then: what does def foo=(param) mean in Ruby?
Sep 22, 2014 at 17:51
‘define a function with name foo= that accepts one parameter with name param’. However the function’s behaviour has changed. My question is ‘what happened?’
Sep 22, 2014 at 18:51
1 Answer 1
A method ending with an = defines a setter. When defining a setter like that, ruby will always return the method argument regardless of the body definition when you call the setter.
You can then use object.full_name= «Daniel» which returns => «Daniel»
Some other common appendices with your example:
The question mark, e.g. has_full_name?
is expected to return a boolean value if the object has a full_name
The exclamation mark, e.g. revert_full_name!
is expected to revert the full_name variable of the object
in contrast is expected to return the reverted full_name variable but not to change it in the object
Defining a method
So far you have seen methods that “belong” to objects, or, in other words, are defined on objects, and can be called on objects. E.g. you have seen the method downcase which is defined on every string.
However, Ruby also knows methods that are not defined on any of these objects. They’re sort of “stand alone” methods.
For example, you can try this in irb :
$ irb > is_a?(Object) true
We’ll use this type of methods in this chapter because we want to focus on the characteristics of methods. If you’re curious what’s up with them have a look at the bonus chapter about the top-level object. Later when you learn how to define your own classes we also look at defining methods for these.
Ok, let’s get started.
Suppose we need to define a simple method that takes a number, adds the number 2 to it, and returns the result. Here’s how we can do that:
def add_two(number) number + 2 end
This defines a method. It does not use it, yet: You only build and place that vending machine, so it can be used (by yourself, or others) later.
Let’s walk through this method definition step by step:
Ruby will start reading the code at the top, and find the keyword def . This tells Ruby that we’re about to define a new method.
Methods need a name, so Ruby looks for it next, and finds the word add_two .
Ruby then checks if we define anything to “input” to the method (remember, this is optional). She finds the parentheses, and knows that we’re about to define a list of things that can be given to the method. This list is called an argument list.
An argument list defines names for objects passed to the method, enclosed by parentheses right after the method name.
In our case the argument list has one single argument number , which means our method can accept one single thing (object).
The next line is the block of code that our method has (“encapsulates”). This is also referred to as the method body. In our case that’s just one single line because the operation that our method encapsulates is very simple. Other methods (think of sort , defined on Arrays) would require more code, and are longer.
Inside the method body the arguments are known as local variables: You can see how the code in our method body uses the variable name number .
Finally the keyword end tells Ruby that we’re done with the method body, and the method definition.
All we’ve done so far is defining the method, we haven’t used it for anything, yet. We’ll do that in the next chapter.
On formatting: Note that the keywords def and end sit on the same level, while the method body is indented by two spaces. Also, there are no space before or inside the argument list, i.e. the () parentheses.
Ruby за двадцать минут
Что если мы хотим сказать “Hello” без утомления наших пальцев? Мы должны создать метод!
irb(main):010:0> def h irb(main):011:1> puts "Hello World!" irb(main):012:1> end => nil
Код def h означает начало объявление метода. Он говорит Ruby, что мы определяем метод, имя которого h . Следующая строка – тело метода, та же строка, что мы видели раньше: puts «Hello World» . И, наконец, последняя строка, end , говорит Ruby, что мы завершили объявление метода. Ответ от Ruby, => nil , говорит нам, что он понял, что мы завершили объявление метода.
Кратко о повторяющихся жизнях метода
Теперь давайте попробуем вызвать метод несколько раз:
irb(main):013:0> h Hello World! => nil irb(main):014:0> h() Hello World! => nil
Ну, это было просто. Вызвать метод в Ruby так же просто, как просто упомянуть его имя в коде. Если метод не принимает параметры – это все что вам нужно сделать. Вы можете добавить пустые скобки, если вам так нравится, но это не обязательно.
Что если мы хотим сказать hello одному человеку, а не всему миру? Просто переопределим метод h , чтобы он принимал имя как параметр.
irb(main):015:0> def h(name) irb(main):016:1> puts "Hello #name>!" irb(main):017:1> end => nil irb(main):018:0> h("Matz") Hello Matz! => nil
Итак, это работает… но давайте на секунду задумаемся, что здесь происходит.
Места для интерполяции в String
Что такое # в коде выше? Это способ Ruby вставить что-либо в строку. Кусок кода между фигурными скобками превратится в строку (если это еще не строка) и потом подставится во внешнюю строку в этом месте. Вы также можете использовать это, чтобы убедиться, что имя будет с большой буквы:
irb(main):019:0> def h(name = "World") irb(main):020:1> puts "Hello #name.capitalize>!" irb(main):021:1> end => nil irb(main):022:0> h "chris" Hello Chris! => nil irb(main):023:0> h Hello World! => nil
Вы должны здесь отметить несколько других приемов. Один из них, что мы опять вызываем метод без скобок. Если очевидно то, что вы хотите сделать – скобки не обязательны. Другой прием – это параметр по умолчанию World . Это означает что, “Если имя не передано в качестве параметра, используй параметр по умолчанию World ”.
Эволюционируем в Greeter (Приветствующего)
Что если мы хотим создать реального приветствующего, того кто запомнит ваше имя и поприветствует вас и всегда будет относиться к вам с уважением? Вы можете использовать для этого объект. Давайте создадим класс “Greeter”.
irb(main):024:0> class Greeter irb(main):025:1> def initialize(name = "World") irb(main):026:2> @name = name irb(main):027:2> end irb(main):028:1> def say_hi irb(main):029:2> puts "Hi #@name>!" irb(main):030:2> end irb(main):031:1> def say_bye irb(main):032:2> puts "Bye #@name>, come back soon." irb(main):033:2> end irb(main):034:1> end => nil
Новое ключевое слово здесь — class . Оно служит для объявления нового класса, который мы назвали Greeter и несколько методов для этого класса. Также заметьте @name . Это переменная объекта и она доступна для всех методов в классе. Как вы можете видеть, она используется методами say_hi и say_bye .
Итак, как мы сможем заставить этот класс Greeter работать? Создадим объект.
Начните сейчас, это легко!
- Попробуйте Ruby! (в своем браузере)
- Ruby за двадцать минут
- В Ruby из других языков
Функциональное программирование на Ruby. Часть 2
В первой части мы рассмотрели базовые концепции функционального программирования, познакомились с неизменяемостью и кодом без побочных эффектов.
Во второй части речь пойдет о функциях высшего порядка и каррировании. Но сначала рассмотрим типы функций в Ruby и их особенности.
Методы, Блоки, Proc и Lambda
Ruby поддерживает разные типы функций: блоки, методы, Proc и Lambda.
Первым делом рассмотрим методы. Метод — функция, принадлежащая объекту.
def printer
print "Hey!"
enddef printer
def say_hey
print "Hey!"
end
end
Функция say_hey определена внутри метода printer , но не вызывается из него. Чтобы принудительно вызвать say_hey , нам нужно обратиться к ней через printer .
printer.say_hey
=> "Hey"
Метод printer в этом случае ведет себя как объект с методом say_hey .
Блоки в Ruby — произвольный код, который можно передать любому методу в качестве аргумента. Используя блоки можно легко передавать ссылки на функции. Однострочные блоки обычно заключаются в фигурные скобки <> , многострочные используют конструкцию do . end .
def printer
yield if block_given?
endprinter < puts "1 + 2 = #< 1 + 2 >" >
=> 1 + 2 = 3
На перый взгляд блок похож на урезанный метод. На самом деле блок — синтаксический сахар для proc-объектов. Мы можем убедиться в этом, передав блок в функцию как аргумент, используя амперсанд & .
def show_class(&block)
block.class
endshow_class do
end
=> Proc
Передадим функции printer блок и proc и сравним результат.
printer < puts "1 + 2 = #< 1 + 2 >" >
=> 1 + 2 = 3printer &proc < puts "1 + 2 = #< 1 + 2 >" >
=> 1 + 2 = 3
Вернемся к методам. Методы — тоже частный случай proc. Мы можем передавать методы как блок, используя амперсанд & .
def show_sum
puts "1 + 2 = #< 1 + 2 >"
end
printer &method(:show_sum)
=> 1 + 2 = 3
Последний тип функций, который мы рассмотрим — лямбда-функции. Лямбда-функции — тоже proc-объекты, но имеют две особенности:
- Лямбда-функции проверяют передаваемые аргументы. Если лямбда-функция объявлена с двумя параметрами, то на вход она будет ожидать оба параметра, иначе будет вызвано исключение ArgumentError . Этим лямбда-функции похожи на методы. Proc-объекты не проверяют передаваемые им параметры и если вы передадите один аргумент вместо двух — второй установится как nil.
- лямбды похожи не методы тем, что вызов функции return внутри лямбда-функции вернет управление в родительский метод, который ее вызвал. Вызвов return внутри proc, прерывает родительский метод и после этого выполняет return .
Функции высшего порядка
Если функция принимает другую функцию как аргумент или возвращает функцию как результат, то такая функция является функцией высшего порядка (ФВП).
ФВП часто встречаются среди методов стандартной библиотеки Ruby. Мы уже поняли, что блок — частный случаи функций. Методы inject , collect , map , each принимают на вход блок, а значит являются ФВП.
numbers = [1, 2, 3, 4, 5]
numbers.map < |number| number * number >
=> [1, 4, 9, 16, 25]
Функции, возвращающие другие функции встречаются гораздо реже. Мы можем сами объявить такую функцию используя лямбда или proc-объект.
def adder(a, b)
# lambda
-> < a + b >
endadder_funct = adder(1, 2)
adder_funct.call
=> 3
Есть две техники функционального программирования, для которых мы можем использовать такие функции — частичное применение функции и каррирование.
Частичное применение функции и каррирование
Частичное применение функции — процесс фиксации числа аргументов, который создает другую функцию с меньшим количеством аргументов. Например у нас есть функция, принимающая три аргумента.
proc
Так выглядит частичное применение функции, с фиксированным атрибутом z
proc < |x, y| proc < |z| x + y + z >>
Каррирование — разделение функции, принимающей несколько аргументов, на N функций, принимающих один аргумент.
Каррированная функция выглядит так
proc < |x| proc < |y| proc < |z| x + y + z>> >
В Ruby есть метод Proc#curry . С его помощью мы сможем применить обе техники. Рассмотрим пример программы с арифметическими методами
def add(a, b)
a + b
enddef subtract(a, b)
a - b
enddef multiply(a, b)
a * b
enddef divide(a, b)
a / b
end
Все функции принимают одинаковые аргументы, отличаются только тем, какая операция применяется к a . Применим принцип DRY.
def apply_math(fn, a, b)
a.send(fn, b)
endapply_math(:+, 1, 2)
=> 3
Но теперь мы должны писать имя функции каждый раз. Попробуем применить Proc#curry .
apply_math = -> (fn, a, b) add = apply_math.curry.(:+)
susubtract = apply_math.curry.(:-)
multiply = apply_math.curry.(:*)
divide = apply_math.curry.(:/)add.(1, 2)
=> 3
Теперь код выглядит гораздо лучше. Объявим метод increment с помощью частичного применения функции.
increment = add.curry.(1)increment.(41)
=> 42
В функциональных языках каррирование и частичное применение функции встроено в сам язык. Это очень полезные техники, с их помощью можно разбивать большие и сложные функции на маленькие и простые. Так код становится компактнее, его легче читать, проще тестировать и повторно использовать.
В третьей части мы поговорим про рекурсию и ленивые вычисления в Ruby.
