Kuo skiriasi rekursyvinė funkcija nuo funkcijos iškvietimo pagal C / C ++?


Atsakymas 1:

Abi šios funkcijos yra rekursyvios. Pagrindinis skirtumas yra tas, kad antros funkcijos argumentas yra nuoroda į reikšmę, o ne į tiesiogiai perduodamą reikšmę, tai reiškia, kad kiekvieną kartą naudojant funkciją nebus kopijuojama tiesioginė vertė. (Tokia savybė yra mažiau naudinga vidinėms reikšmėms ir būtų naudingesnė struktūroms ar klasėms, kad nereikėtų kopijuoti ištisų duomenų blokų kiekvieną kartą, kai pasikartoja funkcija.)

Vienintelis kitas skirtumas yra tas, kad antroji funkcija negrąžina jokios vertės, o pirmoji - ne.


Atsakymas 2:

Tai abu yra rekursiniai funkcijos skambučiai. Vienas grąžina rekursinio skambučio rezultatą, o kitas ne. Abu yra galiojantys (ir naudingi) naudojant rekursiją.

Akivaizdu, kad abiejuose pavyzdžiuose yra klaidos, nes jūs niekada nenutraukiate pasikartojimo (ir galiausiai sukelsite krūvos perpildymą), tačiau aš manau, kad jūs tiesiog pateikiate išgalvotą kodą, kad apibrėžtumėte klausimą.


Atsakymas 3:

Aš mušiu negyvą arklį, pasakodamas tą patį dalyką, kaip ir kiti atsakymai, tačiau negaliojančiu metodu vėl ir vėl bus vykdoma ta pati užduotis, nes grįžtamasis metodas bus naudojamas kaupiant kiekvieno rekursinio skambučio rezultatus.

Dabar, jei nežinojote, kad tai nėra geros pasikartojančios funkcijos, nes nėra jokio pagrindinio atvejo, be to, dirbdami C temperatūroje patirsite krūvos perpildymą (arba buferio perpildymą, atsižvelgiant į tai, su kuo kalbatės), kuris turi daug atminties problemų. turite būti atsargūs. Norint nurodyti dugną, jums reikia pagrindinio atvejo rekursinėje funkcijoje.


Atsakymas 4:

Išskyrus tuos, kurie turi grįžtamąją vertę, dažniausiai tai yra pirmenybė / įskaitomumas.

Nors aš manyčiau, kad tai dažniausiai yra bloga praktika imperatyvaus programavimo srityje. Tais atvejais, kai rekursija yra labai svarbi, greičiausiai tai susiję su sudėtingesnių matematinių algoritmų įgyvendinimu, todėl funkcinis programavimas yra daug labiau pritaikytas savybių ir vykdymo laiko analizei. Kai keičiamasi į faktinį vartojimą, imperatyviųjų programavimo kalbų sąvokos yra geresnės nei rekursinės.


Atsakymas 5:

Kaip sakė visi kiti, abi jūsų funkcijos yra tiesiog rekursyvios.

Ir jūs nieko nekviečiate remdamiesi nuoroda, bet turite kintamąjį, kuris pirmuoju atveju paprasčiausiai pateikiamas (o nenaudojamas), o antruoju atveju buvo duota nuoroda (rodyklė) į tą kintamąjį.

Jūsų (int & x), beje, neveiktų C. Tai daro kažkas, ką daro tik C ++, ir tai taip pat reiškia, kad iš skambinančiojo negalite pamatyti, kad skambinantysis gali pakeisti šį kintamąjį.

int x = 42;
func (x);
funcref (& x);

int func (int i) {
i = 1;
grįžti i + 2; // grįš 3, x nepakeistas (aš tik vietinis)
}
int funcref (int * i) {
* i = 1;
grįžti * i + 2; // grįš 3, x yra nustatytas 1
}

Čia yra skambinimo pagal vertę ir skambučio pagal nuorodą pavyzdžiai. Pirmasis skambutis negali tiesiogiai pakeisti X. Funkcija keičia skambinančiojo X reikšmę, nes ji gauna ne tik to kintamojo vertę, bet ir paties kintamojo adresą.

Prie funkcijos iškvietimo pagal nuorodą:

# įtraukti 
int func1 (int x) {
grįžti x + 1;
}
int func2 (int x) {
grįžti x + 2;
}

int pagrindinis () {
int (* skambutis) (int x);

skambinti = & func1;
„printf“ („% d \ n“, skambinkite (41));

skambinti = & func2;
„printf“ („% d \ n“, skambinkite (41));
}

Kompiliavimas ir išvestis:

> cc -O3 -o fcall fcall.c
> .skambinkite
42
43

Funkcijos iškvietimas pagal nuorodą turi daug galios. Surinkimo lygyje toks skambutis yra tik vieno opkodo skambutis, todėl paprastai nereikia daugiau laiko peršokti į registre esantį adresą, nei peršokti tiesiai adresu, kuris turi būti išgaunamas iš atminties. Tai netgi gali būti greitesnis, nes tokio tipo šuoliams mums nereikia jokios prieigos prie atminties.

Praktikoje dažniausiai šokinėjame per šuolio lenteles. Kaip ir šiame pavyzdyje:

# įtraukti 
int fadd (int a, int b) {grąžinti a + b; }
int fsub (int a, int b) {grąžinti a-b; }
int fmul (int a, int b) {grąžinti a * b; }
int fdiv (int a, int b) {grąžinti a / b; }

int pagrindinis () {
// inicializuojamas šuolio lentelės „skambutis“
int (* skambutis []) (int x, int y) = {& fadd, & fsub, & fmul, & fdiv};

už (int i = 0; i 

Kompiliuoti ir išvesti:

> cc -O3 -o fcall fcall.c
> .skambinkite
15
9
36
4

Kaip matote, šios rūšies funkcijos masyvuose yra gana galingos kaip įrankis. Ir tai jūs galite išspręsti daugybę problemų. Iš tikrųjų tai gali būti naudojama kaip jungiklio / dėklo struktūra, tik sudėtingesnė ir kompaktiškesnė. Kompaktiškas yra geras, todėl jis saugo jūsų kodą talpykloje. Taigi, jei, pavyzdžiui, turite kokių nors problemų sprendimo problemų, tai gali būti vienas iš efektyviausių būdų ją išspręsti.

Mano pavyzdyje dabar prireiks labai nedaug, kad galėčiau pasidaryti nedidelę skaičiuoklę. Panašius dalykus galite naudoti modeliavimui ir net valstybinėms mašinoms, o gimtoji būsenos mašina naudoja tam tikrą šuolio mechanizmą, taip, aš čia kalbu apie GOTO. Bet tai yra dėl kito klausimo.

Bet jei turite valstybines mašinas su rekursinėmis atgalinio atšaukimo struktūromis, kurios jums gali kilti esant daugeliui PG problemų, turite po ranka įrankį, kuris padės tai išspręsti.

Be to, tai taip pat yra sprendimas, jei norite atlikti gryną funkcinį programavimą C kalba: dirbate su funkcijų lentelėmis.

Funkcinis programavimas nėra vienos kalbos sritis. Kažkas panašaus į „Schema“ ar „Lisp“ gali tai labai palaikyti, tačiau funkcijos kvietimas kintamajame, bevardyje kintamajame, tai yra „Lambda“ tiems žmonėms, apie kuriuos mėgsta tiek daug kalbėti. Tiesiog palyginkite „Lisp“ efektyvumą su tuo.

Gali būti patogiau rašyti tokius dalykus kaip labai HLL, tačiau kaina už šį patogumą yra tokia didelė, kad už prototipų kūrimo paprastai neverta vengti tokio mažo vargo; vargo aš jau jums parodė, kaip išspręsti.


Atsakymas 6:

Kaip sakė visi kiti, abi jūsų funkcijos yra tiesiog rekursyvios.

Ir jūs nieko nekviečiate remdamiesi nuoroda, bet turite kintamąjį, kuris pirmuoju atveju paprasčiausiai pateikiamas (o nenaudojamas), o antruoju atveju buvo duota nuoroda (rodyklė) į tą kintamąjį.

Jūsų (int & x), beje, neveiktų C. Tai daro kažkas, ką daro tik C ++, ir tai taip pat reiškia, kad iš skambinančiojo negalite pamatyti, kad skambinantysis gali pakeisti šį kintamąjį.

int x = 42;
func (x);
funcref (& x);

int func (int i) {
i = 1;
grįžti i + 2; // grįš 3, x nepakeistas (aš tik vietinis)
}
int funcref (int * i) {
* i = 1;
grįžti * i + 2; // grįš 3, x yra nustatytas 1
}

Čia yra skambinimo pagal vertę ir skambučio pagal nuorodą pavyzdžiai. Pirmasis skambutis negali tiesiogiai pakeisti X. Funkcija keičia skambinančiojo X reikšmę, nes ji gauna ne tik to kintamojo vertę, bet ir paties kintamojo adresą.

Prie funkcijos iškvietimo pagal nuorodą:

# įtraukti 
int func1 (int x) {
grįžti x + 1;
}
int func2 (int x) {
grįžti x + 2;
}

int pagrindinis () {
int (* skambutis) (int x);

skambinti = & func1;
„printf“ („% d \ n“, skambinkite (41));

skambinti = & func2;
„printf“ („% d \ n“, skambinkite (41));
}

Kompiliavimas ir išvestis:

> cc -O3 -o fcall fcall.c
> .skambinkite
42
43

Funkcijos iškvietimas pagal nuorodą turi daug galios. Surinkimo lygyje toks skambutis yra tik vieno opkodo skambutis, todėl paprastai nereikia daugiau laiko peršokti į registre esantį adresą, nei peršokti tiesiai adresu, kuris turi būti išgaunamas iš atminties. Tai netgi gali būti greitesnis, nes tokio tipo šuoliams mums nereikia jokios prieigos prie atminties.

Praktikoje dažniausiai šokinėjame per šuolio lenteles. Kaip ir šiame pavyzdyje:

# įtraukti 
int fadd (int a, int b) {grąžinti a + b; }
int fsub (int a, int b) {grąžinti a-b; }
int fmul (int a, int b) {grąžinti a * b; }
int fdiv (int a, int b) {grąžinti a / b; }

int pagrindinis () {
// inicializuojamas šuolio lentelės „skambutis“
int (* skambutis []) (int x, int y) = {& fadd, & fsub, & fmul, & fdiv};

už (int i = 0; i 

Kompiliuoti ir išvesti:

> cc -O3 -o fcall fcall.c
> .skambinkite
15
9
36
4

Kaip matote, šios rūšies funkcijos masyvuose yra gana galingos kaip įrankis. Ir tai jūs galite išspręsti daugybę problemų. Iš tikrųjų tai gali būti naudojama kaip jungiklio / dėklo struktūra, tik sudėtingesnė ir kompaktiškesnė. Kompaktiškas yra geras, todėl jis saugo jūsų kodą talpykloje. Taigi, jei, pavyzdžiui, turite kokių nors problemų sprendimo problemų, tai gali būti vienas iš efektyviausių būdų ją išspręsti.

Mano pavyzdyje dabar prireiks labai nedaug, kad galėčiau pasidaryti nedidelę skaičiuoklę. Panašius dalykus galite naudoti modeliavimui ir net valstybinėms mašinoms, o gimtoji būsenos mašina naudoja tam tikrą šuolio mechanizmą, taip, aš čia kalbu apie GOTO. Bet tai yra dėl kito klausimo.

Bet jei turite valstybines mašinas su rekursinėmis atgalinio atšaukimo struktūromis, kurios jums gali kilti esant daugeliui PG problemų, turite po ranka įrankį, kuris padės tai išspręsti.

Be to, tai taip pat yra sprendimas, jei norite atlikti gryną funkcinį programavimą C kalba: dirbate su funkcijų lentelėmis.

Funkcinis programavimas nėra vienos kalbos sritis. Kažkas panašaus į „Schema“ ar „Lisp“ gali tai labai palaikyti, tačiau funkcijos kvietimas kintamajame, bevardyje kintamajame, tai yra „Lambda“ tiems žmonėms, apie kuriuos mėgsta tiek daug kalbėti. Tiesiog palyginkite „Lisp“ efektyvumą su tuo.

Gali būti patogiau rašyti tokius dalykus kaip labai HLL, tačiau kaina už šį patogumą yra tokia didelė, kad už prototipų kūrimo paprastai neverta vengti tokio mažo vargo; vargo aš jau jums parodė, kaip išspręsti.


Atsakymas 7:

Kaip sakė visi kiti, abi jūsų funkcijos yra tiesiog rekursyvios.

Ir jūs nieko nekviečiate remdamiesi nuoroda, bet turite kintamąjį, kuris pirmuoju atveju paprasčiausiai pateikiamas (o nenaudojamas), o antruoju atveju buvo duota nuoroda (rodyklė) į tą kintamąjį.

Jūsų (int & x), beje, neveiktų C. Tai daro kažkas, ką daro tik C ++, ir tai taip pat reiškia, kad iš skambinančiojo negalite pamatyti, kad skambinantysis gali pakeisti šį kintamąjį.

int x = 42;
func (x);
funcref (& x);

int func (int i) {
i = 1;
grįžti i + 2; // grįš 3, x nepakeistas (aš tik vietinis)
}
int funcref (int * i) {
* i = 1;
grįžti * i + 2; // grįš 3, x yra nustatytas 1
}

Čia yra skambinimo pagal vertę ir skambučio pagal nuorodą pavyzdžiai. Pirmasis skambutis negali tiesiogiai pakeisti X. Funkcija keičia skambinančiojo X reikšmę, nes ji gauna ne tik to kintamojo vertę, bet ir paties kintamojo adresą.

Prie funkcijos iškvietimo pagal nuorodą:

# įtraukti 
int func1 (int x) {
grįžti x + 1;
}
int func2 (int x) {
grįžti x + 2;
}

int pagrindinis () {
int (* skambutis) (int x);

skambinti = & func1;
„printf“ („% d \ n“, skambinkite (41));

skambinti = & func2;
„printf“ („% d \ n“, skambinkite (41));
}

Kompiliavimas ir išvestis:

> cc -O3 -o fcall fcall.c
> .skambinkite
42
43

Funkcijos iškvietimas pagal nuorodą turi daug galios. Surinkimo lygyje toks skambutis yra tik vieno opkodo skambutis, todėl paprastai nereikia daugiau laiko peršokti į registre esantį adresą, nei peršokti tiesiai adresu, kuris turi būti išgaunamas iš atminties. Tai netgi gali būti greitesnis, nes tokio tipo šuoliams mums nereikia jokios prieigos prie atminties.

Praktikoje dažniausiai šokinėjame per šuolio lenteles. Kaip ir šiame pavyzdyje:

# įtraukti 
int fadd (int a, int b) {grąžinti a + b; }
int fsub (int a, int b) {grąžinti a-b; }
int fmul (int a, int b) {grąžinti a * b; }
int fdiv (int a, int b) {grąžinti a / b; }

int pagrindinis () {
// inicializuojamas šuolio lentelės „skambutis“
int (* skambutis []) (int x, int y) = {& fadd, & fsub, & fmul, & fdiv};

už (int i = 0; i 

Kompiliuoti ir išvesti:

> cc -O3 -o fcall fcall.c
> .skambinkite
15
9
36
4

Kaip matote, šios rūšies funkcijos masyvuose yra gana galingos kaip įrankis. Ir tai jūs galite išspręsti daugybę problemų. Iš tikrųjų tai gali būti naudojama kaip jungiklio / dėklo struktūra, tik sudėtingesnė ir kompaktiškesnė. Kompaktiškas yra geras, todėl jis saugo jūsų kodą talpykloje. Taigi, jei, pavyzdžiui, turite kokių nors problemų sprendimo problemų, tai gali būti vienas iš efektyviausių būdų ją išspręsti.

Mano pavyzdyje dabar prireiks labai nedaug, kad galėčiau pasidaryti nedidelę skaičiuoklę. Panašius dalykus galite naudoti modeliavimui ir net valstybinėms mašinoms, o gimtoji būsenos mašina naudoja tam tikrą šuolio mechanizmą, taip, aš čia kalbu apie GOTO. Bet tai yra dėl kito klausimo.

Bet jei turite valstybines mašinas su rekursinėmis atgalinio atšaukimo struktūromis, kurios jums gali kilti esant daugeliui PG problemų, turite po ranka įrankį, kuris padės tai išspręsti.

Be to, tai taip pat yra sprendimas, jei norite atlikti gryną funkcinį programavimą C kalba: dirbate su funkcijų lentelėmis.

Funkcinis programavimas nėra vienos kalbos sritis. Kažkas panašaus į „Schema“ ar „Lisp“ gali tai labai palaikyti, tačiau funkcijos kvietimas kintamajame, bevardyje kintamajame, tai yra „Lambda“ tiems žmonėms, apie kuriuos mėgsta tiek daug kalbėti. Tiesiog palyginkite „Lisp“ efektyvumą su tuo.

Gali būti patogiau rašyti tokius dalykus kaip labai HLL, tačiau kaina už šį patogumą yra tokia didelė, kad už prototipų kūrimo paprastai neverta vengti tokio mažo vargo; vargo aš jau jums parodė, kaip išspręsti.