У чым розніца паміж рэкурсіўнай функцыяй і выклікам функцыі з выкарыстаннем спасылкі C / C ++?


адказ 1:

Абедзве функцыі з'яўляюцца рэкурсіўнымі. Асноўнае адрозненне заключаецца ў тым, што аргумент другой функцыі ставіцца да значэння, а не да непасрэдна перададзенага значэння. Гэта азначае, што прамае значэнне не капіруецца кожны раз пры выкліку функцыі. (Гэты тып уласцівасці менш карысны для значэнняў int і будзе больш карысным для структур або класаў, каб пазбегнуць капіявання цэлых кропак дадзеных кожны раз, калі функцыя паўтараецца.)

Адзінае іншае адрозненне заключаецца ў тым, што другая функцыя не вяртае значэнне і першая.


адказ 2:

Абодва рэкурсіўныя функцыі выкліку. Адзін вяртае вынік рэкурсіўнага выкліку, а другі - не. Абодва дзейнічаюць (і карысна), калі выкарыстоўваецца рэкурсія.

Відавочна, што абодва прыкладу ўтрымліваюць памылку, таму што вы ніколі не скончыце рэкурсію (і ў канчатковым выніку выклікае перапаўненне стэка), але я мяркую, што вы дасце толькі выдуманы код для кадравання пытання.


адказ 3:

Я перамагаю мёртвага каня, кажучы вам амаль тое ж самае, што і іншыя адказы, але метад анулявання будзе рабіць тое ж самае зноў і зноў, выкарыстоўваючы метад вяртання для назапашвання вынікаў кожнага рэкурсіўнага выкліку.

У выпадку, калі вы не ведалі, што гэта не добрыя рэкурсіўныя функцыі, бо няма базавага выпадку і перапаўненне стэка (альбо перапаўненне буфера, у залежнасці ад таго, з кім вы размаўляеце), што выклікае мноства праблем з памяццю пры працы на C, вы павінны быць асцярожнымі. Вам патрэбна базавая справа ў рэкурсіўнай функцыі, каб высветліць прычыну.


адказ 4:

Акрамя аднаго са зваротным значэннем, гэта пераважна перавага / чытальнасць.

Хоць я б у асноўным бачыў гэта як дрэннае практыкаванне ў імператыўным праграмаванні. Для рэчаў, дзе рэкурсія мае вырашальнае значэнне, вы, хутчэй за ўсё, укараніце больш складаныя матэматычныя алгарытмы. Таму функцыянальнае праграмаванне значна больш падыходзіць для аналізу ўласцівасцей і часу выканання. Калі справа даходзіць да фактычнага выкарыстання, у імператыўных мовах праграмавання ёсць лепшыя паняцці, чым рэкурсія.


адказ 5:

Як усе астатнія казалі, абедзве функцыі проста рэкурсіўныя.

І вы не называеце нічога ў якасці спасылкі, замест гэтага ў вас ёсць пераменная, якая была проста паказана (а не выкарыстоўвалася) у першым выпадку і паказана ў якасці спасылкі (паказальніка) на гэтую зменную ў другім выпадку.

Дарэчы, ваш (int & x) не працуе ў C. Гэта тое, што робіць толькі C ++, і гэта таксама азначае, што вы не можаце бачыць ад абанента, які выкліканы ўдзельнік можа змяніць гэтую зменную.

int x = 42; func (x); funcref (& x); int func (int i) {i = 1; вяртанне i + 2; // вяртае 3, х не мяняецца (толькі мясцовы)} int funcref (int * i) {* i = 1; вяртанне * i + 2; // вяртае 3, х усталёўваецца на 1}

Вось прыклады званка па значэнні і выкліку па спасылцы. Першы званок не можа змяніць X наўпрост. Радыё спасылка змяняе значэнне X ад выклікае абанента, таму што ён прымае не толькі значэнне гэтай зменнай, але і сам адрас гэтай зменнай.

Зараз, каб выклікаць функцыю па спасылцы:

#include int func1 (int x) {вяртанне х + 1; } int func2 (int x) {вяртанне х + 2; } int main () {int (* выклік) (int x); выклік = & func1; printf ("% d \ n", выклік (41)); выклік = & func2; printf ("% d \ n", выклік (41)); }

Складанне і вывад:

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

Выклік функцыі ў якасці эталона мае вялікую сілу. На ўзроўні асемблера такі званок - гэта проста званок з опкода, таму звычайна не патрабуецца больш часу прапускаць адрас у рэестры, чым непасрэдна пераходзіць на адрас, які трэба атрымаць з памяці. Гэта можа быць яшчэ хутчэй, бо для гэтага скачка нам не патрэбны доступ да памяці.

На практыцы мы звычайна пераскокваем скачковыя сталы. Як у гэтым прыкладзе тут:

#include int fadd (int a, int b) {вяртанне a + b; } int fsub (int a, int b) {вярнуцца ab; } int fmul (int a, int b) {вярнуць a * b; } int fdiv (int a, int b) {вярнуць a / b; } int main () {// ініцыялізацыя табліцы скачкоў "выклік" int (* выклік []) (int x, int y) = {& fadd, & fsub, & fmul, & fdiv}; для (int i = 0; i

Складанне і вывад:

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

Як бачыце, гэтыя функцыі ў масівах вельмі магутныя ў якасці інструмента. І вы можаце выкарыстоўваць яго для вырашэння многіх праблем. На самай справе, гэта можа быць выкарыстана як структура перамыкача / корпуса, якая толькі больш складаная і значна больш кампактная. Кампактнасць добрая, яна захоўвае ваш код у кэшы. Напрыклад, калі ў вас ёсць праблемы з рашэннем, гэта адзін з самых эфектыўных спосабаў іх вырашэння.

У маім прыкладзе вельмі мала спатрэбіцца, каб ператварыць яго ў невялікі калькулятар. Вы можаце выкарыстоўваць такія рэчы для мадэлявання і нават для дзяржаўных машын, у той час як родная дзяржаўная машына выкарыстоўвае нейкі механізм скачкоў. Так, я кажу пра GOTO. Але гэта ўжо іншае пытанне.

Аднак калі ў вас ёсць дзяржаўныя машыны з рэкурсіўнымі структурамі зваротнага званка, якія могуць узнікнуць з многімі праблемамі з ІІ, у вас ёсць інструмент для вырашэння гэтага пытання.

Акрамя таго, гэта таксама рашэнне, калі вы хочаце ажыццявіць чыстае функцыянаванне праграмавання на C: Вы працуеце з функцыйнымі табліцамі.

Функцыянальнае праграмаванне не з'яўляецца даменам адной мовы. Ён можа падтрымліваць нешта накшталт "Схема" альбо "Лісп", але выклік функцыі ў зменнай, у неназванай зменнай - гэта лямбда, пра якую людзі любяць гаварыць. Проста параўнайце эфектыўнасць Lisp з ім.

Магчыма, зручней пісаць такія рэчы ў HLL, але цана гэтага выгоды настолькі высокая, што, за межамі прататыпаў, звычайна не варта пазбягаць гэтых дробных клопатаў. Я ўжо паказваў вам гнеў, як гэта вырашыць.


адказ 6:

Як усе астатнія казалі, абедзве функцыі проста рэкурсіўныя.

І вы не называеце нічога ў якасці спасылкі, замест гэтага ў вас ёсць пераменная, якая была проста паказана (а не выкарыстоўвалася) у першым выпадку і паказана ў якасці спасылкі (паказальніка) на гэтую зменную ў другім выпадку.

Дарэчы, ваш (int & x) не працуе ў C. Гэта тое, што робіць толькі C ++, і гэта таксама азначае, што вы не можаце бачыць ад абанента, які выкліканы ўдзельнік можа змяніць гэтую зменную.

int x = 42; func (x); funcref (& x); int func (int i) {i = 1; вяртанне i + 2; // вяртае 3, х не мяняецца (толькі мясцовы)} int funcref (int * i) {* i = 1; вяртанне * i + 2; // вяртае 3, х усталёўваецца на 1}

Вось прыклады званка па значэнні і выкліку па спасылцы. Першы званок не можа змяніць X наўпрост. Радыё спасылка змяняе значэнне X ад выклікае абанента, таму што ён прымае не толькі значэнне гэтай зменнай, але і сам адрас гэтай зменнай.

Зараз, каб выклікаць функцыю па спасылцы:

#include int func1 (int x) {вяртанне х + 1; } int func2 (int x) {вяртанне х + 2; } int main () {int (* выклік) (int x); выклік = & func1; printf ("% d \ n", выклік (41)); выклік = & func2; printf ("% d \ n", выклік (41)); }

Складанне і вывад:

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

Выклік функцыі ў якасці эталона мае вялікую сілу. На ўзроўні асемблера такі званок - гэта проста званок з опкода, таму звычайна не патрабуецца больш часу прапускаць адрас у рэестры, чым непасрэдна пераходзіць на адрас, які трэба атрымаць з памяці. Гэта можа быць яшчэ хутчэй, бо для гэтага скачка нам не патрэбны доступ да памяці.

На практыцы мы звычайна пераскокваем скачковыя сталы. Як у гэтым прыкладзе тут:

#include int fadd (int a, int b) {вяртанне a + b; } int fsub (int a, int b) {вярнуцца ab; } int fmul (int a, int b) {вярнуць a * b; } int fdiv (int a, int b) {вярнуць a / b; } int main () {// ініцыялізацыя табліцы скачкоў "выклік" int (* выклік []) (int x, int y) = {& fadd, & fsub, & fmul, & fdiv}; для (int i = 0; i

Складанне і вывад:

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

Як бачыце, гэтыя функцыі ў масівах вельмі магутныя ў якасці інструмента. І вы можаце выкарыстоўваць яго для вырашэння многіх праблем. На самай справе, гэта можа быць выкарыстана як структура перамыкача / корпуса, якая толькі больш складаная і значна больш кампактная. Кампактнасць добрая, яна захоўвае ваш код у кэшы. Напрыклад, калі ў вас ёсць праблемы з рашэннем, гэта адзін з самых эфектыўных спосабаў іх вырашэння.

У маім прыкладзе вельмі мала спатрэбіцца, каб ператварыць яго ў невялікі калькулятар. Вы можаце выкарыстоўваць такія рэчы для мадэлявання і нават для дзяржаўных машын, у той час як родная дзяржаўная машына выкарыстоўвае нейкі механізм скачкоў. Так, я кажу пра GOTO. Але гэта ўжо іншае пытанне.

Аднак калі ў вас ёсць дзяржаўныя машыны з рэкурсіўнымі структурамі зваротнага званка, якія могуць узнікнуць з многімі праблемамі з ІІ, у вас ёсць інструмент для вырашэння гэтага пытання.

Акрамя таго, гэта таксама рашэнне, калі вы хочаце ажыццявіць чыстае функцыянаванне праграмавання на C: Вы працуеце з функцыйнымі табліцамі.

Функцыянальнае праграмаванне не з'яўляецца даменам адной мовы. Ён можа падтрымліваць нешта накшталт "Схема" альбо "Лісп", але выклік функцыі ў зменнай, у неназванай зменнай - гэта лямбда, пра якую людзі любяць гаварыць. Проста параўнайце эфектыўнасць Lisp з ім.

Магчыма, зручней пісаць такія рэчы ў HLL, але цана гэтага выгоды настолькі высокая, што, за межамі прататыпаў, звычайна не варта пазбягаць гэтых дробных клопатаў. Я ўжо паказваў вам гнеў, як гэта вырашыць.


адказ 7:

Як усе астатнія казалі, абедзве функцыі проста рэкурсіўныя.

І вы не называеце нічога ў якасці спасылкі, замест гэтага ў вас ёсць пераменная, якая была проста паказана (а не выкарыстоўвалася) у першым выпадку і паказана ў якасці спасылкі (паказальніка) на гэтую зменную ў другім выпадку.

Дарэчы, ваш (int & x) не працуе ў C. Гэта тое, што робіць толькі C ++, і гэта таксама азначае, што вы не можаце бачыць ад абанента, які выкліканы ўдзельнік можа змяніць гэтую зменную.

int x = 42; func (x); funcref (& x); int func (int i) {i = 1; вяртанне i + 2; // вяртае 3, х не мяняецца (толькі мясцовы)} int funcref (int * i) {* i = 1; вяртанне * i + 2; // вяртае 3, х усталёўваецца на 1}

Вось прыклады званка па значэнні і выкліку па спасылцы. Першы званок не можа змяніць X наўпрост. Радыё спасылка змяняе значэнне X ад выклікае абанента, таму што ён прымае не толькі значэнне гэтай зменнай, але і сам адрас гэтай зменнай.

Зараз, каб выклікаць функцыю па спасылцы:

#include int func1 (int x) {вяртанне х + 1; } int func2 (int x) {вяртанне х + 2; } int main () {int (* выклік) (int x); выклік = & func1; printf ("% d \ n", выклік (41)); выклік = & func2; printf ("% d \ n", выклік (41)); }

Складанне і вывад:

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

Выклік функцыі ў якасці эталона мае вялікую сілу. На ўзроўні асемблера такі званок - гэта проста званок з опкода, таму звычайна не патрабуецца больш часу прапускаць адрас у рэестры, чым непасрэдна пераходзіць на адрас, які трэба атрымаць з памяці. Гэта можа быць яшчэ хутчэй, бо для гэтага скачка нам не патрэбны доступ да памяці.

На практыцы мы звычайна пераскокваем скачковыя сталы. Як у гэтым прыкладзе тут:

#include int fadd (int a, int b) {вяртанне a + b; } int fsub (int a, int b) {вярнуцца ab; } int fmul (int a, int b) {вярнуць a * b; } int fdiv (int a, int b) {вярнуць a / b; } int main () {// ініцыялізацыя табліцы скачкоў "выклік" int (* выклік []) (int x, int y) = {& fadd, & fsub, & fmul, & fdiv}; для (int i = 0; i

Складанне і вывад:

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

Як бачыце, гэтыя функцыі ў масівах вельмі магутныя ў якасці інструмента. І вы можаце выкарыстоўваць яго для вырашэння многіх праблем. На самай справе, гэта можа быць выкарыстана як структура перамыкача / корпуса, якая толькі больш складаная і значна больш кампактная. Кампактнасць добрая, яна захоўвае ваш код у кэшы. Напрыклад, калі ў вас ёсць праблемы з рашэннем, гэта адзін з самых эфектыўных спосабаў іх вырашэння.

У маім прыкладзе вельмі мала спатрэбіцца, каб ператварыць яго ў невялікі калькулятар. Вы можаце выкарыстоўваць такія рэчы для мадэлявання і нават для дзяржаўных машын, у той час як родная дзяржаўная машына выкарыстоўвае нейкі механізм скачкоў. Так, я кажу пра GOTO. Але гэта ўжо іншае пытанне.

Аднак калі ў вас ёсць дзяржаўныя машыны з рэкурсіўнымі структурамі зваротнага званка, якія могуць узнікнуць з многімі праблемамі з ІІ, у вас ёсць інструмент для вырашэння гэтага пытання.

Акрамя таго, гэта таксама рашэнне, калі вы хочаце ажыццявіць чыстае функцыянаванне праграмавання на C: Вы працуеце з функцыйнымі табліцамі.

Функцыянальнае праграмаванне не з'яўляецца даменам адной мовы. Ён можа падтрымліваць нешта накшталт "Схема" альбо "Лісп", але выклік функцыі ў зменнай, у неназванай зменнай - гэта лямбда, пра якую людзі любяць гаварыць. Проста параўнайце эфектыўнасць Lisp з ім.

Магчыма, зручней пісаць такія рэчы ў HLL, але цана гэтага выгоды настолькі высокая, што, за межамі прататыпаў, звычайна не варта пазбягаць гэтых дробных клопатаў. Я ўжо паказваў вам гнеў, як гэта вырашыць.