Funkció összetétele rubinban
Ebben a cikkben a következő témákkal foglalkozunk:
- a Funkciókompozíció használata a Ruby nyelvben
- emberbarát szintaxis
- hogyan működik a lambdákkal
- Funkció összeállítás és Kacsa gépelés
- Funkciókompozíció a színfalak mögött
Mielőtt elkezdené
Kérem, engedje meg, hogy itt bemutassam azt a platformot, amely segített elsajátítani a Rubyval kapcsolatos ismereteim nagy részét. Valóban, a Pluralsight egy csodálatos platform.
Az 50+ tanfolyam, amelyek különböző témákat fednek le a Ruby és Ruby on Rails témakörben, ez a legjobb módja annak, hogy tudását a következő szintre emelje!
Próbáld ki ingyen 👇😉
Köszönöm az idődet!
Funkció Összetétel
A CompSci-ben a Funkciókompozíció több függvény kombinálására utal bonyolultabbak létrehozása érdekében. A Rubyban ezt a fogalmat a Proc#<<
és Proc#>>
metódusok képviselik
increment = proc {|x| x + 1} double = proc {|x| x * 2} (double << increment).call(5) # => 12
Lépésről lépésre részletezzük, mi történt az előző példában
increment = proc {|x| x + 1} double = proc {|x| x * 2} x = 5 x = increment.call(x) # => 5 + 1 = 6 double.call(x) # => 6 * 2 = 12
Mivel a bal-shift operátort használjuk, a jobb oldali proc (increment
) kerül végrehajtásra először, és ennek a proc hívásnak az eredménye kerül átadásra a bal oldali proc hívás argumentumaként (double
).
A mechanizmus ugyanaz a right-shift operátornál, kivéve, hogy a bal oldali proc végrehajtása először történik meg.
Emberbarát szintaxis
A Ruby által biztosított szintaxis emberbarát, és lehetőséget ad arra, hogy ugyanazt az eredményt többféleképpen biztosítsd. Ez a megközelítés lehetőséget ad arra, hogy közvetlenül a gondolataiból fejezze ki a megoldást. Valójában, míg más nyelveken egyedi szintaxisnak kell megfelelnie, és játszania kell a végrehajtási sorrenddel, hogy megfelelő eredményt érjen el a kompozícióhoz, a Rubyban nem kell megbirkóznia ezzel a kellemetlenséggel. Ezenkívül a szintaxis sokkal jobban olvasható Rubyval.
Hasonlítsuk össze a Funkciókompozíció használatát a Python és a Ruby-ban.
Pythonban
import functools def double(x): return x * 2 def inc(x): return x + 1 def dec(x): return x - 1 def compose(*functions): return functools.reduce(lambda f, g: lambda x: f(g(x)), functions, lambda x: x) func_comp = compose(dec, double, inc) func_comp(10) # => 21
Rubyban
double = proc { |x| x * 2 } inc = proc { |x| x + 1 } dec = proc { |x| x - 1 } (dec << double << inc).call(10) # => 21
Egyet kell értenie azzal, hogy a szintaxis sokkal természetesebb és olvashatóbb Rubyban. Ezenkívül a Pythonban a compose
metódushívás argumentumaként átadott metódusok sorrendjével kell játszania, hogy biztosítsa a várt eredményt.
A Rubyban megváltoztathatja a végrehajtás sorrendjét, miközben ugyanazt az eredményt biztosítja anélkül, hogy kockáztatná a forráskód összezavarását.
(double << inc >> dec).call(10) # => 21 (dec << double << inc).call(10) # => 21 (inc >> double >> dec).call(10) # => 21
Funkció Összetétel lambdákkal
Először is nyugodtan tekintse meg a Proc vs Lambda in Ruby cikket, ha nem ismeri a két idióma közötti különbségeket.
Mivel a lambdas és a procs egyaránt a Proc
osztály példányai, a Function Composition használható mindkét eszközzel.
double = proc { |x| x * 2 } inc = lambda { |x| x + 1 } dec = proc { |x| x - 1 } (dec << double << inc).call(10) # => 21
Mivel a lambdák szigorúak az argumentumszámok tekintetében, óvatosnak kell lennie a mellékhatások elkerülése érdekében
double = proc { |x| x * 2} add = lambda { |x, y| x + y } (double << add).call(1)
termel
ArgumentError (wrong number of arguments (given 1, expected 2))
Tehát ennek javítására átadhatunk egy második argumentumot a call
metódusnak
double = proc { |x| x * 2} add = lambda { |x, y| x + y } (double << add).call(1, 2) # => 6
Valójában itt a add
lambda eredménye a double
proc egyetlen argumentumaként kerül átadásra. Tehát (1 + 2) * 2 = 6
.
A funkció összeállítása és a kacsagépelés
Tehát mi van, ha elmondom, hogy a Ruby Funkciókompozíciója nem kizárólag procokkal és lambdákkal működik?
class Triple def call(x) x * 3 end end increment = ->(x) { x + 1 } triple = Triple.new (increment << triple).call(1) # => 4
Itt a triple
a Triple
példánya, amely a call
metódusra válaszol. Valójában a Proc#<<
és Proc#>>
metódusok elvárják, hogy az argumentumként átadott objektum válaszoljon a call
metódusra. Az egyetlen megkötés az, hogy az első függvénynek balról a Proc
példányának vagy annak valamelyik szakterületének kell lennie.
Ez a megvalósítás a Duck Typing tervezési elv szabályait követi:
Ha látok egy madarat, amely úgy jár, mint egy kacsa, úszik, mint egy kacsa, és hápog, mint egy kacsa, azt a madarat kacsának nevezem.
Tehát ezt feltételezhetjük
Ha egy objektum prociként működik (a
call
metódusra reagálva), akkor folytassa és kezelje procikként.
Nyugodtan tekintse meg a Miért ösztönzi a Ruby közösség a kacsagépelést cikket, ha nem ismeri ezt a tervezési elvet.
Funkció Kompozíció a színfalak mögött
A Proc#>>
és Proc#<<
metódusok a Proc
új példányát adják vissza, amely végrehajtja az első proc-t, és ennek a proc hívásnak a visszatérési értékét adja át a második proc< argumentumként. /em> végrehajtásához. Ez a mechanizmus akkor lép működésbe, amikor meghívja a call
-t ezen a frissen létrehozott procin
a = proc { 42 } b = proc { |x| x * 2 } composed_proc = (a >> b) # => #<Proc:0x00007f95820bb908> composed_proc.call # => 84
Itt láthatjuk, hogy a a >> b
egy új proc-ot ad vissza, és amikor meghívjuk a composed_proc.call
-t, akkor a b.call(a.call)
végrehajtásra kerül.
Most pedig nézzük meg, mi történik, ha kompozíciót használunk lambdákkal és procokkal
a = lambda { |x| x * 2 } b = proc { 42 } composed_proc = (a << b) # => #<Proc:0x00007f99fd054a80(lambda)> composed_proc.call # => 84
Itt láthatjuk, hogy a composed_proc
egy lambda. Valójában a bal oldali proc típusa határozza meg a composed_proc
típusát. Tehát, mivel a a
egy lambda, úgy a composed_proc
is egy lambda. Most fordítsuk meg a a
és b
összetételt
a = lambda { |x| x * 2 } b = proc { 42 } composed_proc = (b << a) # => #<Proc:0x00007f99fd054a80> composed_proc.call
termel
ArgumentError (wrong number of arguments (given 0, expected 1))
Itt a b
egy proc. A Socomposed_proc
szintén egy proc. A ArgumentError
azért jelenik meg, mert a composed_proc.call
először a a.call
-ot hívja meg, és mivel a a
egy lambda, szigorúan argumentumra számít.
Ez egyben a bal oldali proc, amely meghatározza az összeállított proc típusát a Proc#>>
metódus használatakor. Valójában, mivel a bal oldali proc a Proc#>>
és Proc#<<
üzenet receiver
-je, automatikusan meghatározza a visszaadott proc típusát a self
típusának megfelelően — az a példány, amely ezt a metódushívást fogadja.
Ezt a viselkedést a Ruby 2.7.1-ig vállalhatja. Úgy tűnik, hogy a Ruby következő verziója kissé módosítja ezt a viselkedést azáltal, hogy a jobb oldali proci típusát használja a
Proc#<<
metódushoz.
Voálá!