1)
Níže je funkce pythonu summation
, která může provádět součet krychlí / čtverců / .., podobných operací .
def identity(k): return k def cube(k): return pow(k, 3) def square(k): return pow(k,2) def summation(n, term): if n == 0: return 0 else: return term(n) + summation(n-1, term) def sum_cubes(n): return summation(n, cube) if __name__ == "__main__": sum = sum_cubes(4) print(sum) """ In C, We can implement the same using function pointers. Goal is, to perform similar operations(Sum of ..) using single function summation()"""
2)
Zvažte níže řazení API z C,
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
Zde qsort
může třídit data jakéhokoli typu , pole plováků / názvy souborů v adresáři / strings / …
Otázka:
Jak definovat obecnou funkci?
Je summation
obecná funkce?
nebo
Je qsort
obecná funkce?
nebo
Vzhledem ke dvěma příkladům je obecná funkce neplatná terminologie?
Poznámka: Motivační výraz qsort
nebo jakákoli funkce řazení, kterou navrhuji
Komentáře
- Jaká definice “ obecná funkce “ četli jste, že ‚ nerozumíte? Pomohlo by, kdybyste místo toho napsali spoustu kódu.
- Pojem teorie typů pro druh genericismu, kde funkce funguje pro jakýkoli typ bez omezení a bez znalosti konkrétního typu, je parametrický polymorfismus . Funkce identity je tímto způsobem obecná.
- V některých jazycích (například Java) má “ obecná funkce “ konkrétní technická definice. Ale v Pythonu tomu tak není, takže “ obecná funkce “ nemá přesně definovaný význam. Neznamená to, že jde o “ neplatnou terminologii „, jen když byste tento výraz používali, měli byste znát kontext.
- @AndresF. Javascrpt také hodně používá tuto obecnou funkci terminologii. Protože můžete mít funkci, která zpracovává jakýkoli html prvek (příklad – smažte všechny podřízené prvky daného html prvku)
Odpovědět
„ Obecný “má několik významů.
Neformální definice
„obecný“ v běžném jazyce něco, co sdílí společné vlastnosti, ale je v některých ohledech méně konkrétní.
Z tohoto hlediska můžete považovat qsort()
za obecný: kód této funkce je schopen třídit jakoukoli datovou strukturu pevné velikosti, pro kterou můžete definovat srovnávací funkci pomocí algoritmu QSORT.
Totéž platí pro vaši funkci summation()
, která shrnuje termíny získané pomocí libovolných funkcí s jedním parametrem.
Formální definice
Programovací jazyky jako C ++ nebo Java umožňují generické programování s použitím šablon nebo generik:
Definice ze standardu C ++ 14 : Šablona definuje rodinu tříd nebo funkcí nebo alias pro rodinu typů.
Princip je v tom, že implementaci třídy nebo funkce lze parametrizovat podle typů.
Podle tohoto formálnějšího pohledu qsort()
není obecná funkce. Jeho implementace nemusí při kompilaci určovat žádný typ a jeho chování je nezávislé na typu. Jediná věc, kterou potřebuje, je velikost tříděných prvků a tato velikost je běžný argument, který se zpracovává za běhu.
U jazyka, který není zadán staticky, jako je Python , nejsem si jistý, na co mám odpovědět summation()
. Myslím, že to není obecné, protože jeho implementace a chování není závislé na typu: tato funkce je pouze funkcí vyššího řádu, přičemž argument term
je funkce. Nepoužívá žádnou funkci, která by změnila chování této funkce na základě typů.
Pro ilustraci obecné funkce se můžete podívat na standardní funkci C ++ std::sort()
: jeho implementace závisí na typu jeho argumentů (a volitelně srovnávací funkce s argumenty určeného typu). Pomocí funkcí šablon C ++ může řadit libovolný kontejner libovolného typu za podmínky, že má operátory / členské funkce / vlastnosti / iterátory, které jsou vyžadovány implementací obecné funkce.
Může mít dynamický typovaný jazyk obecné funkce
Dynamicky typovaný jazyk vyžaduje méně obecný kód než staticky zadané jazyky.
Například pokud máte kontejner objektů dynamického typu, funkce qsort může kontejner obecně seřadit, pokud lze porovnat libovolnou kombinaci dvou prvků v kontejneru.
Ale i v tak flexibilním prostředí může být užitečné obecné programování závislé na typu. Typickým případem použití jsou multimetody, kde chování nebo kód závisí na typu argumentů nebo dokonce na kombinaci typů (například pro určení průniku mezi dvěma různými tvary). Další informace naleznete na:
- Obecný programovací balíček pro python: multidispatching
- Vytváření obecných funkcí použitelných v smalltalk
- Příklad obecné funkce v programu Common Lisp
Komentáře
- Nejsem si jistý, proč porovnáváme Generika (hlavně používá se k vyhnutí se castingu v Javě & pomáhá provádět polymorfismus) s definicí obecné funkce?
- @overexchange Myslím, že java také nabízí obecné programování, včetně obecných metod (viz specifikace nebo návod ). Přesto jsem ‚ mírně upravil definiční část, abych řešil vaši poznámku.
- Obecný balíček od Pythonu nemá nic společného s obecnými funkcemi. Až na to, že sdílejí stejné adjektivum.
- @Killian if generické programování , je o myšlence abstrahovat od konkrétních, efektivních algoritmů Chcete-li získat obecné algoritmy, které lze kombinovat s různými reprezentacemi dat , myslím, že multimetody v tomto balíčku by měly být v, nemyslíte si to ‚?
Odpověď
Obecné funkce berou typ alespoň jednoho argumentu funkce obecně v době kompilace. To znamená, že kompilátor zjistí, který typ se používá na určitém místě, a použije přesně tento typ, kde se ve funkci používá. Např. pokud máte ve své funkci obecný argument, který se používá s operátorem +
, musí mít typ vhodné metody. Pro řetězce / pole by to v mnoha případech bylo zřetězení a pro a integer / float přídavek. Kompilátor může zjistit, že použije správnou operaci. Vaše rutina C není v tomto smyslu obecná, protože je to programátor, který aplikuje určité informace o velikosti, a nikoli kompilátor, který detekuje typ a používá správnou velikost.
Např. V nějakém fiktivním jazyce
func add(p1,p2) { return p1+p2 } print add("a", "b") // yields "ab" print add(1, 2) // yields 3
Zde překladač v prvním případě zjistí, že jsou použity dva řetězce, a interně rozbalí něco jako
func add(p1:string, p2:string)
a považovat +
za zřetězení, zatímco v druhém případě by se rozšířil
func add(p1:int, p2:int)
podle dodaného celočíselné parametry. Obecný znamená, že kompilátor generuje individuální kód během doby kompilace. Například Python je netypový a během běhu by tento druh náhrady dělal. Znamená: Python nemá obecné funkce, protože vše je jakési obecné.
Komentáře
- Nedostali jste svůj nápad. Myslíte tím, že + je obecná funkce, syntaxe v C ++?
- Funkce přijímající funkční argumenty jsou vyššího řádu funkce s ve světě Python / JavaScript. V jazyce C potřebujeme stejné ukazatele funkcí.
- Viz moje úprava výše.
- Takže, jaký druh funkce je
summation
, Funkce vyššího řádu? a nic víc než to? - Existuje mnoho definic toho, co je obecné. Stroustrup to například definuje jako “ programování pomocí typů jako parametrů „. Pokud jde o odkaz na wikipedii, přejdu ‚ raději na: en.wikipedia.org/wiki/Generic_programming
Odpověď
Začnu to z pohledu C ++ a poté se dostanu do C.
Ve staticky zadávaných jazycích, jako jsou C, C ++, Java atd., umožňuje „obecná“ funkce určit funkce operací jednou pomocí zástupných symbolů pro všechny typy, které se mohou lišit mezi různými hovory (což znamená, že funkce jako qsort
a bsearch
jsou rozhodně ne obecné funkce). V ideálním případě byste také chtěli, aby kompilátor automaticky rozpoznal volání této obecné funkce a podle potřeby vygeneroval skutečný kód.
C ++ usnadňuje 1 tím, že nabízí šablony :
template <typename T> T summation( T *values, size_t numValues ) { T result = 0; for ( size_t i = 0; i < numValues; i++ ) result += values[i]; return result; }
T
je zástupný symbol pro jakýkoli typ 2 , takže jej můžete nazvat jako
int ivals[] = {1,2,3,4,5,6,7,8,9}; double dvals[] = {1,2,3,4,5,6,7,8,9}; int sumi = summation( ivals, 10 ); double sumd = summation( dvals, 10 );
Když je kód zkompilován, kompilátor vidí dvě volání summation
a odvodí typy argumentů. Pro každý jiný typ vygeneruje novou instanci funkce a dá jí jedinečný název:
int summation_i( int *values, size_t numValues ) // actual compilers will generate { // more complex "mangled" names int result = 0; // than this ... } double summation_d( double *values, size_t numValues ) { double result = 0; ... }
Potom vygeneruje kód takový, že výsledek summation_i
je přiřazen sumi
a summation_d
je přiřazen sumd
.
C nenabízí nic podobného funkci šablony. Obecně jsme na obecné programování zaútočili jedním ze dvou způsobů – buď pomocí maker nebo pomocí void *
anywhere a delegování operací typu na jiné funkce.
Zde je špatný příklad řešení založeného na makrech:
#include <stdio.h> #define SUMMATION_DEF(t) \ t summation_##t( t *values, size_t numValues ) \ { \ t result = 0; \ for ( size_t i = 0; i < numValues; i++ ) \ result += values[i]; \ return result; \ } #define SUMMATION(t,x,s) summation_##t(x, s) SUMMATION_DEF(int) SUMMATION_DEF(double) int main( void ) { int ivals[] = {1, 2, 3, 4, 5}; double dvals[] = {1, 2, 3, 4, 5}; int sumi = SUMMATION(int, ivals, 5); double sumd = SUMMATION(double, dvals, 5); printf( "sumi = %d\n", sumi ); printf( "sumd = %f\n", sumd ); return 0; }
SUMMATION_DEF
je zhruba podobný šabloně v tom, že specifikuje operace funkce pomocí parametru makra t
jako zástupného typu. Používáme také t
jako součást názvu funkce – ##
je operátor vkládání tokenů a preprocesor rozbalí t
a přidejte tuto hodnotu k názvu funkce 3 .
Kde se liší od C ++, je skutečnost, že makro je jen hloupá substituce textu. Nezpouští jakékoli speciální operace ze strany kompilátoru. Skutečné instance funkcí nejsou automaticky generovány na základě jakýchkoli vyvolání makra SUMMATION
– musíme explicitně generovat funkce, které chceme (proto SUMMATION_DEF(int)
a SUMMATION_DEF(double)
před main
). Znamená to také, že když zavoláme summation_xxx
prostřednictvím makra SUMMATION
musíme typ předat jako součást seznamu argumentů makra, aby byla volána správná funkce. Jaká bolest.
The Standard C 2011 přidal klíčové slovo _Generic
, které v tomto ohledu může trochu usnadnit život:
#include <stdio.h> #define SUMMATION_DEF(t) \ t summation_##t( t *values, size_t numValues ) \ { \ t result = 0; \ for ( size_t i = 0; i < numValues; i++ ) \ result += values[i]; \ return result; \ } #define SUMMATION(x,s) _Generic((x), \ int * : summation_int, \ double * : summation_double \ )(x, s) SUMMATION_DEF(int) SUMMATION_DEF(double) int main( void ) { int ivals[] = {1, 2, 3, 4, 5}; double dvals[] = {1, 2, 3, 4, 5}; int sumi = SUMMATION(ivals, 5); double sumd = SUMMATION(dvals, 5); printf( "sumi = %d\n", sumi ); printf( "sumd = %f\n", sumd ); return 0; }
The Klíčové slovo _Generic
umožňuje vyhodnotit výrazy na základě typů ; tedy pokud typ prvního argumentu SUMMATION
je int *
, voláme summation_int
; je to double *
voláme summation_double
. Tímto způsobem nemusíme v argumentech makra specifikovat název typu.
Druhým přístupem, jak jste již viděli, je použití void *
a delegování operací podle typu na jiné funkce. Jak jsem řekl výše, že “ Ve skutečnosti nejde o „obecné“ programování, protože je nutné ručně implementovat každou srovnávací funkci pro každý typ. Nelze jej pouze jednou kódovat a hotovo. A pomocí void *
v zásadě vyhodíte typ zabezpečení z okna a do protijedoucího provozu.
A než si někdo stěžuje – ne, žádná z těchto součtových funkcí nekontroluje nebo neřeší aritmetické přetečení. To je předmět na další den.
- Pro dostatečně volné definice výrazu „easy“. Metaprogramovací jazyk používaný k podpoře šablon je Turing-complete, takže s ním můžete dělat * opravdu úžasné * a nemožné rozumět věcem.
- Pro dostatečně volné definice „jakéhokoli typu“. Všimněte si, že jakýkoli typ, který používáte, musí podporovat operátor
+=
, jinak na vás kompilátor bude křičet. - Tento kód se rozbije pro typy jako
unsigned int
nebolong double
, protože mají v názvu prázdné znaky. Okamžitě neznám řešení tohoto problému a věnoval jsem této odpovědi dostatek času.