Sens wykorzystywania wskaźników do funkcji.

0

Hej!
Przegladam sobie rozne rozwiazania i trafilem na cos takiego:

type    
TFunctionParameter = function(const value : integer) : string;  

...  

function One(const value : integer) : string; 
begin    
result := IntToStr(value) ; 
end;  

function Two(const value : integer) : string; 
begin    
result := IntToStr(2 * value) ; 
end;  

function DynamicFunction(f : TFunctionParameter) : string; 
begin   
 result := f(2006) ; 
end;  ...  

//Example usage:  

var    
s : string; 
begin    
s := DynamicFunction(One) ;    
ShowMessage(s) ; 

 s := DynamicFunction(Two) ;    
ShowMessage(s) ; 

Jasne jest dla mnie, ze przekazujemy tutaj do procedury rozne funcje i rozne wartosci. Pytanie tylko po co?

Ciezko mi sobie wyobrazic w jakies realnej sytuacji moglbym uzyc czegos takiego.

Czy stosuje sie takie rozwiazanie zeby uzyc czegos bardziej eleganckiego niz zwykle ify?

Chyba, ze potrzebujemy przekazana zmienna jeszcze jakos "obrobic" zanim przekazemy do funkcji i robimy to w wielu miejscach programu. Innej sytuacji nie umiem sobie wyobrazic;)

Mozecie mi podpowiedziec?

0

To jest tylko przykład na stosowanie wskaźników do funkcji. Nie rozumiem za bardzo Twojego pytania. Czy pytasz się, jaki jest sens w przekazywaniu parametrów do funkcji, czy jaki jest sens w przekazywaniu wskaźników do funkcji, czy jaki jest sens wskaźników do funkcji?

8

Dobrym przykładem jest procedura sortująca łańcuchy znaków; Służyć ma do posortowania podanej macierzy w sposób dokładnie określony przez nas:

type
  TStringsArray = array of String;

type
  TCompareResult = (crLess, crEqual, crGreater);
  TCompareFunc   = function(const AFirst, ASecond: String): TCompareResult;

  function CompareAscending(const AFirst, ASecond: String): TCompareResult;
  begin
    // zwrócenie wartości dla sortowania komórek rosnąco
  end;

  function CompareDescending(const AFirst, ASecond: String): TCompareResult;
  begin
    // zwrócenie wartości dla sortowania komórek malejąco
  end;

  function CompareRandom(const AFirst, ASecond: String): TCompareResult;
  begin
    // zwrócenie wartości dla mieszania komórek
  end;

  procedure SortStringsArray(var AStrings: TStringsArray; ACompareFunc: TCompareFunc);
  begin
    // główna procedura sortująca macierz
  end;

Przykład prosty, ale ma swoje istniejące odpowiedniki;

I teraz tak, w zależności od tego jak chcemy posortować macierz, podajemy odpowiednią funkcję porównującą elementy i zwracającą odpowiednią wartość, określającą rezultat porównania; Dzięki temu mamy jeden mechanizm sortowania, implementujący np. sortowanie szybkie, a to jaka będzie końcowa postać macierzy zależy tylko i wyłącznie od podanej funkcji porównującej elementy;
____Następnym przykładem jest znana biblioteka do tworzenia gier pod FPC, czyli ZenGL; Biblioteka ta posiada odpowiedni mechanizm, implementujący główną pętlę gry; W tej pętli używane są procedury do malowania po ekranie, obsługę inputu czy logiki, jednak nie są one nigdzie zdefiniowane; Nie jest z góry określone, jaki kod będzie wykonywany w każdej klatce gry np. do malowania ekranu;

Aby główna pętla gry używała naszego kodu, istnieje specjalna procedura, służąca do rejestrowania właśnie tych naszych procedur do obsługi gry - podajemy jej wskaźnik na procedurę oraz typ procedury (do malowania po ekranie, obsługi inputu itd.); Dzięki temu biblioteka wywołuje nasze procedury, które implementują naszą grę; Główny mechanizm gry jest jeden i niezmienny, ale wykonywany przez niego kod jest nasz;

Gdyby nie przekazywanie wskaźników na procedury, trzeba by zmodyfikować mechanizmy biblioteki ZenGL, aby główna pętla gry używała naszego kodu; A tak to podajemy bibliotece nasze fragmenty, a główna pętla wywołuje je w odpowiednich sytuacjach;
____Kolejny przykład to przechowywanie wskaźnika na aktualnie używaną procedurę/funkcję w celu uniknięcia skomplikowanych instrukcji warunkowych; Pakujemy wskaźnik na odpowiednią procedurę/funkcję do zmiennej i wywołujemy odpowiedni algorytm, bez konieczności rozróżniania sytuacji; Sam korzystam z takiego mechanizmu w swojej bibliotece do obsługi plików TreeStructInfo, w klasie ładującej drzewo do pamięci oraz zapisującej drzewo z pamięci np. do pliku;

To czy dodać element referencjonowany do listy pomocniczej czy wstawić w określone jej miejsce, zależy od metody trzymanej jako wskaźnik w polu FStoreRefElement (typu TStoreRefElementProc); Podczas przetwarzania głównego ciała drzewa, pole to przechowuje wskaźnik na metodę AddRefElement, która każdy nowy element dodaje na koniec listy; Natomiast podczas przetwarzania części referencjonowanej, pole trzyma wskazanie na metodę InsertRefElement, wstawiającą do listy nowo napotkany element referencjonowany pod zadany indeks; Ten mechanizm jest nieco bardziej skomplikowany, dlatego też opisuję go ogólnie, bez wielu szczegółów;
____Innym przykładem są standardowe zdarzenia - pole trzyma wskazanie na odpowiednią "metodę proceduralną", która zostanie wywołana przez obiekt komponentu w specyficznych warunkach; Jak powszechnie wiadomo, jedno zdarzenie można podłączyć pod wiele komponentów - nie trzeba dla każdego komponentu pisać tego samego kodu; Dzięki temu, kod może być jeden, krótki oraz zgodny z regułami KISS i DRY;
____Potrzeb wykorzystywania takiej techniki jest mnóstwo - wystarczy nieco wyobraźni :]

0

Nic dodać nic ująć. Dzieki wielkie! O to właśnie mi chodziło, wszystko jasne :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1