Kompozicija funkcija

U programiranju, kompozicija funkcija predstavlja akt ili mehanizam za kombinovanje više jednostavnih funkcija radi stvaranja složenije funkcije. Uobičajeno je da se rezultat svake funckije prenosi kao argument sledeće, a rezultat poslednje je rezultat celine.

Programeri često primenjuju funkcije na rezultate nekih drugih funkcija što dopuštaju gotovo svi programski jezici. U nekim slučajevima kompozicija funkcija je zanimljiva sama po sebi kao funkcija.Takva funkcija se uvek može definisati, ali jezici sa prvoklasnim funkcijama to olakšavaju.

Mogućnost jednostavne kompozicije funkcija podstiče refaktorisanje funkcija za održavanje i ponovnu upotrebu koda. Generalno, veliki sistemi se mogu praviti kompozicijom čitavih programa.

Jednostavno rečeno, kompozicija funkcija se odnosi na funkcije koje rade na ograničenoj količini podataka, svaki korak ih uzastopno obrađuje pre nego što pošalje sledećem. Funkcije koje obrađuju beskonačno podataka poznate su kao filtri i povezane su u cevovod (pipeline), što je analogno kompoziciji funkcija i može se istovremeno izvršiti.

Pozivi kompozicije funkcije uredi

Na primer, pretpostavimo da imamo dve funkcije f i g, u obliku z=f(y) i y=g(x). Njihova kompozicija podrazmeva prvo izračunavanje y=g(x) , a zatim korišćenje tog y za izračunavanje z=f(y) . Sledi primer u programskom jeziku C :

float x, y, z;
// ...
y = g(x);
z = f(y);

Koraci se mogu kombinovati ako ne zadamo ime srednjem rezultatu:

z = f(g(x));

Uprkos razlikama u dužini, ove dve implementacije daju isti rezultat. Druga implementacija sadrži samo jednu liniju koda i naziva se "highly composed" obrazac. Čitljivost, a samim tim održivost je jedna od prednosti visoko sređenih oblika, jer zahtevaju manje linija koda, minimizirajući ,,površinu programa". [1]DeMarco i Lister empirijski potvrđuju obrnut odnos između površine i održanja.[2] S druge strane, visoko komponovani oblici mogu biti preterani. Korišćenje previše funkcija može imati suprotan efekat, čineći kod manje održivim. U jezicima koji koriste stek kompozicija funkcije je još prirodnnija : izvodi se spajanjem i obično je primarna metoda kod dizajniranja programa.Sledi primer u programskom jeziku Forth :

g f

Ovo će uzeti sve što je bilo na steku ranije, primeniti  g, zatim f, i ostaviti rezultat na steku.

Imenovanje kompozicije funkcije uredi

Sada pretpostavimo da je kombinacija pozivanja f() na rezultat g() je često korisna i želimo je nazvati foo() da bi se mogla koristiti kao funkcija sama po sebi .

U većini programskih jezika, možemo definisati novu implementiranu preko kompozicije. Primer u C- u:

float foo(float x) {
    return f(g(x));
}

Primer u Forth :

  : foo g f ;

U programskom jeziku C jedini način da se kreira nova funkcija je definisanje iste u izvornom kodu, što znači da ne mogu biti dodate dok je program pokrenut. Međutim, moguća je procena proizvoljne kompozicije unapred definisanih funkcija:

#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));
}

Prvoklasna kompozicija uredi

U funkcionalnim programskim jezicima, kompozicija funkcija može se prirodno izraziti kao funkcija višeg reda ili operator. U drugim programskim jezicima može se napisati sopstveni mehanizam za izvođenje kompozicije funkcija.

Haskell uredi

U programskom jeziku Haskell , gorenavedeni primer postaje :

foo = f . g

korišćenjem ugrađenog operatora kompozicije (.), koji se može čitati kao f nakon g ili g sastavljen sa f . Sam operator kompozicije se može definisati u Haskell pomođu lambda izraza (računa):

(.) :: (b -> c) -> (a -> b) -> a -> c
f . g = \x -> f (g x)

Haskell ne zahteva specifikaciju konkretnih tipova ulaza i izlaza f i g, samo odnose između njih (f mora prihvatiti ono što g vraća). Ovim (.) postaje polimorfni operator.

Lisp uredi

Varijante Lisp-a posebno Scheme , zamenjivost koda i podataka, zajedno sa tretmanom funkcija, daju se izuzetno dobro za rekurzivnu definiciju varijabilnog kompozicionog operatora.

(define (compose . fs)
  (if (null? fs) (lambda (x) x) ; ако није дат аргумент процењује се као функција идентитета
      (lambda (x) ((car fs) ((apply compose (cdr fs)) x)))))

; примери
(define (add-a-bang str)
  (string-append str "!"))

(define givebang
  (compose string->symbol add-a-bang symbol->string))

(givebang 'set) ; ===> set!

;анонимна композиција
((compose sqrt negate square) 5) ; ===> 0+5i

APL uredi

Mnogi dijalekti APL-a imaju ugrađenu funkcijsku kompoziciju, koja se koristi kao simbol ∘. Ova funkcija višeg reda proširuje sastav funkcije na dijadičnu primenu funkcije leve strane tako da A f∘g B je A f g B.

foofg

Pored toga može se definisati kompozicija funkcije:

o{⍺⍺ ⍵⍵ }

U dijalektu koji ne podržava inline definiciju pomoću zagrade, dostupna je tradicionalna definicija:

 r(f o g)x
  rf g x

Raku uredi

Poput Haskell-a, Raku ima ugrađen operator kompozicije funkcija, glavna razlika je u tome što se predstavlja kao ∘ ili o.

my &foo = &f ∘ &g;

U nastavku je Raku kod koji se koristi da se definiše operator u Rakudo implementaciji.

proto sub infix:<∘> (&?, &?) is equiv(&[~]) is assoc<left> {*}

multi sub infix:<∘> () { *.self } # омогућава да `[∘] @array` ради када је `@array`празан
multi sub infix:<∘> (&f) { &f }   # омогућава да  `[∘] @array`ради када `@array`има један елементt
multi sub infix:<∘> (&f, &g --> Block) {
    (&f).count > 1
    ?? -> |args { f |g |args }
    !! -> |args { f g |args }
}

my &infix:<o> := &infix:<∘>;

Python uredi

U Python-u se kompozicija funkcije definiše korišćenjem  reduce funkcije (functools.reduce u Python 3) :

# Доступно од верзије Python v2.6
from functools import reduce

def compose(*funcs) -> int:
    """Састављање групе композиција (f(g(h(...)))) у једну композитну функцију"""
    return reduce(lambda f, g: lambda x: f(g(x)), funcs)

# Примери
f = lambda x: x + 1
g = lambda x: x * 2
h = lambda x: x - 3

# Позивање функције x=10 : ((x-3)*2)+1 = 15
print(compose(f, g, h)(10))

JavaScript uredi

U ovom programskom jeziku može se definisati kao funkcija koja uzima dve funkcije f i g i tako pravi novu funkciju:

function o(f, g) {
    return function(x) {
        return f(g(x));
    }
}

// Коришћење осталих оператора и ламбда израза у ES2015
const compose = (...fs) => (x) => fs.reduceRight((acc, f) => f(acc), x)

C# uredi

U programskom jeziku C# se može definisati pomoću Func :

// Пример позива:
//   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(_));

Ruby uredi

Jezici poput jezika Ruby-a omogućavaju samostalno pravljenje binarnog operatera:

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

Međutim, sopstveni operator kompozicije funkcije uveden je u Ruby 2.6 :[3]

f = proc{|x| x + 2}
g = proc{|x| x * 3}
(f << g).call(3) # -> 11; идентично  f(g(3))
(f >> g).call(3) # -> 15; идентично g(f(3))

Istraživačka anketa uredi

Pojmovi kompozicije, uključujući princip kompozicionosti i kompostibilnosti, su toliko sveprisutni da su se brojne teme istraživanja odvojeno razvijale.Slede uzorkovanja vrste istraživanja u kojima je pojam kompozicije centralni.

  • Steele (1994) je direktno primenio funkcionu kompoziciju na skup ,,građevinskih blokova" koji su u programskom jeziku Haskell poznati kao "monade".
  • Meyer (1988) se bavio problemom ponovne upotrebe softvera u smislu kompostibilnost.
  • Abadi & Lamport (1993) su formalno definisali pravilo kompozicije funkcije koja osigurava sigurnost i život programa.
  • Kracht (2001) je identifikovao pojačani oblik kompozicionosti stavljajući ga u semiotički sistem i primenjujući ga na problem strukturalne nejasnoće koji se često sreće u računarskoj lingvistici.
  • van Gelder & Port (1993) ispitala je ulogu kompozicionosti u analognim aspektima obrade prirodnog jezika.

Kompozicija velikog obima uredi

Čitavi programi ili sistemi mogu se tretirati kao funkcije, koje se lako mogu sastaviti ako su njihovi ulazi i izlazi dobro definisani,[4] cevovodi omogućavaju lako sastavljanje filtera, pa je sve to postalo dizajnerski obrazac operativnih sistema.

Imperativni postupci sa spoljnim efektima krše referentnu transparentnost i zbog toga se ne mogu čisto sastaviti. Međutim, ako uzimate u obzir „stanje sveta“ pre i posle pokretanja koda kao njegove ulaze i izlaze, dobićete čistu funkciju. Sastav takvih funkcija odgovara pokretanju procedura jedne za drugom . Monadski formalizam koristi ovu ideju da uvrsti nuspojave i I / O u funkcionalne jezike.

Vidi još uredi

Reference uredi

  1. ^ Cox, Brad (1986). Object-oriented Programming, an Evolutionary Approach. str. 15—17. ISBN 978-0-201-54834-1. 
  2. ^ DeMarco, Tom; Lister, Tim (1995). Why Does Software Cost So Much, and other puzzles of the Information Age. New York, NY: Dorset House. ISBN 0-932633-34-X. 
  3. ^ „Ruby 2.6.0 Released”. www.ruby-lang.org. Pristupljeno 2020-07-14. 
  4. ^ Raymond, Eric S. (2003). „1.6.3 Rule of Composition: Design programs to be connected with other programs”. The Art of Unix Programming. Addison-Wesley. str. 15—16. ISBN 978-0-13-142901-7.