namai - Interneto sąranka
Kas yra programos pavadinimas. Programavimo funkcijos

Darbo tikslas: 1) išstudijuoti funkcijų apibūdinimo taisykles; 2) įgyti funkcijų naudojimo rašant programas C++ kalba įgūdžių.

Teorinė informacija

Pagrindinis C++ programų modulis yra funkcija.

Funkcija- logiškai užbaigtas, neabejotinai sukurtas programos fragmentas, turintis pavadinimą. Funkcijos leidžia padalyti dideles skaičiavimo užduotis į mažesnes.

Kiekvienoje C++ programoje būtinai yra funkcija, vadinama main, kuri yra programos turinys. Visoms kitoms funkcijoms, jei jos yra programoje, turi būti deklaruojami prototipai – scheminiai užrašai, kurie kompiliatoriui nurodo kiekvienos programos funkcijos pavadinimą ir formą.

Funkcijos prototipo su parametrais sintaksė yra tokia:

grąžinimo_vertės_tipas funkcijos_pavadinimas (parametrų_sąrašas_su_tipo_indikacija);

Funkcijos C++ yra standartinės (bibliotekos) ir programuojamos vartotojo.

Standartinės funkcijos

Standartinių funkcijų aprašymai pateikiami failuose, įtrauktuose į programą naudojant direktyvą #include. Tokie failai vadinami antraštės failais; jie turi plėtinį h.

Nurodymas į funkcijos pavadinimą pagrindinėje programoje vadinamas funkcijos iškvietimu.

Iškvietus funkcijas, atliekamas koks nors veiksmas arba apskaičiuojama kokia nors reikšmė, kuri vėliau naudojama programoje.

y = sin(x); //sinuso skaičiavimo funkcija

Funkcijos apibrėžimas

Apskritai funkcijos apibrėžiamos taip:

grąžinimo_vertės_tipas funkcijos_pavadinimas (tipas parametro_pavadinimas,..., tipas parametro_pavadinimas)

kūno_funkcija

Programuojamos funkcijos

Funkcijos, kurias pats programuotojas sukuria, supaprastina programų rašymo procesą, nes jos:

    padėti išvengti pakartotinio programavimo, nes ta pati funkcija gali būti naudojama skirtingose ​​programose;

    padidinti programos moduliškumo lygį, todėl lengviau skaityti, atlikti pakeitimus ir taisyti klaidas.

Pavyzdys9 .1. Sukurkime funkciją, kuri spausdina 65 „*“ simbolius iš eilės. Kad ši funkcija veiktų tam tikrame kontekste, ji įtraukta į firminių blankų spausdinimo programą. Programa susideda iš funkcijų: main() ir stars().

// Laiškas

#įtraukti

const int Limit=65;

tuščios žvaigždės (tuščios); // funkcijos prototipas stars ()

cout<<"Moscow Institute of Electronic Engineering"<

// Stars() funkcijos apibrėžimas

už (skaičius = 1; skaičius<=Limit; count++)

Mes pažvelgėme į paprastos funkcijos, kuri neturi argumentų ir nepateikia jokių reikšmių, pavyzdį.

Funkcijos parametrai

Pažvelkime į funkcijos parametrų naudojimo pavyzdį.

Pavyzdys9. 2. Parašykime funkcijos tarpą (), kurio argumentas bus tarpų skaičius, kurį ši funkcija turėtų spausdinti.

#define adresą "Zelenograd"

#define pavadinimas „Maskvos instituto elektronikos inžinerija“

#define skyrius "Informatika ir programavimas"

const int LIMIT=65;

#įtraukti

tuščias tarpas (int numeris);

cout<

tarpai=(LIMIT - strlen(vardas))/2; // Apskaičiuokite, kiek

// reikia tarpų

cout<

space((LIMIT - strlen(department))/2); // argumentas – išraiška

cout<

//Funkcijos žvaigždės() apibrėžimas

už (skaičius = 1; skaičius<=LIMIT; count++)

//Funkcijos tarpo() apibrėžimas

tuščias tarpas (int numeris)

už (skaičius = 1; skaičius<=number; count++)

Skaičių kintamasis vadinamas formaliuoju argumentu. Šis kintamasis įgauna tikrojo argumento reikšmę, kai funkcija iškviečiama. Kitaip tariant, formalus argumentas yra vadinamosios paprogramės apibrėžimo kintamasis, ir tikras argumentas yra konkreti reikšmė, kurią šiam kintamajam priskiria iškviečianti programa.

Jei funkcijai palaikyti reikia daugiau nei vieno argumento, kartu su funkcijos pavadinimu galite nurodyti kableliais atskirtą argumentų sąrašą:

negaliojantis spausdinimo skaičius (int i, int j)

(išeiti<<"Координаты точек”<< i << j <

Funkcijos įvesties reikšmę galima apdoroti dėl buvimo argumentas; Išvesties vertė grąžinama naudojant grąžinimo raktinį žodį.

Vartotojai, kurie yra toli nuo programavimo iš principo, retai susiduria su funkcijų ir procedūrų sąvokomis, ir jie yra siejami su kažkuo matematišku ir biurokratiniu-medicininiu. Programuojant daugelis kalbų veikia su šiomis sąvokomis, tačiau net ekspertai kartais negali aiškiai suprasti skirtumo tarp funkcijos ir procedūros. Kaip su tuo goferiu: jis yra, bet niekas jo nemato. Pažiūrėkime, ar skirtumai tokie nematomi.

Ką reiškia terminai funkcija ir procedūra?

  • Funkcija programuojant iš kitų paprogramių iškviečiama paprogramė reikiamą skaičių kartų.
  • Procedūra- pavadinta programos dalis (paprogramė), pakartotinai iškviesta iš paskesnių programos dalių reikiamą skaičių kartų.

Funkcijos ir procedūros palyginimas

Pagrindinis funkcijos ir procedūros skirtumas yra jos grąžinamas rezultatas. Tiesą sakant, tiek funkcijos, tiek procedūros yra logiškai nedalomi blokai, sudarantys programos kodą. Funkcija grąžina reikšmę, procedūra daugelyje programavimo kalbų – ne arba (pavyzdžiui, C) grąžina tuščią reikšmę. Pastaruoju atveju (C) procedūra laikoma antraeiliu funkcijos variantu.

Funkcijos antraštėje yra žodis „funkcija“, identifikatorius (tikrasis funkcijos pavadinimas), pasirinktinai parametrų sąrašas ir būtinai rezultato tipas. Funkcijos turinyje turi būti operatorius, kuris funkcijos pavadinimui priskiria reikšmę, kurią ji grąžins kaip rezultatą. Procedūros antraštėje yra žodis „procedūra“, identifikatorius (procedūros pavadinimas) ir pasirinktinai parametrų sąrašas.

Funkcijos iškvietimas vykdomas kaip išraiškų dalis, kai šios išraiškos naudojamos; procedūros iškvietimui reikalingas atskiras sakinys.

Procedūra iškviečiama tik pavadinimu, o funkcijos pavadinimas yra susietas su jos verte. Algoritminėse diagramose funkcijos iškvietimas vaizduojamas išvesties bloke arba proceso bloke, procedūros iškvietimas – specialiame „iš anksto apibrėžto proceso“ bloke.

Skirtumas tarp funkcijos ir procedūros programuojant yra toks:

  • Funkcija grąžina reikšmę, procedūra – ne.
  • Funkcijos antraštėje turi būti rezultato tipas.
  • Funkcijos turinyje turi būti sakinys, kuris funkcijos pavadinimui priskiria reikšmę.
  • Norint iškviesti procedūrą, reikalingas atskiras sakinys; funkcijos iškvietimas galimas kaip išraiškos dalis.
  • Procedūros pavadinimas reikalingas norint ją iškviesti, funkcijos pavadinimas reikalingas reikšmei priskirti.
  • Algoritminėse diagramose procedūros iškvietimas vaizduojamas atskirame bloke, funkcijos iškvietimas – proceso arba išvesties bloke.

Bet kurios kompiuterinės programos pagrindas yra algoritmai, išreikšti komandomis. Žmogus, rašantis kodą, sako: imk tai, daryk su juo tą, tą ir aną, tada išvesk rezultatą ten ir eik ilsėtis. Taigi, kad komandos programose nesusijungtų į vieną netvarką ir galėtų sąveikauti viena su kita, jos sugrupuojamos į vadinamąsias funkcijas ir procedūras. Su šiomis sąvokomis susipažinsime.

Kas yra funkcija

Funkcijų pavadinimai naudojami: 1) dokumentacijai kurti; 2) API, tai yra sąsaja, skirta prisijungti prie programos arba visos bet kurios programos operacinės sistemos. Todėl prasminga dar kartą priminti, kad šie pavadinimai turėtų būti pateikiami suprantamai ir, jei įmanoma, atitinkančius atliekamus veiksmus.

Apibendrinkime

Taigi, funkcijos yra tam tikri algoritmų grupavimo konteineriai. Jie:

  1. atsakingas už konkrečias užduotis;
  2. bendrauti su kitais objektais;
  3. yra šiuolaikinio programavimo konceptualus pagrindas, kad ir kaip apgailėtinai tai skambėtų.

Procedūros iš tikrųjų yra tos pačios funkcijos, nors ir „tuščios“, kurios nieko negrąžina (tai yra pagrindinis jų skirtumas). Tai pagalbiniai įrankiai, skirti atlikti įprastinius veiksmus, taip pat sutaupyti vietos, pastangų ir laiko.

Ankstesnės publikacijos:

Ne veltui šį straipsnį pavadinau „Funkcijos kaip neatsiejama programavimo dalis“, nes be jų, mano nuomone, jokia kalba neturi teisės egzistuoti. Kas tai? Funkcija yra pagrindinis gerai parašytos programos komponentas. Tai ne tik palengvina kodo skaitymą, bet ir radikaliai pakeičia struktūrinio programavimo idėją. Funkcijų pagalba galite pakartotinai panaudoti atskiras programos dalis, perduodant joms bet kokius parametrus. Jokia rimta programa neįsivaizduojama be šio programavimo elemento stebuklo.

Trumpai papasakosiu kaip tai veikia. Funkcija yra instrukcijų, kurias gali iškviesti jūsų programa, blokas. Pasiekus šio bloko antraštę (funkcijos pavadinimą), jis vykdomas ir atlieka kai kuriuos programuotojo nurodytus veiksmus. Po to šis blokas grąžina gautą reikšmę ir perduoda ją pagrindinei programai. Leiskite man paaiškinti praktiškai.

Grubiai tariant, tai atrodo taip. Leiskite trumpai paaiškinti. Mes sukuriame tam tikrą kintamąjį ir priskiriame jam funkcijos myfunc vykdymo rezultatą, kuris savo ruožtu apskaičiuoja tam tikro skaičiaus kvadratūros reikšmę. Funkcijos nevykdomos iš karto paleidus programą, o vykdomos tik tada, kai jos iškviečiamos. Tai gali būti šiek tiek painu, bet taip yra.

Kaip iškviesti funkciją?

Norėdami iškviesti funkciją, turite ją sukurti. Nors yra ir įmontuotų funkcijų. Pavyzdžiui tai: cos, sin, md5, skaičius, abs ir taip toliau. Norėdami juos iškviesti, tereikia kintamajam priskirti norimą reikšmę.

Funkcijos argumentas yra reikšmė, kurią jam perduodate, kai ją iškviečiate. Funkcijos argumentai dedami skliausteliuose. Kurdami funkciją, nurodote sąlyginius argumentų pavadinimus. Tada šie pavadinimai gali būti naudojami funkcijos turinyje kaip vietiniai kintamieji. Grįžkime prie funkcijų, kurias kuria pats vartotojas. Tai daroma labai lengvai. Pirmiausia sukuriamas funkcijos korpusas:

Funkcija hello() ( aidas "Sveikas, pasauli!"; )

Tada mes jai skambiname. Be to, jei jis neturi parametrų, mes tiesiog dedame skliaustus. Norėdami iškviesti šią funkciją, naudojame tik eilutę: Sveiki();. Bet kuri funkcija taip pat gali grąžinti reikšmę naudodama rezervuotą žodį grąžinti. Šis teiginys sustabdo funkcijos vykdymą ir siunčia grąžinamąją reikšmę kviečiančiai programai. function sum($first, $second) ($r=$first + $second; return $r;) echo sum(2,5); programos vykdymo rezultatas bus lygus 7. Vietiniai ir globalūs kintamieji

Kaip ir bet kurioje kitoje programavimo kalboje, yra kintamųjų, kurie pasiekiami tik funkcijoje, ir kintamųjų, kuriuos galima rasti pačios programos kode. Tokie kintamieji atitinkamai vadinami vietiniais ir globaliais. Funkcijos viduje negalite tiesiog pasiekti kintamojo, kuris buvo sukurtas už funkcijos ribų. Jei bandysite tai padaryti, sukursite naują kintamąjį tuo pačiu pavadinimu, bet vietinį šiai funkcijai.

$per="Dima"; function primer() // Atlieka: rodo vietinį kintamąjį ( echo "Mano vardas yra ".$per; ) echo primer();

Tokiu atveju ekrane pasirodys frazė „Mano vardas yra“. Tai reiškia, kad $per kintamasis buvo sukurtas pradmenų funkcijos viduje ir pagal numatytuosius nustatymus jam buvo priskirta nulinė reikšmė. Norint išvengti tokių kliūčių, reikia naudoti operatorių globalus. Atitinkamai pataisykime aukščiau pateiktą kodą:

$per="Dima"; function primer() // Atlieka: rodomas pasaulinis kintamasis ( global $per; echo "Mano vardas yra ".$per; ) echo primer();

Dabar viskas turėtų būti gerai – problema išspręsta. Tik nepamirškite, kad jei funkcija pakeis išorinio kintamojo reikšmę, toks pakeitimas paveiks visą programą, todėl šį operatorių reikia naudoti atsargiai!

Dviejų ar daugiau argumentų funkcijos

Kai kurie funkcijai perduodami argumentai gali būti pasirenkami, todėl funkcija tampa mažiau reikli. Toliau pateiktas pavyzdys tai aiškiai iliustruoja:

… funkcijos šriftas($tekstas, $size=5) // Do: išvesties šrifto dydis ( echo " „.$tekstas“."; ) šriftas ("Sveiki
",1); šriftas ("Sveiki
",2); šriftas ("Sveiki
",3); šriftas ("Sveiki
",4); šriftas ("Sveiki
",5); šriftas ("Sveiki
",6); šriftas ("Sveiki
");

Pagal numatytuosius nustatymus šrifto dydis yra 5. Jei praleisime antrą funkcijos parametrą, jis bus lygus šiai reikšmei.

Išvada

Prieš atsisveikindamas noriu atkreipti jūsų dėmesį į vieną patarimą. Jį sudaro visų įrašytų funkcijų įtraukimas į vieną failą (pavyzdžiui, function.php). Ir tada į failą, kuriame reikia iškviesti funkciją, tereikia įtraukti function.php ir viskas bus paruošta naudoti. Taip bus daug lengviau suprasti jūsų programos logiką. Norėdami prisijungti, naudokite:

include_once("funkcija.php");

request_once("funkcija.php");

Jei suprantate šiame straipsnyje aptariamo klausimo esmę, esu tikras, kad galėsite lengvai naudotis savo programų funkcijomis. Dar kartą tai daroma, kad jie būtų lengviau pritaikomi ir pakartotinai naudojami.

Tai trečiasis straipsnis iš serijos „Kategorijų teorija programuotojams“.

Kam reikalingi tipai?

Bendruomenėje yra tam tikrų nesutarimų dėl statinio ir dinaminio spausdinimo pranašumų bei stipraus ir silpno spausdinimo pranašumų. Leiskite man iliustruoti spausdinimo pasirinkimą minties eksperimentu. Įsivaizduokite milijonus beždžionių su klaviatūromis, džiaugsmingai spaudžiančias atsitiktinius klavišus, rašančias, kompiliuojančias ir paleidžiančias programas.

Naudojant mašinos kalbą, bet koks beždžionių sukurtas baitų derinys bus priimtas ir vykdomas. Tačiau aukšto lygio kalbose labai vertinama, kad kompiliatorius sugeba aptikti leksikos ir gramatines klaidas. Daugelis programų bus tiesiog atmesti, o beždžionės liks be bananų, bet likusios turės daugiau galimybių būti prasmingos. Tipo tikrinimas yra dar viena kliūtis nuo absurdiškų programų. Be to, dinamiškai įvestose kalbose tipo neatitikimai bus aptikti tik vykdymo metu, o stipriai įvestose statiškai patikrintose kalbose tipo neatitikimai aptinkami kompiliavimo metu, o tai pašalina daugybę neteisingų programų dar nespėjus jas paleisti.

Taigi kyla klausimas, ar norime, kad beždžionės būtų laimingos, ar kurtume teisingas programas?
(vertėjo pastaba: nereikia įsižeisti, autorė tiesiog mėgsta mažiau nuobodžias metaforas nei RNG ir „atsitiktinės baitų sekos“, o programuotojų nevadina beždžionėmis).

Paprastai spausdinimo beždžionės minties eksperimento tikslas yra sukurti visus Šekspyro kūrinius (vertėjo pastaba: arba Tolstojaus karas ir taika). Rašybos ir gramatikos tikrinimas labai padidins sėkmės galimybes. Tipo tikrinimo analogas eina dar toliau: Romeo paskelbus žmogumi, tipo tikrinimas užtikrins, kad jis neaugintų lapų ir nepagautų fotonų savo galingu gravitaciniu lauku.

Tipai reikalingi komponuojamumui

Kategorijų teorija tiria strėlių kompozicijas. Galima sujungti ne tik dvi rodykles: vienos rodyklės tikslinis objektas turi sutapti su kitos rodyklės šaltinio objektu. Programuodami rezultatus perduodame iš vienos funkcijos į kitą. Programa neveiks, jei antroji funkcija negali teisingai interpretuoti pirmosios gautų duomenų. Abi funkcijos turi derėti, kad jų sudėtis veiktų. Kuo stipresnė kalbos tipo sistema, tuo geriau šią atitiktį galima apibūdinti ir automatiškai patikrinti.

Vienintelis rimtas argumentas, kurį girdžiu prieš stiprų statinį rašymą, yra tai, kad jis gali atmesti kai kurias semantiškai teisingas programas. Praktikoje tai nutinka itin retai (vertėjo pastaba: kad nekiltų painiavos, atkreipiu dėmesį, kad autorius neatsižvelgė arba nesutinka su tuo, kad yra daug stilių, o skriptų kalbomis programuotojams pažįstamas spausdinimas ant ančių taip pat turi teisę į gyvybę . Kita vertus, ančių spausdinimas galimas griežto tipo sistemoje per šablonus, požymius, tipų klases, sąsajas, yra daug technologijų, todėl autoriaus nuomonė negali būti laikoma griežtai neteisinga.) ir bet kuriuo atveju kiekvienoje kalboje yra tam tikros užpakalinės durys, leidžiančios apeiti tipo sistemą, kai to tikrai reikia. Net Haskell turi nesaugią prievartą. Tačiau tokie dizainai turi būti naudojami protingai. Franzo Kafkos personažas Gregoras Samsa sulaužo tipo sistemą, kai virsta milžinišku vabalu, ir mes visi žinome, kuo tai baigiasi (vertėjo pastaba: blogai:).

Kitas argumentas, kurį dažnai girdžiu, yra tai, kad stiprus spausdinimas užkrauna per didelę naštą programuotojui. Galiu užjausti šią problemą, nes pats esu parašęs keletą iteratoriaus deklaracijų C++, išskyrus tai, kad yra technologija, tipo išvada, kuri leidžia kompiliatoriui padaryti išvadą apie daugumą tipų iš konteksto, kuriame jie naudojami. C++ kalboje galite deklaruoti kintamąjį auto, o kompiliatorius nustatys tipą už jus.

Haskell, išskyrus retus atvejus, tipo komentarai yra neprivalomi. Programuotojai vis tiek linkę juos naudoti, nes tipai gali daug pasakyti apie kodo semantiką, o tipo deklaracijos padeda suprasti kompiliavimo klaidas. Įprasta Haskell praktika yra pradėti projektą kuriant tipus. Vėliau tipo komentarai sudaro diegimo pagrindą ir tampa kompiliatoriaus garantuojamais komentarais.

Stiprus statinis spausdinimas dažnai naudojamas kaip pasiteisinimas netikrinti kodą. Kartais išgirsite Haskell programuotojus sakant: „Jei kodas kuriamas, tai teisinga“. Žinoma, nėra jokios garantijos, kad programa, kurios tipas yra teisingas, yra teisinga, nes pateikia teisingą rezultatą. Dėl tokio požiūrio daugelyje tyrimų Haskell kodo kokybe gerokai nepralenkė kitų kalbų, kaip galima tikėtis. Atrodo, kad komercinėje aplinkoje poreikis taisyti klaidas egzistuoja tik iki tam tikro kokybės lygio, kuris daugiausia susijęs su programinės įrangos kūrimo ekonomika ir galutinio vartotojo tolerancija, ir turi labai mažai ką bendro su programavimo kalba ar plėtra. metodika. Geresnė priemonė būtų įvertinti, kiek projektų atsilieka nuo grafiko arba jie pristatomi su labai sumažintu funkcionalumu.

Dabar kalbame apie teiginį, kad vienetų testavimas gali pakeisti griežtą spausdinimą. Pažvelkime į įprastą pertvarkymo praktiką stipriai įvestose kalbose: argumento tipo pakeitimas funkcija. Stipriai įvestomis kalbomis pakanka pakeisti šios funkcijos deklaraciją, o tada ištaisyti kūrimo klaidas. Silpnai įvestomis kalbomis fakto, kad funkcija dabar laukia kitų duomenų, negalima priskirti skambintojui.

Vieneto testavimas gali pastebėti kai kuriuos neatitikimus, tačiau testavimas beveik visada yra tikimybinis, o ne deterministinis procesas (vertėjo pastaba: galbūt jie turėjo omenyje testų rinkinį: aprėpiate ne visas įmanomas įvestis, o tam tikrą reprezentatyvų pavyzdį.) Testavimas yra prastas teisingumo įrodymo pakaitalas.

Kas yra tipai?

Paprasčiausias tipų apibūdinimas yra tas, kad tai yra reikšmių rinkiniai. Tipas Bool (atminkite, konkretūs tipai prasideda didžiosiomis raidėmis Haskell) atitinka dviejų elementų rinkinį: True ir False. Char tipas yra visų unikodo simbolių rinkinys, pvz., "a" arba "ą".

Rinkiniai gali būti baigtiniai arba begaliniai. Stygos tipas, kuris iš esmės yra simbolių sąrašo sinonimas, yra begalinio rinkinio pavyzdys.

Kai deklaruojame x sveikuoju skaičiumi:
x::Sveikasis skaičius
sakome, kad tai yra sveikųjų skaičių aibės elementas. Sveikasis skaičius Haskell yra begalinis skaičius ir gali būti naudojamas bet kokio tikslumo aritmetikai. Taip pat yra baigtinis Int rinkinys, atitinkantis mašinos tipą, pavyzdžiui, int C++.

Yra keletas subtilybių, dėl kurių sunku prilyginti tipus su rinkiniais. Yra problemų su polimorfinėmis funkcijomis, turinčiomis ciklinius apibrėžimus, taip pat dėl ​​to, kad negalite turėti visų rinkinių rinkinio; bet, kaip ir žadėjau, nebūsiu griežtas matematikas. Svarbu tai, kad yra rinkinių kategorija, vadinama Set, ir mes su ja dirbsime.
Aibėje objektai yra aibės, o morfizmai (rodyklės) yra funkcijos.

Rinkinys yra ypatinga kategorija, nes galime pažvelgti į jo objektų vidų ir tai padės mums daug ką suprasti intuityviai. Pavyzdžiui, žinome, kad tuščias rinkinys neturi elementų. Žinome, kad yra specialūs vieno elemento rinkiniai. Žinome, kad funkcijos susieja vieno rinkinio elementus su kitos. Jie gali susieti du elementus į vieną, bet ne vieną elementą į du. Žinome, kad tapatumo funkcija susieja kiekvieną aibės elementą į save ir pan. Aš planuoju palaipsniui pamiršti visą šią informaciją ir visas šias sąvokas išreikšti grynai kategoriška forma, ty daiktais ir rodyklėmis.

Idealiame pasaulyje galėtume tiesiog pasakyti, kad Haskell tipai yra aibės, o funkcijos Haskell yra matematinės funkcijos tarp jų. Yra tik viena nedidelė problema: matematinė funkcija nevykdo jokio kodo – ji žino tik atsakymą. Haskell funkcija turi apskaičiuoti atsakymą. Tai nėra problema, jei atsakymą galima gauti atlikus baigtinį žingsnių skaičių, nesvarbu, koks didelis. Tačiau kai kurie skaičiavimai apima rekursiją, ir jie gali niekada nesibaigti. Negalime tiesiog neleisti nesibaigiančių funkcijų Haskell, nes atskirti, ar funkcija baigiasi, ar ne – garsioji sustabdymo problema – neįmanoma išspręsti. Štai kodėl kompiuterių mokslininkai sugalvojo puikią idėją arba nešvarų įsilaužimą, priklausomai nuo jūsų požiūrio, išplėsti kiekvieną tipą su specialia verte, vadinama dugnu. (vertėjo pastaba: šis terminas (apačioje) rusiškai skamba kvailai, jei kas žino gerą variantą, pasiūlykite.), kuris žymimas _|_ arba Unikode ⊥. Ši „reikšmė“ atitinka nebaigtą skaičiavimą. Taigi funkcija deklaruojama taip:
f::Bool -> Bool
gali grąžinti True, False arba _|_; pastarasis reiškia, kad funkcija niekada neužbaigia.

Įdomu tai, kad priėmus dugną į tipo sistemą, patogu kiekvieną vykdymo klaidą traktuoti kaip apačią ir netgi leisti funkcijai aiškiai grįžti į apačią. Pastaroji paprastai atliekama naudojant neapibrėžtą išraišką:
f::Bool -> Bool f x = neapibrėžta
Šis apibrėžimas praeina tipo patikrinimą, nes neapibrėžtas įvertinimas yra apačia, kuris įtrauktas į visus tipus, įskaitant Bool. Jūs netgi galite parašyti:
f::Bool -> Bool f = neapibrėžta
(be x), nes apačioje taip pat yra Bool -> Bool tipo narys.

Funkcijos, kurios gali grąžinti apačią, vadinamos dalinėmis, o ne įprastomis funkcijomis, kurios pateikia tinkamus visų galimų argumentų rezultatus.

Dėl apačios Haskell tipų ir funkcijų kategorija vadinama Hask, o ne Set. Žvelgiant iš teorinės pusės, tai yra begalės komplikacijų šaltinis, todėl šiuo metu naudosiu savo mėsininko peilį ir vadinsiu tai diena. Žvelgiant iš pragmatinio taško, galima nepaisyti nesibaigiančių funkcijų ir dugno ir traktuoti Hask kaip visavertį Rinkinį.

Kodėl mums reikalingas matematinis modelis?

Kaip programuotojas, esate susipažinęs su programavimo kalbos sintaksė ir gramatika. Šie kalbos aspektai paprastai formaliai aprašomi pačioje kalbos specifikacijos pradžioje. Tačiau kalbos prasmę ir semantiką nusakyti daug sunkiau; šis aprašymas užima daug daugiau puslapių, retai būna pakankamai formalus ir beveik niekada nėra baigtas. Taigi nesibaigiančios diskusijos tarp kalbos teisininkų ir visa knygų pramonė, skirta kalbos standartų subtilybėms aiškinti.

Yra formalių priemonių kalbos semantikai apibūdinti, tačiau dėl savo sudėtingumo jos dažniausiai naudojamos supaprastintoms, akademinėms kalboms, o ne tikriesiems pramoninio programavimo milžinams. Viena iš šių įrankių vadinama operacine semantika ir apibūdina programos vykdymo mechaniką. Jis apibrėžia formalizuotą, idealizuotą interpretatorių. Pramoninių kalbų, tokių kaip C++, semantika paprastai apibūdinama naudojant neformalius samprotavimus, dažnai kaip „abstrakčią mašiną“.

Problema ta, kad labai sunku ką nors įrodyti apie programas, kurios naudoja operacinę semantiką. Norėdami parodyti programos savybę, iš esmės turite ją „paleisti“ per idealizuotą interpretatorių.

Nesvarbu, kad programuotojai niekada formaliai neįrodo teisingumo. Mes visada „galvojame“, kad rašome tinkamas programas. Niekas nesėdi prie klaviatūros sakydamas: „O, aš tiesiog parašysiu kelias kodo eilutes ir žiūrėsiu, kas atsitiks“. (vertėjo pastaba: o, jei tik...) Tikime, kad mūsų parašytas kodas atliks tam tikrus veiksmus, kurie duos norimus rezultatus. Paprastai labai nustembame, jei taip nėra. Tai reiškia, kad mes iš tikrųjų galvojame apie programas, kurias rašome, ir paprastai tai darome naudodami vertėją savo galvose. Tiesiog labai sunku sekti visus kintamuosius. Kompiuteriai gerai vykdo programas, o žmonės ne! Jei būtume, mums nereikėtų kompiuterių.

Bet yra alternatyva. Ji vadinama denotacine semantika ir remiasi matematika. Denotacinėje semantikoje kiekvienai kalbinei konstrukcijai aprašoma matematinė interpretacija. Taigi, jei norite įrodyti programos savybę, tiesiog įrodykite matematinę teoremą. Jūs manote, kad teoremas įrodyti sunku, bet iš tikrųjų mes, žmonės, matematinius metodus kuriame tūkstančius metų, todėl sukaupta daug žinių, kurias galima panaudoti. Be to, lyginant su teoremomis, kurias įrodo profesionalūs matematikai, problemos, su kuriomis susiduriame programuodami, yra gana paprastos, jei ne nereikšmingos. (vertėjo pastaba: norėdamas įrodyti, autorius nesistengia įžeisti programuotojų.)

Apsvarstykite faktorialinės funkcijos apibrėžimą Haskell, kalbos, kuri lengvai tinka denotacinės semantikai:
faktas n = produktas
Išraiška yra sveikųjų skaičių nuo 1 iki n sąrašas. Produkto funkcija padaugina visus sąrašo elementus. Lygiai taip pat, kaip faktorialo apibrėžimas, paimtas iš vadovėlio. Palyginkite tai su C:
int fact(int n) ( int i; int rezultatas = 1; for (i = 2; i<= n; ++i) result *= i; return result; }
Ar turėčiau tęsti? (vertėjo pastaba: autorius šiek tiek apgavo, atlikdamas Haskell bibliotekos funkciją. Tiesą sakant, sukčiauti nereikėjo; sąžiningas aprašymas iš esmės nėra sunkesnis):
faktas 0 = 1 faktas n = n * faktas (n - 1)
Gerai, iš karto prisipažinsiu, kad tai buvo pigus kadras! Faktorialas turi akivaizdų matematinį apibrėžimą. Sumanus skaitytojas gali paklausti: koks yra matematinis simbolio skaitymo iš klaviatūros arba paketo siuntimo tinkle modelis? Ilgą laiką tai būtų nepatogus klausimas, dėl kurio kiltų gana painūs paaiškinimai. Denotacinė semantika atrodė netinkama daugeliui svarbių problemų, reikalingų naudingoms programoms rašyti ir kurias būtų galima lengvai išspręsti operacine semantika. Proveržis atsirado dėl kategorijų teorijos. Eugenio Moggi atrado, kad skaičiavimo efektus galima paversti monadomis. Tai pasirodė esąs svarbus pastebėjimas, kuris ne tik suteikė naują gyvybę denotacinei semantikai ir padarė patogesnes grynai funkcines programas, bet ir suteikė naujos informacijos apie tradicinį programavimą. Apie monadas pakalbėsiu vėliau, kai sukursime kategoriškesnius įrankius.

Vienas iš svarbių matematinio programavimo modelio privalumų yra galimybė atlikti formalų programinės įrangos teisingumo įrodymą. Tai gali atrodyti ne taip svarbu, kai rašote plataus vartojimo programinę įrangą, tačiau yra programavimo sričių, kuriose gedimo kaina gali būti didžiulė arba kai kyla pavojus žmonių gyvybei. Tačiau net rašydami žiniatinklio programas sveikatos priežiūros sistemai galite įvertinti mintį, kad Haskell standartinės bibliotekos funkcijos ir algoritmai pateikiami kartu su teisingumo įrodymais.

Švaros ir purvinos funkcijos

Tai, ką mes vadiname funkcijomis C++ ar bet kuria kita privaloma kalba, nėra tas pats, ką matematikai vadina funkcijomis. Matematinė funkcija yra tiesiog reikšmių susiejimas su reikšmėmis.

Matematinę funkciją galime įgyvendinti programavimo kalba: tokia funkcija, gavusi įvesties reikšmę, apskaičiuos išvesties reikšmę. Skaičiaus kvadrato funkcija tikriausiai padaugins įvesties reikšmę iš savęs. Tai padarys kiekvieną kartą, kai jis bus iškviestas, ir garantuotai duos tą patį rezultatą kiekvieną kartą, kai bus iškviestas tuo pačiu argumentu. Skaičiaus kvadratas nesikeičia atsižvelgiant į mėnulio fazes.

Be to, skaičiaus kvadrato apskaičiavimas neturėtų turėti šalutinio poveikio, kad jūsų šuo būtų skanus. „Funkcija“, kuri tai atlieka, negali būti lengvai modeliuojama matematine funkcija.

Programavimo kalbose funkcijos, kurios visada duoda tą patį rezultatą tais pačiais argumentais ir neturi šalutinio poveikio, vadinamos grynosiomis. Gryna funkcine kalba, kaip Haskell, visos funkcijos yra grynos. Tai leidžia lengviau nustatyti šių kalbų denotacinę semantiką ir modeliuoti jas naudojant kategorijų teoriją. Jei naudojate kitas kalbas, visada galite apsiriboti grynu pogrupiu arba atskirai pagalvoti apie šalutinį poveikį. Vėliau pamatysime, kaip monados leidžia modeliuoti visokius efektus naudojant tik grynąsias funkcijas. Dėl to nieko neprarandame apsiribodami matematinėmis funkcijomis.

Tipų pavyzdžiai

Kai nuspręsite, kad tipai yra rinkiniai, galite sugalvoti keletą gana egzotiškų pavyzdžių. Pavyzdžiui, koks tipas atitinka tuščią rinkinį? Ne, jis nėra tuščias C++, nors Haskell šis tipas vadinamas Tuščiu. Tai tipas, kuriame nėra jokios vertės. Galite apibrėžti funkciją, kuri užima tuštumą, bet niekada negalite jos iškviesti. Norėdami jį iškviesti, turite pateikti vertę, kurios tipas yra Void, o jos tiesiog nėra. Kalbant apie tai, ką ši funkcija gali grąžinti, nėra jokių apribojimų. Jis gali grąžinti bet kokį tipą (nors tai niekada neįvyks, nes jo negalima pavadinti). Kitaip tariant, tai funkcija, kurios grąžinimo tipas yra polimorfinis. Haskellers tai pavadino:
absurdas::Vid -> a
(vertėjo pastaba: C++ neįmanoma apibrėžti tokios funkcijos: C++ kalboje kiekvienas tipas turi bent vieną reikšmę.)

(Atminkite, kad a yra tipo kintamasis, kuris gali būti bet kokio tipo.) Šis pavadinimas nėra atsitiktinumas. Yra gilesnis tipų ir funkcijų aiškinimas logikos požiūriu, vadinamas Curry-Howard izomorfizmu. Tuštumos tipas reiškia netiesą, o absurdiška funkcija reiškia teiginį, kad kažkas išplaukia iš melo, kaip lotyniškoje frazėje „ex falso sequitur quodlibet“. (vertėjo pastaba: viskas išplaukia iš melo.)

Toliau pateikiamas tipas, atitinkantis vienetų rinkinį. Tai tipas, turintis tik vieną galimą reikšmę. Ši reikšmė tiesiog yra „yra“. Galbūt jo neatpažinsite iš karto, tačiau C++ jis negalioja. Pagalvokite apie šio tipo ir į jo funkcijas. Tuščią funkciją visada galima iškviesti. Jei tai gryna funkcija, ji visada pateiks tą patį rezultatą. Štai tokios funkcijos pavyzdys:
int f44() (grįžta 44; )
Galite manyti, kad ši funkcija nepriima „nieko“, tačiau, kaip ką tik matėme, funkcija, kuri priima „nieko“, negali būti iškviesta, nes nėra reikšmės, atitinkančios tipą „nieko“. Taigi, ką ši funkcija priima? Konceptualiai tai turi fiktyvią reikšmę, kuri turi tik vieną egzempliorių, todėl neturime jos aiškiai nurodyti kode. Tačiau Haskell turi šios reikšmės simbolį: tuščią skliaustų porą (). Taigi, dėl juokingo sutapimo (ar ne sutapimo?) funkcijos iškvietimas iš void atrodo vienodai tiek C++, tiek Haskell. Be to, dėl Haskello pomėgio glaustumui, tas pats simbolis () naudojamas tipui, konstruktoriui ir vienai reikšmei, atitinkančiai vienetų rinkinį. Štai „Haskell“ funkcija:
f44::() -> Sveikasis skaičius f44() = 44
Pirmoje eilutėje nurodoma, kad f44 konvertuos tipą (), vadinamą "vienetu", į tipą Integer. Antroje eilutėje nurodoma, kad f44 naudoja šablonų atitikimą, kad konvertuotų vienintelį konstruktorių vienam, ty (), į skaičių 44. Šią funkciją iškviečiate pateikdami reikšmę ():
f44()
Atkreipkite dėmesį, kad kiekviena vieno funkcija prilygsta vieno elemento pasirinkimui iš tikslinio tipo (čia pasirinktas sveikasis skaičius 44). Tiesą sakant, galite galvoti apie f44 kaip kitą skaičių 44. Tai pavyzdys, kaip galime pakeisti tiesioginę nuorodą į aibės elementus funkcija (rodykle). Funkcijos nuo vieno iki tam tikro tipo A yra viena su kita su aibės A elementais.

O kaip su funkcijomis, kurios grąžina negaliojančią arba, Haskell, grąžina vieną? C++ kalboje tokios funkcijos naudojamos šalutiniams poveikiams, tačiau žinome, kad tokios funkcijos nėra tikros funkcijos matematine šio žodžio prasme. Gryna funkcija, kuri grąžina vieną, nieko nedaro: ji atmeta savo argumentą.

Matematiškai funkcija iš aibės A į vienetinę aibę susieja kiekvieną elementą su vienu tos aibės elementu. Kiekvienam A yra lygiai viena tokia funkcija. Čia jis skirtas sveikajam skaičiui:
fInt::Integer -> () fInt x = ()
Pateikite bet kokį sveikąjį skaičių ir jis grąžins vieną. Siekdamas trumpumo, Haskell kaip argumentą leidžia naudoti apatinį brūkšnį, kuris atmetamas. Tokiu būdu nereikia sugalvoti pavadinimo. Aukščiau pateiktą kodą galima perrašyti taip:
fInt::Integer -> () fInt_ = ()
Atkreipkite dėmesį, kad šios funkcijos vykdymas nepriklauso tik nuo jai perduodamos reikšmės, bet ir nepriklauso nuo argumento tipo.

Funkcijos, kurias bet kokiam tipui galima apibrėžti ta pačia formule, vadinamos parametriškai polimorfinėmis. Galite įgyvendinti visą tokių funkcijų šeimą su viena lygtimi, naudodami parametrą, o ne konkretų tipą. Kaip iškviesti polimorfinę funkciją iš bet kokio tipo į vieną? Žinoma, mes tai vadinsime vienetu:
vienetas::a -> () vienetas _ = ()
C++ programoje tai įgyvendintumėte taip:
šabloną tuščias vienetas (T) ()
(vertėjo pastaba: kad padėtų kompiliatoriui jį optimizuoti iš karto, geriau taip):
šabloną tuščias vienetas (T&&) ()
Kitas „tipų tipologijos“ yra dviejų elementų rinkinys. C++ kalboje jis vadinamas bool, o Haskell, nenuostabu, Bool. Skirtumas yra tas, kad C++ bool yra įtaisytas tipas, o Haskell jis gali būti apibrėžtas taip:
duomenys Bool = True | Netiesa
(Šį apibrėžimą reikėtų skaityti taip: Bool gali būti True arba False.) Iš esmės šį tipą būtų galima apibūdinti C++:
enum bool(tiesa, klaidinga);
Tačiau C++ enum iš tikrųjų yra sveikasis skaičius. Galima naudoti C++11 "class enum", bet tada reiktų kvalifikuoti reikšmę klasės pavadinimu: bool::true arba bool::false, jau nekalbant apie poreikį įtraukti atitinkamą antraštę į kiekvieną failą. kad jį naudoja.

„Pure Bool“ funkcijos tiesiog pasirenka dvi reikšmes iš tikslinio tipo, vieną atitinkančią True, o kitą – klaidingą.

Funkcijos Būlyje vadinamos predikatais. Pavyzdžiui, Haskell bibliotekoje Data.Char yra daug predikatų, tokių kaip IsAlpha arba isDigit. Panaši biblioteka yra ir C++ , kuri, be kita ko, deklaruoja funkcijas isalpha ir isdigit, tačiau jos grąžina int, o ne loginę reikšmę. Dabartiniai predikatai yra apibrėžti ir vadinami ctype::is(alfa, c) ir ctype::is(skaitmuo, c).



 


Skaityti:



Įvesta programavimo kalba Tipas arba formato specifikacijos, arba konvertavimo simboliai, arba valdymo simboliai

Įvesta programavimo kalba Tipas arba formato specifikacijos, arba konvertavimo simboliai, arba valdymo simboliai

C++ programavimo kalba Paskutinį kartą atnaujinta: 2017-08-28 C++ programavimo kalba yra aukšto lygio kompiliuota kalba...

Rusijos pašto darbo grafikas Naujųjų metų švenčių dienomis Pašto darbas Naujųjų metų švenčių dienomis

Rusijos pašto darbo grafikas Naujųjų metų švenčių dienomis Pašto darbas Naujųjų metų švenčių dienomis

Rusijos paštas XXI amžiuje tapo universalia institucija, padedančia ne tik gauti laiškus ir siuntinius. Komunaliniai mokesčiai, pensijos,...

Tass: santrumpos dekodavimas

Tass: santrumpos dekodavimas

Šis terminas kilęs iš italų santrumpos ir lotyniško brevis – trumpas. Senovinėse knygose ir rankraščiuose taip buvo vadinamas sutrumpintas...

Sertifikato šablonų tuščias atsisiuntimas Garbės sertifikato šablonas spausdinimui

Sertifikato šablonų tuščias atsisiuntimas Garbės sertifikato šablonas spausdinimui

Sveikinu, brangus skaitytojau! Šiandien aš jums pasakysiu, kaip padaryti laišką „Word“. Savo darbe turėjau parašyti daugybę...

tiekimo vaizdas RSS