Защо:
[1,2,3,4,5].map(&:to_s) #=> ["1", "2", "3", "4", "5"]
работа, но:
[1,2,3,4,5].map(&:*(2))
хвърля неочаквана синтактична грешка?
Отговори:
3 за отговор № 1&
се нарича to_proc
оператор. Той нарича to_proc
метод на израза, който го следва и след това предава получения Proc на метода като блок.
В случай че &:to_s
, :to_s
е символ, така че операторът призовава Symbol#to_proc
, Документите са малко объркани, но е достатъчно да се каже, че тези два израза са повече или по-малко еквивалентни:
my_proc = :to_s.to_proc
my_proc = Proc.new {|obj| obj.to_s }
Така че отговорът на въпроса "Защо не &:*(2)
работа "? е, че изразът, който следва &
оператор, :*(2)
, не е валиден израз на Ruby.Това прави толкова смисъл за Ruby parser като "hello"(2)
.
Между другото, има начин да направите това, което се опитвате да направите:
[1,2,3,4,5].map(&2.method(:*))
# => [2, 4, 6, 8, 10]
В горния код, 2.method(:*)
се връща препратка към *
метод на обекта 2
като метод обект. Предметите на метода се държат много като Proc обекти и отговарят на тях to_proc
, Въпреки това, горното не е точно еквивалентно - това е така 2 * n
отколкото n * 2
(разлика, че няма значение дали n
също е числено) и не е по-кратко и разбираемо от това {|n| n * 2 }
, и рядко си струва неприятностите.
-1 за отговор № 2
Ampersand и обект (&: метод)
Операторът & също може да се използва за пренасяне на обект като блок на метод, както е показано в следния пример:
arr = [ 1, 2, 3, 4, 5 ]
arr.map { |n| n.to_s }
arr.map &:to_s
И двата примера имат същия резултат. И в двата метода на картата се използва масив arr и блок, след което той изпълнява блока на всеки елемент от масива. Кодът вътре в блока върви към_s на всеки елемент, превръщайки го от цели числа в низове. След това, методът на картата връща нов масив, съдържащ конвертираните елементи.
Първият пример е общ и широко използван. Вторият пример може на пръв поглед да изглежда малко загадъчен. Нека да видим какво се случва:
В Ruby елементите, настроени с двоеточие (:) са символи. Ако не сте запознати с класа / типа на символите, ви предлагам да го намерите в Google и да прочетете няколко статии, преди да продължите. Всички имена на методите в Ruby са вътрешно съхранени като символи. Чрез префиксиране на име на метода с двоеточие, ние не превръщаме метода в символ, нито наричаме метода, ние просто предаваме името на метода наоколо (позовавайки се на метода). В горния пример минаваме: to_s, който е препратка към метода to_s, към оператора ampersand (&), който ще създаде proc (като се обади на to_proc под капака). Процесът приема стойност като аргумент, извиква към него и връща стойността, превърната в низ.
Въпреки че :to_s символът е винаги еднакъв, когато изпълнявате схемата на картата, тя ще се отнася до метода to_s на класа, съответстващ на всеки елемент от масива. Ако преминем към масив като [21, 4.453,: foobar], методът to_s на класа Fixnum ще бъде приложен (наречен) на първия елемент, методът to_s на класа Float ще бъде приложен към вторият елемент и методът to_s на класа Символ ще бъдат приложени към третия елемент. Това има смисъл, защото не предаваме действителния метод to_s на оператора ampersand, само името му.
По-долу е даден пример за създаване на прок, който взема аргумент, извиква метод върху него и връща резултата от метода.
p = :upcase.to_proc
p.call("foo bar")
Output:
=> "FOO BAR"
Нека да прегледаме какво се случва в arr.map &: to_s
- При всяка итерация на карта един елемент от масива (цяло число) се предава на &: to_s
- В:to_s символ (който е препратка към метода to_s) се предава на оператора &, който създава proc, който ще вземе аргумент (елемент от масив), извика to_s върху аргумента и връща стойността, превърната в низ;
- Методът на картата връща нов масив, съдържащ низовете "1", "2", "3", "4" и "5".