Funkcióösszetétel (informatika) - Function composition (computer science)
A számítógép-tudomány , a funkció készítmény olyan cselekmény vagy mechanizmus kombinálni egyszerű funkciókat építeni bonyolultabb is. Mint a szokásos készítmény funkciók a matematika , az eredmény az egyes funkciók átadásra az érvelés a következő, és az eredmény az utolsó pedig a végeredményt.
A programozók gyakran más funkciók eredményeire is alkalmazzák a funkciókat, és szinte az összes programozási nyelv ezt lehetővé teszi. Bizonyos esetekben a függvények összetétele önmagában is érdekes, később felhasználható. Egy ilyen függvény mindig meghatározható, de az első osztályú funkciókkal rendelkező nyelvek megkönnyítik.
Az a képesség, hogy könnyen levélírás funkciók ösztönzi faktoring (széttöredeznének) funkciót a karbantarthatóság és a kód újrafelhasználásának . Általánosságban elmondható, hogy nagy rendszereket egész programok összeállításával lehet felépíteni.
Szűkebb értelemben a függvényösszetétel azokra a funkciókra vonatkozik, amelyek véges mennyiségű adattal működnek, és minden lépés egymás után feldolgozza, mielőtt átadná a következőnek. Azok a funkciók, amelyek potenciálisan végtelen adatokon (egy adatfolyam vagy más kodata ) működnek, szűrőként ismertek , és ehelyett egy folyamatban vannak összekötve , ami analóg a funkciók összetételével és egyidejűleg is végrehajtható .
Funkcióhívások írása
Tegyük fel például, hogy két f és g függvényünk van , mint például z = f ( y ) és y = g ( x ) . Összeállításuk azt jelenti, hogy először kiszámoljuk az y = g ( x ) értéket , majd y segítségével kiszámoljuk a z = f ( y ) értéket . Íme a példa a C nyelven :
float x, y, z;
// ...
y = g(x);
z = f(y);
A lépések kombinálhatók, ha nem adunk nevet a köztes eredménynek:
z = f(g(x));
A hosszbeli különbségek ellenére ez a két megvalósítás ugyanazt az eredményt számítja ki. A második megvalósítás csak egy kódsort igényel, és köznyelven "erősen összetett" formának nevezik. Az olvashatóság és ennélfogva a karbantarthatóság az egyik előnye a nagyon összetett űrlapoknak, mivel kevesebb kódsorra van szükségük, ezzel minimalizálva a program "felületét". DeMarco és Lister empirikusan igazolja a felület és a karbantarthatóság közötti inverz kapcsolatot. Másrészt lehetséges lehet a túlságosan összetett formák túlzott használata. A túl sok funkció beágyazása ellenkező hatást eredményezhet, így a kód kevésbé karbantartható.
Egy halom alapú nyelv , funkcionális készítmény még természetes: végezzük összefűzés , és általában az elsődleges módja a programok tervezését. A fenti példa a Forth-ban :
g f
Ami azt veszi, ami korábban volt a veremben, alkalmazza g-t, majd f-t, és az eredményt a veremen hagyja. A megfelelő matematikai jelölést lásd a postfix kompozíció jelölésében.
A függvények összetételének megnevezése
Tegyük fel most, hogy az f () meghívásának kombinációja g () eredményén gyakran hasznos, és amelyet a foo () néven szeretnénk megnevezni, hogy önálló függvényként használjuk.
A legtöbb nyelvben meghatározhatunk egy új, kompozícióval megvalósított függvényt. Példa a C-ben :
float foo(float x) {
return f(g(x));
}
(a hosszú forma köztes termékekkel is működne.) Példa a Forth-ban :
: foo g f ;
Az olyan nyelvekben, mint a C , az új függvény létrehozásának egyetlen módja az, ha definiálja azt a programforrásban, ami azt jelenti, hogy a függvények nem állíthatók össze futás közben . Előre definiált függvények tetszőleges összetételének értékelése azonban lehetséges:
#include <stdio.h>
typedef int FXN(int);
int f(int x) { return x+1; }
int g(int x) { return x*2; }
int h(int x) { return x-3; }
int eval(FXN *fs[], int size, int x)
{
for (int i=0; i<size; i++) x = (*fs[i])(x);
return x;
}
int main()
{
// ((6+1)*2)-3 = 11
FXN *arr[] = {f,g,h};
printf("%d\n", eval(arr, 3, 6));
// ((6-3)*2)+1 = 7
arr[2] = f; arr[0] = h;
printf("%d\n", eval(arr, 3, 6));
}
Első osztályú kompozíció
A funkcionális programozási nyelvekben a függvényösszetétel természetesen magasabb rendű függvényként vagy operátorként is kifejezhető . Más programozási nyelveken megírhatja saját mechanizmusait a funkciók összetételének végrehajtására.
Haskell
A Haskell , A fent megadott példában pl válik:
foo = f . g
a beépített kompozíciós operátor (.) használatával, amely g- ként f- ként vagy f-vel komponált g- ként olvasható .
Maga a kompozíció operátora meghatározható a Haskellben egy lambda kifejezés segítségével :
(.) :: (b -> c) -> (a -> b) -> a -> c
f . g = \x -> f (g x)
Az első sorok a (.) Típusát írják le - pár függvényt vesz fel, és egy függvényt ad vissza. Ne feledje, hogy Haskell nem követeli meg az f és g pontos bemeneti és kimeneti típusainak megadását, csak a közöttük lévő kapcsolatokra (f-nek el kell fogadnia azt, amit g visszaad). Ez (.) Polimorf operátorrá teszi.
Selypít
A Lisp változatai , különösen a Scheme , a kód és az adatok felcserélhetősége, valamint a függvények kezelése rendkívül jól alkalmazható a variadikus kompozíciós operátor rekurzív meghatározásához .
(define (compose . fs)
(if (null? fs) (lambda (x) x) ; if no argument is given, evaluates to the identity function
(lambda (x) ((car fs) ((apply compose (cdr fs)) x)))))
; examples
(define (add-a-bang str)
(string-append str "!"))
(define givebang
(compose string->symbol add-a-bang symbol->string))
(givebang 'set) ; ===> set!
; anonymous composition
((compose sqrt negate square) 5) ; ===> 0+5i
APL
Az APL számos dialektusa a funkciókompozíciót a szimbólum segítségével építette fel ∘ . Ez a magasabb rendű funkció kiterjeszti a függvény összetételét a bal oldali függvény diadikus alkalmazására, A f∘g B azaz A f g B .
foo←f∘g
Ezenkívül meghatározhatja a függvény összetételét:
o←{⍺⍺ ⍵⍵ ⍵}
Olyan nyelvjárásban, amely nem támogatja a zárójelek használatával történő inline meghatározást, a hagyományos meghatározás érhető el:
∇ r←(f o g)x
r←f g x
∇
Raku
A Hakuellhez hasonló Rakunak van egy beépített függvény-összetevő operátora, a fő különbség az, hogy ∘ vagy vagy o .
my &foo = &f ∘ &g;
A Haskellhez hasonlóan maga is meghatározhatja az operátort. Valójában a következő a Raku kód, amelyet a Rakudo implementációban definiálnak .
# the implementation has a slightly different line here because it cheats
proto sub infix:<∘> (&?, &?) is equiv(&[~]) is assoc<left> {*}
multi sub infix:<∘> () { *.self } # allows `[∘] @array` to work when `@array` is empty
multi sub infix:<∘> (&f) { &f } # allows `[∘] @array` to work when `@array` has one element
multi sub infix:<∘> (&f, &g --> Block) {
(&f).count > 1
?? -> |args { f |g |args }
!! -> |args { f g |args }
}
# alias it to the "Texas" spelling ( everything is bigger, and ASCII in Texas )
my &infix:<o> := &infix:<∘>;
Piton
A Python , olyan módon, hogy meghatározzák a készítmény bármely csoport a funkciók, használja csökkentése funkciót (használatra functools.reduce Pythonban 3):
# Available since Python v2.6
from functools import reduce
def compose(*funcs) -> int:
"""Compose a group of functions (f(g(h(...)))) into a single composite func."""
return reduce(lambda f, g: lambda x: f(g(x)), funcs)
# Example
f = lambda x: x + 1
g = lambda x: x * 2
h = lambda x: x - 3
# Call the function x=10 : ((x-3)*2)+1 = 15
print(compose(f, g, h)(10))
JavaScript
A JavaScript- ben olyan funkcióként definiálhatjuk, amely két f és g függvényt vesz fel, és létrehoz egy függvényt:
function o(f, g) {
return function(x) {
return f(g(x));
}
}
// Alternatively, using the rest operator and lambda expressions in ES2015
const compose = (...fs) => (x) => fs.reduceRight((acc, f) => f(acc), x)
C #
A C # -ben Func-ként definiálhatjuk, amely két Funcs f és g felvételével Func-ot hoz létre:
// Call example:
// var c = Compose(f, g);
//
// Func<int, bool> g = _ => ...
// Func<bool, string> f = _ => ...
Func<TIn, TOut> Compose<TIn, TMid, TOut>(Func<TMid, TOut> f, Func<TIn, TMid> g) => _ => f(g(_));
Rubin
Az olyan nyelvek, mint a Ruby, lehetővé teszik egy bináris operátor létrehozását:
class Proc
def compose(other_fn)
->(*as) { other_fn.call(call(*as)) }
end
alias_method :+, :compose
end
f = ->(x) { x * 2 }
g = ->(x) { x ** 3 }
(f + g).call(12) # => 13824
Azonban egy natív függvény-összetevő operátort vezettek be a Ruby 2.6-ban:
f = proc{|x| x + 2}
g = proc{|x| x * 3}
(f << g).call(3) # -> 11; identical to f(g(3))
(f >> g).call(3) # -> 15; identical to g(f(3))
Kutatási felmérés
Fogalmai készítmény, beleértve a kompozicionalitás és alakíthatóságának , annyira mindenütt, hogy számos szál kutatás külön fejlődtek. Az alábbiakban bemutatunk egy mintát arról a kutatásról, amelyben a kompozíció fogalma központi szerepet játszik.
- Steele (1994) közvetlenül alkalmazta a funkciókompozíciót a Haskell programozási nyelvben „ monádok ” néven ismert építőelemek összegyűjtésére .
- Meyer (1988) a szoftver újrafelhasználási problémájával foglalkozott az összeállíthatóság szempontjából.
- Abadi és Lamport (1993) hivatalosan meghatározta a funkcionális összetétel igazolási szabályát, amely biztosítja a program biztonságát és életképességét.
- Kracht (2001) az összetettség megerősített formáját azonosította azáltal, hogy szemiotikus rendszerbe helyezte és alkalmazta a számítási nyelvészetben gyakran előforduló strukturális kétértelműség problémájára .
- van Gelder & Port (1993) a kompozíció szerepét vizsgálta a természetes nyelv feldolgozásának analóg aspektusaiban.
- Gibbons (2002) áttekintése szerint a kompozíció formális kezelése megalapozza az alkatrész-összeállítás validálását olyan vizuális programozási nyelveken, mint az IBM Visual Age for Java nyelv.
Nagyszabású kompozíció
Egész programok vagy rendszerek funkcióként kezelhetők, amelyek könnyen összeállíthatók, ha a bemenetek és kimenetek jól meghatározott csővezetékek, amelyek lehetővé teszik a szűrők egyszerű összeállítását, olyan sikeresek voltak, hogy az operációs rendszerek tervezési mintájává vált .
A mellékhatásokkal járó kötelező eljárások megsértik a referenciák átláthatóságát , ezért nem tisztán összeállíthatók. Ha azonban a kód futtatása előtti és utáni "világállapotot" tekintjük be- és kimenetnek, akkor tiszta funkciót kap. Az ilyen funkciók összetétele megfelel az eljárások egymás utáni futtatásának. A monádok formalizmusa ezt az ötletet alkalmazza a mellékhatások és az I / O beépítésére a funkcionális nyelvekbe.
Lásd még
- Curry
- Funkcionális bomlás
- Végrehajtási öröklés
- Öröklési szemantika
- Iteratee
- Csővezeték (Unix)
- A kompozicionalitás elve
- Virtuális öröklés
Megjegyzések
Hivatkozások
- Abadi, Martín ; Lamport, Leslie (1993), "Kompozíciós specifikációk" (PDF) , ACM tranzakciók a programozási nyelveken és rendszereken , 15 (1): 73–132, doi : 10.1145 / 151646.151649 .
- Cox, Brad (1986), Objektum-orientált programozás, evolúciós megközelítés , Olvasás, MA: Addison-Wesley, ISBN 978-0-201-54834-1 .
- Daume, Hal, III., Még egy Haskell bemutató .
- DeMarco, Tom ; Lister, Tim (1995): "Szoftverfejlesztés: a legkorszerűbb gyakorlat és a gyakorlat állapota", DeMarco, Tom (szerk.), Miért kerül ennyibe a szoftver, és az információs kor más rejtvényei , New York, NY: Dorset-ház, ISBN 0-932633-34-X .
- van Gelder, Timothy ; Port, Robert (1993), "A szimbolikán túl: a kompozitivitás kamaszutrájának prolegoménája ", Honavar, Vasant ; Uhr, Leonard (szerk.), Szimbólumfeldolgozó és kapcsolati modellek a mesterséges intelligenciában és a megismerésben: lépések az integráció felé , Academic Press .
- Gibbons, Jeremy (2002), Arbab, Farhad; Talcott, Carolyn (szerk.), Proc. 5. nemzetközi konferencia a koordinációs modellekről és nyelvekről (PDF) , Előadások a számítástechnikában, 2315 , Springer-Verlag, 339–350., Doi : 10.1007 / 3-540-46000-4 \ _18 CS1 maint: nem ajánlott paraméter ( link ) .
- Korn, Henry; Liberi, Albert (1974), A funkciók elemi megközelítése , New York, NY: McGraw-Hill, ISBN 0-07-035339-5 .
- Kracht, Marcus (2001), "Szigorú összetétel és szó szerinti mozgás grammatikák", Proc. 3. nemzetközi konferencia a számítástechnikai nyelvészet logikai vonatkozásairól , előadások a számítástechnikában, 2014 , Springer-Verlag, 126–143. Oldal, doi : 10.1007 / 3-540-45738-0_8 .
- Meyer, Bertrand (1988), Objektum-orientált szoftverépítés , New York, NY: Prentice Hall, 13–15. O., ISBN 0-13-629049-3 .
- Miller, George A. (1956), "A varázslatos hetes szám, plusz vagy mínusz kettő: az információfeldolgozás képességének korlátai vannak" , Psychological Review , 63 (2): 81–97, doi : 10.1037 / h0043158 , hdl : 11858 / 00-001M-0000-002C-4646-B , PMID 13310704 , az eredetiből 2010-06-19- én archiválva , letöltve 2010-05-02 .
- Pierce, Benjamin C.; Turner, David N. (2000), "Pict: A pi-calculuson alapuló programozási nyelv", Bizonyítás, nyelv és interakció: esszék Robin Milner tiszteletére , Cambridge, MA: MIT Press, pp 455–494, ISBN 0-262-16188-5 .
- Raymond, Eric S. (2003), "1.6.3 Kompozíciós szabály: Más programokkal összekapcsolható tervezési programok" , The Art of Unix Programming , Addison-Wesley, 15–16. O., ISBN 978-0-13-142901-7 .
- Steele, Guy L., Jr. (1994), "Tolmácsok építése monádok összeállításával" , Proc. 21. ACM szimpózium a programozási nyelvek elveiről, 472–492. O., Doi : 10.1145 / 174675.178068 .