Ampersand (&) v detailech
Operátor apersand může být užívaný pro explicitní přeměnu mezi bloky a Procesy v několika případech. Je o něco těžší porozumět jak toto funguje.
Pamatujte si jak už jsem říkal že ačkoliv připojený blok je převedený na Proces pod pokličkou, není přístupný jako Proces zevnitř metody? Takže, jestli značka předchazí poslednímu argumentu ve výchozím seznamu metody, blok napojený na tuto metodu je převedený na Proces objekt a je přiřazený k poslednímu argumentu:
def contrived(a, &f)
# blok je pristupny pres f
f.call(a)
# ale yield take funguje !
yield(a)
end
# toto funguje
contrived(25) {|x| puts x}
# toto nevyhodi (ArgumentError), kvuli &f
# neni to skutecny argument argument - je zde jenom
# pro prevedeni bloku
contrived(25, lambda {|x| puts x})
Další (IMHO daleko víc efektivní) použití značky je jiný způsob konverze - převádění Proces do bloku. Toto je velmi užitečné kvůli velkému množství Ruby zabudovaných částí, a zvláště iterátorů, které očekávají blok jak argument, a někdy to je mnohem lepší předat jim Proces. Následující příklad je vzatý přímo z vynikající "Programování v Ruby" knihy od pragmatických programátorů:
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(", "))
Sběrná metoda očekává blok, ale v tomto případě se přímo nabízí poskytnutí Procesu, od doby kdy je zkonstruován Proces s použitím znalostí získaných od uživatelů. Pomocí ampersandu se ujistíme, že Proces je změněný na kódový blok a je svolný k tomu, aby se choval jako připojený blok.
Ampersand také dovoluje implementaci velmi běžného stylu programování mezi Ruby programátory. Předpokládejte, že chci přeměnit všechny písmena ve slovech v poli na velká písmena. Mohu to udělat takto:
slova = %w(Jana, aara, multiko)
upcase_slova = slova.map {|x| x.upcase}
p upcase_slova
Toto je hezké, a funguje, ale asi cítíte, že je to trochu příliš mnohomluvné. Upcase metoda by měla sama mapovat, bez potřeby pro separátní blok a zřejmě přebytečný x argument. Naštěstí, jako jsme viděli předtím, Ruby podporuje styl poslání zpráv objektu, a metody mohou být uváděny jejich jmény, která jsou implementována jako Ruby Symbols. Například:
p "Erik".send(:upcase)
Toto doslova říká pošli zprávu/metodu vzhůru do objektu "Erik". Tento rys může být využitý k tomu, aby realizoval mapu {|x| x.upcase} vkusným způsobem, a my se chystáme užívat značku právě kvůli tomu! Jak už jsem řekl, když ampersand předchází nějakému Procesu v metodě volání, to přeměňuje Proc na blok. Ale co kdyby to nebyl Proces, ale jiný objekt? Pak, Rubyovy zahrnutá pravidla konverzi spustí to_proc metodu a zkusí z něho udělat Proces. Můžeme užívat toto to_proc také pro Symbol a dosáhne co my chceme:
class Symbol
# Zakladni prevedeni nazvu metody
# na proces ktery spusti tuto metodu.
#
def to_proc
lambda {|x, *args| x.send(self, *args)}
end
end
# Voala !
slova = %w(Jana, aara, multiko)
upcase_slova = slova.map(&:upcase)
Závěr
Ruby ve skutečnosti nemá funkce. Avšak, má dva mírně rozdílné koncepty - metody a Procesy (které jak my jsme viděli, jednodušší stejně jako další jazyky volají funkční objekty, nebo funktory). Oba jsou bloky kódu - metody jsou vázané na Objects, a Procesy jsou vázany lokální proměnnou v rozsahu. Jejich použití je docela různé.
Metody jsou základní kámen objektově orientovaného programování, a od té doby, co je Ruby čistě OO jazyk (všechno je objekt), metody jsou tkvící v povaze Ruby. Metody jsou akce Ruby vytvářející zprávy, které přijmou, pokud dáváte přednost zprávě posílajícímu stylu.
Procesy vytváří silné prostředí pro programování tím, že dokáží udělat z bloku objekt první třídy.
Budování bloku může nejdříve matoucí, ale po chvíli je to docela jednoduché. A blok je, jak má metafora říká, nenarozený Proces - to je Proces v přechodném stavu, ještě na nic nevázaný. Myslím, že nejjednodušší způsob jak přemýšlet o blocích v Ruby, bez ztracení jakékoliv chápavosti, by bylo dívat se na bloky jako opravdovou formu Procesů, a ne separátní koncept. Jen jedenkrát kdy musíme myslet na bloky jako nepatrně odlišné od Procesů je mimořádný případ kdy jsou uvedeny jak poslední argument k metodě která může pak zpřístupnit používání yield.