Kas yra dinaminis spausdinimas? Dinaminis spausdinimas Php statinis spausdinimas.

Viskas labai paprasta. Tai tarsi skirtumas tarp viešbučio ir privataus buto.

Bute gyvena tik ten registruoti asmenys. Jei, tarkime, jame gyvena Sidorovų šeima, tai Pupkinų šeima dėl mūsų gyvybės negali ten gyventi. Tuo pačiu metu Petya Sidorov gali gyventi šiame bute, tada Grisha Sidorov gali ten persikelti (kartais jie netgi gali ten gyventi tuo pačiu metu - tai yra masyvas). Tai yra statinis spausdinimas.

Vienu metu viešbutyje gali gyventi Sidorovų šeima. Jie net ne visada turi ten registruotis. Tada jie iš ten išeis, o Pupkins persikels ten. Ir tada Kuznecovai. Ir tada kažkas kitas. Tai dinaminis spausdinimas.

Jei grįšime prie programavimo, tai pirmasis atvejis (statinis spausdinimas) randamas, tarkime, C, C++, C#, Java ir kt. Prieš priskirdami reikšmę kintamajam pirmą kartą, turėtumėte pasakyti, ką ten saugosite: sveikuosius skaičius, slankiojo kablelio skaičius, eilutes ir pan. Sidorovai gyvens šiame bute). Kita vertus, dinaminiam spausdinimui to nereikia. Kai priskiriate reikšmę, tuo pačiu kintamajam priskiriate jos tipą ( Šiame viešbučio kambaryje dabar gyvena Vasya Pupkin iš Pupkinų šeimos). Tai randama tokiomis kalbomis kaip PHP, Python ir JavaScript.

Abu metodai turi savo privalumų ir trūkumų. Kuris yra geresnis ar blogesnis, priklauso nuo sprendžiamų problemų. Išsamiau galite pasiskaityti, tarkime, Vikipedijoje.

Naudodami statinį spausdinimą, jūs tiksliai žinote kintamojo tipą tuo metu, kai rašote programą ir kuriate algoritmą, ir į tai atsižvelgiate. tie. jei sakėte, kad kintamasis G yra keturių baitų nežymėtas sveikasis skaičius, tai algoritme jis visada bus keturių baitų beženklis sveikasis skaičius (jei yra, tuomet reikia jį aiškiai konvertuoti arba žinoti, kaip vertėjas konvertuoja į tam tikrą skaičių įvairių situacijų, tačiau dažniausiai jei yra tipo neatitikimas, tai yra algoritmo klaida, o kompiliatorius bent įspės), esant statinei, į skaičių negalite įdėti eilutės „Vasya the fool“ ir papildomų patikrinimų prieš naudojant kintamąjį, norint nustatyti „ar yra skaičius“, nereikia, jūs atliekate visus duomenų teisingumą jų įvedimo į programą metu arba taip, kaip reikalauja pats algoritmas.

naudojant dinaminį spausdinimą, to paties kintamojo tipas paprastai jums nežinomas ir gali pasikeisti jau vykdant programą, o jūs į tai atsižvelgsite, niekas jūsų neįspės apie galimą algoritmo klaidą dėl tipo neatitikimo (kurdami algoritmą darėte prielaidą, kad G yra sveikasis skaičius, o vartotojas įvedė, tarkime, slankaus kablelio skaičių, arba dar blogiau, eilutę arba, tarkime, po aritmetinės operacijos vietoj sveikojo skaičiaus buvo slankaus kablelio skaičius , o kitame žingsnyje bandysite naudoti bitų operacijas...), kita vertus, Jums nereikės rūpintis daugybe smulkmenų.

  • Dinaminis spausdinimas yra metodas, plačiai naudojamas programavimo kalbose ir specifikacijų kalbose, kai kintamasis susiejamas su tipu reikšmės priskyrimo, o ne kintamojo deklaravimo momentu. Taigi skirtingose ​​programos dalyse tas pats kintamasis gali įgyti skirtingų tipų reikšmes. Kalbų su dinaminiu spausdinimu pavyzdžiai yra Smalltalk, Python, Objective-C, Ruby, PHP, Perl, JavaScript, Lisp, xBase, Erlang, Visual Basic.

    Priešinga technika yra statinis spausdinimas.

    Kai kuriose kalbose, kuriose dinaminis spausdinimas silpnas, kyla problemų lyginant reikšmes, pavyzdžiui, PHP turi palyginimo operatorius „==“, „!=“ ir „===“, „!==“, kur antrasis operacijų pora lygina reikšmes ir kintamųjų tipus. Operacija „===“ suteikia „true“, tik jei yra visiška atitiktis, priešingai nei „==“, kuri šią išraišką laiko teisinga: (1=="1"). Verta pažymėti, kad tai yra ne dinaminio rašymo problema apskritai, o su konkrečiomis programavimo kalbomis.

Susijusios sąvokos

Programavimo kalba yra oficiali kalba, skirta kompiuterių programoms rašyti. Programavimo kalba apibrėžia leksinių, sintaksinių ir semantinių taisyklių rinkinį, nulemiantį programos išvaizdą ir veiksmus, kuriuos vykdytojas (dažniausiai kompiuteris) atliks jai valdomas.

Sintaksinis cukrus programavimo kalboje – tai sintaksės ypatybės, kurių naudojimas neturi įtakos programos elgsenai, tačiau daro kalbą patogesnę žmogui.

Savybė yra būdas pasiekti vidinę objekto būseną, imituojant tam tikro tipo kintamąjį. Prieiga prie objekto ypatybės atrodo taip pat, kaip prieiga prie struktūros lauko (struktūriniame programavime), tačiau iš tikrųjų įgyvendinama funkcijos iškvietimu. Kai bandoma nustatyti tam tikros savybės vertę, iškviečiamas vienas metodas, o kai bandoma gauti šios savybės vertę, iškviečiamas kitas metodas.

Išplėstinė Backus-Naur Form (EBNF) yra formali sintaksės apibrėžimo sistema, kurioje kai kurios sintaksės kategorijos apibrėžiamos nuosekliai per kitas. Naudojamas formalioms gramatikoms be konteksto apibūdinti. Pasiūlė Niklaus Wirth. Tai išplėstas „Backus-Naur“ formų apdorojimas, skiriasi nuo BNF „talpesniu“ dizainu, leidžiančiu su tokia pačia išraiškinga galia supaprastinti...

Taikomasis programavimas yra deklaratyvaus programavimo rūšis, kai programos rašymas susideda iš sistemingo vieno objekto pritaikymo kitam. Tokios programos rezultatas vėl yra objektas, kuris gali dalyvauti programose ir kaip funkcija, ir kaip argumentas ir pan. Taip programos žymėjimas tampa matematiškai aiškus. Tai, kad funkcija žymima išraiška, rodo galimybę naudoti reikšmės funkcijas – funkcines...

Konkatenacinė programavimo kalba yra programavimo kalba, pagrįsta idėja, kad dviejų kodo dalių sujungimas išreiškia jų sudėtį. Tokioje kalboje plačiai naudojamas numanomas funkcijų argumentų nurodymas (žr. beprasmišką programavimą), naujos funkcijos apibrėžiamos kaip funkcijų kompozicija, o vietoj taikymo naudojamas sujungimas. Šis metodas prieštarauja taikomajam programavimui.

Kintamasis yra fizinės ar abstrakčios sistemos požymis, galintis pakeisti savo, dažniausiai skaitinę, reikšmę. Kintamojo sąvoka plačiai naudojama tokiose srityse kaip matematika, mokslas, inžinerija ir programavimas. Kintamųjų pavyzdžiai: oro temperatūra, funkcijos parametras ir daug daugiau.

Sintaksinė analizė (arba analizavimas, žargono analizavimas ← anglų kalbos analizė) kalbotyroje ir informatikoje – tai natūralios ar formalios kalbos tiesinės leksemų (žodžių, žetonų) sekos lyginimo su formaliąja gramatika procesas. Rezultatas paprastai yra analizės medis (sintaksės medis). Paprastai naudojamas kartu su leksine analize.

Apibendrintas algebrinis duomenų tipas (GADT) yra vienas iš algebrinių duomenų tipų, kuriam būdinga tai, kad jo konstruktoriai gali grąžinti kitas reikšmes nei su juo susietas tipas. Sukurta veikiant kūriniams apie indukcines šeimas tarp priklausomų tipų tyrinėtojų.

Semantika programuojant yra disciplina, tirianti programavimo kalbos konstrukcijų reikšmių formalizavimą, kuriant formalius matematinius modelius. Tokiems modeliams sudaryti gali būti naudojami įvairūs įrankiai, pavyzdžiui, matematinė logika, λ skaičiavimas, aibių teorija, kategorijų teorija, modelių teorija ir universalioji algebra. Programavimo kalbos semantikos formalizavimas gali būti naudojamas kalbai apibūdinti, kalbos savybėms apibrėžti...

Objektinis programavimas (OOP) – tai programavimo metodika, pagrįsta programos vaizdavimu kaip objektų rinkiniu, kurių kiekvienas yra konkrečios klasės pavyzdys, o klasės sudaro paveldėjimo hierarchiją.

Dinaminis kintamasis yra programos kintamasis, kuriam programos vykdymo metu yra skirta vietos RAM. Iš esmės tai yra atminties dalis, kurią sistema skiria programai konkretiems tikslams, kol programa veikia. Tuo jis skiriasi nuo visuotinio statinio kintamojo – atminties dalies, kurią sistema skiria programai tam tikriems tikslams prieš programai paleidžiant. Dinaminis kintamasis yra viena iš atminties kintamųjų klasių.

Šiame straipsnyje paaiškinami skirtumai tarp statinio tipo ir dinamiškai įvedamų kalbų, nagrinėjamos „stipraus“ ir „silpno“ spausdinimo sąvokos ir palyginama spausdinimo sistemų skirtingomis kalbomis galia. Pastaruoju metu programuojant ryškus judėjimas link griežtesnių ir galingesnių spausdinimo sistemų, todėl kalbant apie tipus ir spausdinimą svarbu suprasti, apie ką kalbame.



Tipas yra galimų reikšmių rinkinys. Sveikasis skaičius gali turėti reikšmes 0, 1, 2, 3 ir pan. Būlio reikšmė gali būti teisinga arba klaidinga. Galite sugalvoti savo tipą, pavyzdžiui, tipą „High Five“, kuriame galimos reikšmės yra „aukšta“ ir „5“, ir nieko daugiau. Tai ne eilutė ar skaičius, tai naujas, atskiras tipas.


Statiškai įvestos kalbos riboja kintamųjų tipus: programavimo kalba gali žinoti, pavyzdžiui, kad x yra sveikasis skaičius. Tokiu atveju programuotojui draudžiama daryti x = true, tai bus neteisingas kodas. Kompiliatorius atsisakys jį kompiliuoti, todėl mes net negalėsime paleisti kodo. Kita statiškai spausdinama kalba gali turėti skirtingas išraiškos galimybes ir nė viena iš populiarių tipų sistemų negali išreikšti mūsų HighFive tipo (tačiau daugelis gali išreikšti kitas, sudėtingesnes idėjas).


Dinamiškai įvestos kalbos žymi reikšmes tipais: kalba žino, kad 1 yra sveikas skaičius, 2 – sveikasis skaičius, bet negali žinoti, kad kintamajame x visada yra sveikasis skaičius.


Kalbos vykdymo laikas tikrina šias etiketes skirtingais laiko momentais. Jei bandysime pridėti dvi reikšmes, ji gali patikrinti, ar tai skaičiai, eilutės ar masyvai. Tada jis pridės šias reikšmes, suklijuos jas arba išmes klaidą, priklausomai nuo tipo.

Statiškai įvestos kalbos

Statinės kalbos tikrina programos tipus kompiliavimo metu, prieš paleidžiant programą. Bet kuri programa, kurios tipai pažeidžia kalbos taisykles, laikoma neteisinga. Pavyzdžiui, dauguma statinių kalbų atmes posakį „a“ + 1 (C yra šios taisyklės išimtis). Kompiliatorius žino, kad "a" yra eilutė, o 1 yra sveikas skaičius, o + veikia tik tada, kai kairioji ir dešinė pusės yra to paties tipo. Taigi jam nereikia paleisti programos, kad suprastų, jog yra problema. Kiekviena išraiška statiškai įvestoje kalboje yra konkretaus tipo, kurį galima nustatyti nepaleidus kodo.


Daugeliui statiškai įvestų kalbų reikalingas tipo žymėjimas. Java funkcija public int add(int x, int y) paima du sveikuosius skaičius ir grąžina trečią sveikąjį skaičių. Kitos statiškai įvestos kalbos tipą gali nustatyti automatiškai. Ta pati pridėjimo funkcija Haskell atrodo taip: pridėti x y = x + y . Kalbai tipų nenurodome, bet ji gali juos išsiaiškinti pati, nes žino, kad + veikia tik su skaičiais, todėl x ir y turi būti skaičiai, todėl funkcija pridėti kaip argumentus paima du skaičius.


Tai nesumažina tipo sistemos „statiškumo“. Haskell tipo sistema garsėja tuo, kad yra statiška, griežta ir galinga, o Haskell visuose šiuose frontuose lenkia Java.

Dinamiškai įvestos kalbos

Dinamiškai įvestoms kalboms tipo nurodyti nereikia, tačiau jos pačios jo neapibrėžia. Kintamųjų tipai nežinomi, kol jie neturi konkrečių verčių paleidimo metu. Pavyzdžiui, funkcija Python


def f(x, y): grąžinti x + y

gali pridėti du sveikuosius skaičius, sujungti eilutes, sąrašus ir pan., ir mes negalime suprasti, kas tiksliai vyksta, kol nepaleidžiame programos. Gali būti, kad funkcija f tam tikru momentu bus iškviesta dviem eilutėmis, o kitu metu – dviem skaičiais. Šiuo atveju x ir y skirtingu laiku bus skirtingų tipų reikšmės. Štai kodėl teigiama, kad dinaminių kalbų reikšmės turi tipą, o kintamieji ir funkcijos neturi. Reikšmė 1 tikrai yra sveikas skaičius, bet x ir y gali būti bet koks.

Palyginimas

Dauguma dinamiškų kalbų išmes klaidą, jei tipai bus naudojami neteisingai („JavaScript“ yra pastebima išimtis; ji bando grąžinti bet kokios išraiškos reikšmę, net jei ji neturi prasmės). Naudojant dinamiškai įvestas kalbas, gamybinėje aplinkoje gali įvykti net tokia paprasta klaida, kaip „a“ + 1. Statinės kalbos užkerta kelią tokioms klaidoms, tačiau, žinoma, prevencijos laipsnis priklauso nuo tipo sistemos stiprumo.


Statinės ir dinaminės kalbos yra pagrįstos iš esmės skirtingomis idėjomis apie programos teisingumą. Dinaminėje kalboje "a" + 1 yra tinkama programa: kodas bus paleistas ir vykdymo aplinkoje pasirodys klaida. Tačiau daugumoje statiškai įvestų kalbų išraiška „a“ + 1 yra ne programa: Jis nebus kompiliuojamas ir nebus paleistas. Tai neteisingas kodas, kaip ir atsitiktinių simbolių rinkinys!&%^@*&%^@* yra neteisingas kodas. Ši papildoma teisingumo ir neteisingumo samprata neturi atitikmens dinaminėse kalbose.

Stiprus ir silpnas spausdinimas

Sąvokos „stiprus“ ir „silpnas“ yra labai dviprasmiškos. Štai keletas jų naudojimo pavyzdžių:

    Kartais „stiprus“ reiškia „statiškas“.
    Tai paprasta, bet geriau vartoti terminą „statinis“, nes dauguma žmonių jį vartoja ir supranta.

    Kartais „stiprus“ reiškia „neatlieka numanomo tipo konvertavimo“.
    Pavyzdžiui, „JavaScript“ leidžia rašyti „a“ + 1, o tai gali būti vadinama „silpnu spausdinimu“. Tačiau beveik visose kalbose yra tam tikro lygio netiesioginis konvertavimas, leidžiantis automatiškai konvertuoti sveikuosius skaičius į slankiojo kablelio skaičius, pvz., 1 + 1,1. Tiesą sakant, dauguma žmonių vartoja žodį „stiprus“, norėdami apibrėžti ribą tarp priimtino ir nepriimtino atsivertimo. Nėra visuotinai priimtos ribos, jos visos yra netikslios ir priklauso nuo konkretaus žmogaus nuomonės.

    Kartais „stiprus“ reiškia, kad neįmanoma apeiti griežtų kalbos spausdinimo taisyklių.

  • Kartais „stiprus“ reiškia saugų atmintį.
    C yra nesaugios atminties kalbos pavyzdys. Jei xs yra keturių skaičių masyvas, tada C mielai vykdys xs arba xs , grąžindamas tam tikrą reikšmę iš atminties iškart už xs .

Sustokime. Štai kaip kai kurios kalbos atitinka šiuos apibrėžimus. Kaip matote, tik Haskell yra nuolat „stiprus“ visais atžvilgiais. Dauguma kalbų nėra tokios aiškios.



(Stulpelyje „Numanomos konversijos“ esantis „Kada kaip“ reiškia, kad skirstymas į stiprias ir silpnas priklauso nuo to, kokias konversijas laikome priimtinomis).


Dažnai terminai „stiprus“ ir „silpnas“ reiškia miglotą įvairių aukščiau pateiktų apibrėžimų ir kitų čia nepateiktų apibrėžimų derinį. Dėl visos šios painiavos žodžiai „stiprus“ ir „silpnas“ tampa beveik beprasmiai. Kai norite vartoti šiuos terminus, geriau apibūdinkite, kas tiksliai turi omenyje. Pavyzdžiui, galite pasakyti, kad „JavaScript grąžina reikšmę, kai pridedama eilutė su skaičiumi, bet Python grąžina klaidą“. Tokiu atveju neeikvosime savo energijos bandydami susitarti dėl kelių žodžio „stiprus“ reikšmių. Arba, dar blogiau: baigsime neišspręstus nesusipratimus dėl terminijos.


Dažniausiai internete vartojamos sąvokos „stiprus“ ir „silpnas“ yra neaiškios ir menkai apibrėžtos konkrečių žmonių nuomonės. Jie vartojami kalbai vadinti „bloga“ arba „gera“, ir ši nuomonė virsta techniniu žargonu.



Stiprus spausdinimas: tipo sistema, kuri man patinka ir kuri man patinka.

Silpnas spausdinimas: tipo sistema, kuri mane trikdo arba kuri man netinka.

Laipsniškas spausdinimas

Ar galima prie dinaminių kalbų pridėti statinių tipų? Kai kuriais atvejais – taip. Kitose tai sunku arba neįmanoma. Akivaizdžiausia problema – eval ir kitos panašios dinaminių kalbų savybės. Atlikus 1 + eval("2") Python, gaunama 3. Bet ką duoda 1 + eval(read_from_the_network())? Tai priklauso nuo to, kas yra internete vykdymo metu. Jei gauname skaičių, tada išraiška yra teisinga. Jei tai eilutė, tada ne. Prieš paleisdami jokiu būdu negalima žinoti, todėl neįmanoma išanalizuoti tipo statiškai.


Nepatenkinamas sprendimas praktikoje yra nustatyti reiškinį eval() į tipą Any, kuris yra panašus į Object kai kuriose objektinio programavimo kalbose arba sąsają () programoje Go: tai tipas, kurį galima patenkinti bet kokia reikšme.


Bet tipo reikšmės yra neribotos, todėl tipinės sistemos galimybė mums padėti su eval kodu išnyksta. Kalbos, turinčios ir eval, ir tipo sistemą, turi atsisakyti tipo saugos, kai naudojama eval.


Kai kuriose kalbose yra pasirenkamas arba laipsniškas spausdinimas: pagal numatytuosius nustatymus jos yra dinaminės, tačiau leidžia pridėti keletą statinių komentarų. Python neseniai pridėjo pasirenkamus tipus; „TypeScript“ yra „JavaScript“ superrinkinys, turintis pasirenkamų tipų; „Flow“ atlieka statinę seno gero „JavaScript“ kodo analizę.


Šios kalbos suteikia tam tikrų statinio spausdinimo pranašumų, tačiau jos niekada nesuteiks absoliučios tikrai statiškų kalbų garantijų. Kai kurios funkcijos bus įvestos statiškai, o kai kurios – dinamiškai. Programuotojas visada turėtų žinoti skirtumą ir būti atsargus.

Statiškai įvesto kodo sudarymas

Kompiliuojant statiškai įvestą kodą, pirmiausia patikrinama sintaksė, kaip ir bet kuriame kompiliatoriuje. Tada tikrinami tipai. Tai reiškia, kad statinė kalba pirmiausia gali skųstis viena sintaksės klaida, o ją ištaisius – 100 spausdinimo klaidų. Sintaksės klaidos taisymas nesukūrė tų 100 spausdinimo klaidų. Kompiliatorius tiesiog negalėjo aptikti tipo klaidų, kol sintaksė nebuvo ištaisyta.


Statinių kalbų kompiliatoriai paprastai gali generuoti greitesnį kodą nei dinaminių kalbų kompiliatoriai. Pavyzdžiui, jei kompiliatorius žino, kad funkcija pridėti priima sveikuosius skaičius, jis gali naudoti procesoriaus savąją ADD instrukciją. Dinaminė kalba tikrins tipą vykdymo metu, pasirinkdama iš daugybės pridėjimo funkcijų, priklausomai nuo tipų (pridėti sveikuosius skaičius ar slankiuosius skaičius, arba sujungti eilutes, o gal sąrašus?) arba nuspręsti, kad įvyko klaida ir tipai nesutampa. . Visi šie patikrinimai užtrunka. Dinaminėse kalbose optimizavimui naudojami įvairūs gudrybės, pvz., JIT kompiliavimas (just-in-time), kai kodas yra perkompiliuojamas vykdymo metu, gavus visą reikiamą tipo informaciją. Tačiau jokia dinamiška kalba negali prilygti tvarkingai užrašyto statinio kodo greičiui tokia kalba kaip Rust.

Statinių ir dinaminių tipų argumentai

Statinio tipo sistemos šalininkai atkreipia dėmesį, kad be tipo sistemos paprastos klaidos gali sukelti problemų gamyboje. Tai, žinoma, tiesa. Kiekvienas, vartojęs dinamišką kalbą, tai patyrė iš pirmų lūpų.


Dinaminių kalbų šalininkai pabrėžia, kad tokiomis kalbomis kodą rašyti lengviau. Tai neabejotinai galioja kai kurioms kodų rūšims, kurias retkarčiais rašome, pavyzdžiui, eval kodui. Tai prieštaringas sprendimas įprastam darbui, ir čia prasminga prisiminti neaiškų žodį „lengvas“. Rich Hickey atliko puikų darbą kalbėdamas apie žodį „lengvas“ ir jo ryšį su žodžiu „paprastas“. Peržiūrėję šį reportažą suprasite, kad nelengva teisingai vartoti žodį „lengva“. Saugokitės „lengvo“.


Statinio ir dinaminio spausdinimo sistemų privalumai ir trūkumai vis dar menkai suprantami, tačiau jie tikrai priklauso nuo kalbos ir konkrečios sprendžiamos problemos.


„JavaScript“ bando tęsti, net jei tai reiškia beprasmę konversiją (pvz., „a“ + 1, dėl kurio gaunama „a1“). Kita vertus, „Python“ stengiasi būti konservatyvus ir dažnai pateikia klaidas, kaip „a“ + 1 atveju.


Yra įvairių metodų su skirtingais saugumo lygiais, tačiau „Python“ ir „JavaScript“ yra dinamiškai įvedamos kalbos.



Haskell neleis jums pridėti sveikojo skaičiaus ir slankiojo skaičiaus be aiškios konversijos. C ir Haskell yra statiniai, nepaisant tokių didelių skirtumų.


Yra daug dinaminių ir statinių kalbų variantų. Bet koks bendras teiginys, pavyzdžiui, „statinės kalbos yra geresnės už dinamines kalbas, kai kalbama apie X“, beveik garantuotai bus nesąmonė. Tai gali būti tiesa, kai kalbama apie konkrečias kalbas, bet tada geriau pasakyti: „Haskell yra geresnis už Python, kai kalbama apie X“.

Statinio spausdinimo sistemų įvairovė

Pažvelkime į du garsius statiškai įvestų kalbų pavyzdžius: Go ir Haskell. „Go“ spausdinimo sistema neturi bendrinių tipų, tipų su „parametrais“ iš kitų tipų. Pavyzdžiui, galime sukurti savo „MyList“ sąrašų tipą, kuriame galima saugoti bet kokius mums reikalingus duomenis. Mes norime turėti galimybę sukurti sveikųjų skaičių MyList, eilučių MyList ir pan., nekeičiant MyList šaltinio kodo. Kompiliatorius turi stebėti spausdinimą: jei yra sveikųjų skaičių MyList ir mes netyčia įtraukiame ten eilutę, tada kompiliatorius turi atmesti programą.


„Go“ buvo specialiai sukurtas taip, kad nebūtų galima apibrėžti tokių tipų kaip „MyList“. Geriausia, ką galima padaryti, yra sukurti „tuščių sąsajų“ MyList: „MyList“ gali turėti objektų, tačiau kompiliatorius tiesiog nežino jų tipo. Kai gauname objektus iš „MyList“, turime nurodyti kompiliatoriui jų tipą. Jei sakome „gaunu eilutę“, bet iš tikrųjų reikšmė yra skaičius, įvyks vykdymo klaida, kaip ir dinaminių kalbų atveju.


„Go“ taip pat neturi daugelio kitų funkcijų, esančių šiuolaikinėse statiškai įvestose kalbose (ar net kai kuriose aštuntojo dešimtmečio sistemose). „Go“ kūrėjai turėjo savo priežasčių tokius sprendimus priimti, tačiau pašalinių žmonių nuomonė šiuo klausimu kartais gali skambėti griežtai.


Dabar palyginkime su Haskell, kuris turi labai galingą tipo sistemą. Jei nustatote tipą į MyList, tada „numerių sąrašo“ tipas yra tiesiog MyList Integer . Haskell neleis mums netyčia pridėti eilutės į sąrašą ir užtikrins, kad į eilutės kintamąjį neįdėtume elemento iš sąrašo.


Haskell gali išreikšti daug sudėtingesnes idėjas tiesiogiai su tipais. Pavyzdžiui, Num a => MyList a reiškia "Mano reikšmių sąrašas, priklausantis to paties tipo skaičiams". Tai gali būti sveikųjų skaičių, slankiųjų ar fiksuoto tikslumo dešimtainių skaičių sąrašas, tačiau tai tikrai niekada nebus eilučių sąrašas, kuris tikrinamas kompiliavimo metu.


Galite parašyti pridėjimo funkciją, kuri veikia su bet kokiu skaičių tipu. Šios funkcijos tipas bus Num a => (a -> a -> a) . Tai reiškia:

  • a gali būti bet koks skaitinis tipas (Num a =>).
  • Funkcija paima du a tipo argumentus ir grąžina tipą a (a -> a -> a).

Paskutinis pavyzdys. Jei funkcijos tipas yra String -> String , tada ji priima eilutę ir grąžina eilutę. Bet jei tai String -> IO String , tada ji taip pat atlieka tam tikrą įvesties / išvesties funkciją. Tai gali būti prieiga prie disko, prieiga prie tinklo, skaitymas iš terminalo ir pan.


Jei funkcija turi tipą Nr IO, tada žinome, kad jis neatlieka jokių I/O operacijų. Pavyzdžiui, žiniatinklio programoje galite pasakyti, ar funkcija modifikuoja duomenų bazę, tiesiog žiūrėdami į jos tipą. Jokia dinamiška ir beveik jokios statinės kalbos to negali padaryti. Tai kalbų su galingiausiomis spausdinimo sistemomis ypatybė.


Daugumoje kalbų turėtume naršyti per funkciją ir visas iš ten iškviečiamas funkcijas ir t. t., bandydami rasti ką nors, kas pakeistų duomenų bazę. Tai varginantis procesas ir lengva padaryti klaidų. O Haskell tipo sistema į šį klausimą gali atsakyti paprastai ir patikimai.


Palyginkite šią galią su „Go“, kuri negali išreikšti paprastos „MyList“ idėjos, jau nekalbant apie „funkciją, kuri turi du argumentus, tiek skaitinius, tiek to paties tipo, ir kuri atlieka I/O“.


„Go“ metodas leidžia lengviau rašyti „Go“ programavimo įrankius (ypač kompiliatoriaus įgyvendinimas gali būti paprastas). Be to, reikia išmokti mažiau sąvokų. Kaip šios naudos palyginti su reikšmingais apribojimais, yra subjektyvus klausimas. Tačiau negalima ginčytis, kad Haskell yra sunkiau išmokstamas nei Go ir kad Haskell tipo sistema yra daug galingesnė ir kad Haskell kompiliuodamas gali užkirsti kelią daugybei kitų klaidų tipų.


„Go“ ir „Haskell“ yra tokios skirtingos kalbos, kad jų sugrupavimas į tą pačią „statinių kalbų“ klasę gali būti klaidinantis, net jei terminas vartojamas teisingai. Lyginant praktinius saugumo pranašumus, Go yra artimesnis dinamiškoms kalboms nei Haskell.


Kita vertus, kai kurios dinamiškos kalbos yra saugesnės nei kai kurios statinės kalbos. (Python paprastai laikomas daug saugesniu nei C). Kai norite apibendrinti statines ar dinamines kalbas kaip grupes, nepamirškite apie daugybę kalbų skirtumų.

Konkretūs spausdinimo sistemų galimybių skirtumų pavyzdžiai

Galingesnės spausdinimo sistemos gali nurodyti apribojimus mažesniais lygiais. Štai keli pavyzdžiai, tačiau nesijaudinkite dėl jų, jei sintaksė neaiški.


„Go“ galite pasakyti „pridėjimo funkcija užima du sveikuosius skaičius ir grąžina sveikąjį skaičių“:


func add(x int, y int) int ( return x + y )

Haskell galite pasakyti „funkcija trunka bet koks skaitinį tipą ir grąžina to paties tipo skaičių":


f:: Skaičius a => a -> a -> a pridėti x y = x + y

„Idris“ galite pasakyti „funkcija paima du sveikuosius skaičius ir grąžina sveikąjį skaičių, tačiau pirmasis argumentas turi būti mažesnis už antrąjį“:


pridėti: (x: Nat) -> (y: Nat) -> (automatiškai mažesnis: LT x y) -> Nat pridėti x y = x + y

Jei bandysite iškviesti funkciją add 2 1 kur pirmasis argumentas yra didesnis nei antrasis, kompiliatorius atmes programą kompiliavimo metu. Neįmanoma parašyti programos, kurioje pirmasis argumentas yra didesnis nei antrasis. Retai kuri kalba turi tokią galimybę. Daugumoje kalbų šis patikrinimas įvyksta vykdymo metu: parašytume kažką panašaus į if x >= y: raise SomeError() .


Nėra Haskell atitikmens aukščiau pateiktame Idris pavyzdyje, ir nėra Go atitikmens nei Haskell, nei Idris pavyzdžiui. Dėl to Idris gali užkirsti kelią daugeliui klaidų, kurių Haskell negali išvengti, o Haskell gali užkirsti kelią daugeliui klaidų, kurių Go nepastebės. Abiem atvejais reikalingos papildomos spausdinimo sistemos galimybės, kad kalba būtų sudėtingesnė.

Kai kurių statinių kalbų spausdinimo sistemos

Čia pateikiamas apytikslis kai kurių kalbų spausdinimo sistemų sąrašas didėjančia galia. Šis sąrašas suteiks jums bendrą supratimą apie sistemų galią, nelaikykite to absoliučia tiesa. Vienoje grupėje surinktos kalbos gali labai skirtis viena nuo kitos. Kiekviena spausdinimo sistema turi savo keistenybių, ir dauguma jų yra labai sudėtingos.

  • C (1972), Go (2009): Šios sistemos visai nėra galingos, be bendrųjų tipų palaikymo. Neįmanoma apibrėžti „MyList“ tipo, kuris reikštų „sveikųjų skaičių sąrašą“, „eilių sąrašą“ ir kt. Vietoj to turėsite sudaryti „nepaskirtų vertybių sąrašą“. Programuotojas turi neautomatiniu būdu pranešti „tai yra eilučių sąrašas“ kiekvieną kartą, kai iš sąrašo gaunama eilutė, ir dėl to gali atsirasti vykdymo klaida.
  • Java (1995), C# (2000): Abi kalbos palaiko bendruosius tipus, todėl galite sakyti „MyList“. ir gauti sąrašą eilučių, apie kurias kompiliatorius žino ir gali vykdyti tipo taisykles. Sąrašo elementai bus String tipo, o kompiliatorius privers taikyti kompiliavimo taisykles, kaip įprasta, todėl vykdymo klaidų tikimybė yra mažesnė.
  • Haskell (1990), Rust (2010), Swift (2014): Visos šios kalbos turi keletą išplėstinių funkcijų, įskaitant bendruosius tipus, algebrinių duomenų tipus (ADT) ir tipų klases ar kažką panašaus (atitinkamai klasių tipus, bruožus ir protokolus). Rust ir Swift yra populiaresni nei Haskell ir juos reklamuoja didelės organizacijos (atitinkamai „Mozilla“ ir „Apple“).
  • Agda (2007), Idris (2011): Šios kalbos palaiko priklausomus tipus, todėl galite sukurti tokius tipus kaip „funkcija, kuri užima du sveikuosius skaičius x ir y, kur y yra didesnis už x“. Netgi „y yra didesnis už x“ apribojimas yra priverstinis kompiliavimo metu. Vykdant y niekada nebus mažesnis arba lygus x, nesvarbu, kas nutiktų. Šiose kalbose statiškai galima patikrinti labai subtilias, bet svarbias sistemos savybes. Labai mažai programuotojų jas studijuoja, tačiau šios kalbos kelia didelį jų entuziazmą.

Aiškus judėjimas link galingesnių spausdinimo sistemų, ypač vertinant kalbų populiarumą, o ne tik kalbų faktą. Reikšminga išimtis yra „Go“, kuri paaiškina, kodėl daugelis statinių kalbų šalininkų mano, kad tai žingsnis atgal.


Antroji grupė (Java ir C#) yra pagrindinės kalbos, brandžios ir plačiai naudojamos.


Trečioji grupė artėja prie pagrindinio srauto, kurią labai palaiko „Mozilla“ („Rust“) ir „Apple“ („Swift“).


Ketvirta grupė (Idris ir Agda) yra toli nuo pagrindinės krypties, tačiau laikui bėgant tai gali pasikeisti. Prieš dešimt metų trijų grupių kalbos buvo toli nuo pagrindinės.

Šiame straipsnyje pateikiamas būtinas minimumas iš tų dalykų, kuriuos tiesiog reikia žinoti apie spausdinimą, kad dinaminio spausdinimo nevadintumėte blogiu, Lisp – be tipo kalba, o C – stipriai spausdinama kalba.

Pilnoje versijoje yra išsamus visų tipų spausdinimo aprašymas, pagardintas kodų pavyzdžiais, nuorodomis į populiarias programavimo kalbas ir iliustraciniais paveikslėliais.

Pirmiausia rekomenduoju perskaityti trumpąją straipsnio versiją, o tada, jei norite, pilną.

Trumpa versija

Remiantis spausdinimu, programavimo kalbos paprastai skirstomos į dvi dideles stovyklas - įvestas ir nespausdinamas (be tipo). Pirmasis apima, pavyzdžiui, C, Python, Scala, PHP ir Lua, o antrasis apima asamblėjos kalbą, Forth ir Brainfuck.

Kadangi „be tipo spausdinimas“ iš esmės yra paprastas kaip kištukas, jis nėra skirstomas į jokius kitus tipus. Tačiau įvestos kalbos yra suskirstytos į keletą daugiau sutampančių kategorijų:

  • Statinis/dinaminis spausdinimas. Statinis apibrėžiamas tuo, kad galutiniai kintamųjų ir funkcijų tipai nustatomi kompiliavimo metu. Tie. kompiliatorius jau 100% tikras, kuris tipas kur yra. Dinaminio spausdinimo metu visi tipai aptinkami programos vykdymo metu.

    Pavyzdžiai:
    Statinis: C, Java, C#;
    Dinaminis: Python, JavaScript, Ruby.

  • Stiprus / silpnas spausdinimas (taip pat kartais vadinamas stipriu / silpnu). Stiprus spausdinimas išsiskiria tuo, kad kalba neleidžia maišyti skirtingų tipų išraiškose ir neatlieka automatinių numanomų konversijų, pavyzdžiui, iš eilutės negalima atimti aibės. Silpnai įvestos kalbos daug numanomų konversijų atlieka automatiškai, net jei gali sumažėti tikslumas arba konversija yra dviprasmiška.

    Pavyzdžiai:
    Stiprios: Java, Python, Haskell, Lisp;
    Silpnas: C, JavaScript, Visual Basic, PHP.

  • Aiškus / numanomas spausdinimas. Aiškiai įvestos kalbos skiriasi tuo, kad turi būti aiškiai nurodytas naujų kintamųjų/funkcijų/jų argumentų tipas. Atitinkamai, kalbos su netiesioginiu spausdinimu perkelia šią užduotį kompiliatoriui / vertėjui.

    Pavyzdžiai:
    Aiškus: C++, D, C#
    Numanoma: PHP, Lua, JavaScript

Taip pat reikėtų pažymėti, kad visos šios kategorijos sutampa, pavyzdžiui, C kalba turi statinį silpną aiškų spausdinimą, o Python kalba turi dinamišką stiprų implicitinį tipavimą.

Tačiau nėra kalbų su statiniu ir dinaminiu spausdinimu vienu metu. Nors, žvelgdamas į priekį, pasakysiu, kad čia guliu - jie tikrai egzistuoja, bet apie tai vėliau.

Detali versija

Jei trumpos versijos jums nepakako, viskas gerai. Ar ne veltui parašiau išsamų? Svarbiausia, kad visos naudingos ir įdomios informacijos buvo tiesiog neįmanoma sutalpinti į trumpą variantą, o išsamus turbūt būtų per ilgas, kad visi perskaitytų neįsitempę.

Be tipo spausdinimas

Be tipo programavimo kalbose visi objektai laikomi tiesiog įvairaus ilgio bitų sekomis.

Be tipo spausdinimas dažniausiai būdingas žemo lygio (assembly language, Forth) ir ezoterinėms (Brainfuck, HQ9, Piet) kalboms. Tačiau, kartu su trūkumais, jis turi ir privalumų.

Privalumai
  • Leidžia rašyti itin žemu lygiu, o kompiliatorius/vertėjas netrukdys atlikti jokių tipo patikrų. Galite laisvai atlikti bet kokias operacijas su bet kokio tipo duomenimis.
  • Gautas kodas paprastai yra efektyvesnis.
  • Instrukcijų skaidrumas. Jei mokate kalbą, paprastai nekyla abejonių, kas yra tas ar kitas kodas.
Trūkumai
  • Sudėtingumas. Dažnai reikia pateikti sudėtingas reikšmes, tokias kaip sąrašai, eilutės ar struktūros. Tai gali sukelti nepatogumų.
  • Trūksta čekių. Bet kokie beprasmiai veiksmai, pavyzdžiui, žymeklio į masyvą atėmimas iš simbolio, bus laikomi visiškai normaliais, o tai kupina subtilių klaidų.
  • Žemas abstrakcijos lygis. Darbas su bet kokiu sudėtingu duomenų tipu nesiskiria nuo darbo su skaičiais, o tai, žinoma, sukels daug sunkumų.
Stiprus be tipo spausdinimas?

Taip, toks dalykas egzistuoja. Pavyzdžiui, asamblėjos kalba (x86/x86-64 architektūrai, kitų nežinau) negalite surinkti programos, jei bandote įkelti duomenis iš rax registro (64 bitai) į cx registrą (16 bitų). .

mov cx, eax ; surinkimo laiko klaida

Taigi paaiškėja, kad surinkėjas vis dar spausdina? Manau, kad šių patikrinimų neužtenka. Ir jūsų nuomonė, žinoma, priklauso tik nuo jūsų.

Statinis ir dinaminis spausdinimas

Pagrindinis dalykas, kuris skiria statinį spausdinimą nuo dinaminio, yra tai, kad visas tipo tikrinimas atliekamas kompiliavimo metu, o ne vykdymo metu.

Kai kurie žmonės gali manyti, kad statinis spausdinimas yra per daug ribojantis (iš tikrųjų taip yra, bet tai jau seniai pašalinta naudojant kai kuriuos metodus). Kai kurie žmonės sako, kad dinamiškai įvestos kalbos žaidžia su ugnimi, bet kokios savybės jas išskiria? Ar tikrai abi rūšys turi galimybę egzistuoti? Jei ne, kodėl yra tiek daug kalbų, kurios įvedamos ir statiškai, ir dinamiškai?

Išsiaiškinkime.

Statinio spausdinimo pranašumai
  • Tipo tikrinimas atliekamas tik vieną kartą – kompiliavimo etape. Tai reiškia, kad mums nereikės nuolatos aiškintis, ar bandome padalyti skaičių iš eilutės (ir padarome klaidą, ar atliekame konversiją).
  • Vykdymo greitis. Iš ankstesnio punkto aišku, kad statiškai įvestos kalbos beveik visada yra greitesnės nei dinamiškai įvestos.
  • Esant tam tikroms papildomoms sąlygoms, jis leidžia aptikti galimas klaidas jau kompiliavimo etape.
Dinaminio rašymo pranašumai
  • Universalių kolekcijų kūrimo paprastumas - krūvos visko ir visų (retai toks poreikis iškyla, bet kai atsiranda dinamiškas spausdinimas, tai padės).
  • Patogumas aprašyti apibendrintus algoritmus (pavyzdžiui, masyvo rūšiavimas, kuris veiks ne tik sveikųjų skaičių sąraše, bet ir realiųjų skaičių sąraše ir net eilučių sąraše).
  • Lengva išmokti – dinamiškai įvestos kalbos paprastai yra labai tinkamos norint pradėti programuoti.

Apibendrintas programavimas

Gerai, svarbiausias dinaminio spausdinimo argumentas yra bendrųjų algoritmų aprašymo patogumas. Įsivaizduokime problemą – mums reikia funkcijos, kad galėtume ieškoti keliuose masyvuose (arba sąrašuose) – sveikųjų skaičių masyvą, realiųjų skaičių masyvą ir simbolių masyvą.

Kaip mes tai išspręsime? Išspręskime tai 3 skirtingomis kalbomis: viena su dinaminiu spausdinimu ir dviem su statiniu.

Naudosiu vieną iš paprasčiausių paieškos algoritmų – brute force. Funkcija gaus ieškomą elementą, patį masyvą (arba sąrašą) ir grąžins elemento indeksą, o jei elementas nerastas – (-1).

Dinaminis sprendimas („Python“):

Def find(required_element, list): for (indeksas, elementas) in enumerate (sąrašas): if elementas == reikalingas_elementas: grąžina indeksą grąžinti (-1)

Kaip matote, viskas paprasta ir nėra problemų dėl to, kad sąraše gali būti skaičių, sąrašų ar kitų masyvų. Labai gerai. Eikime toliau – tą pačią problemą išspręskite C!

Statinis tirpalas (C):

Unsigned int find_int(int reikalingas_elementas, int masyvas, unsigned int dydis) ( for (unsigned int i = 0; i< size; ++i) if (required_element == array[i]) return i; return (-1); } unsigned int find_float(float required_element, float array, unsigned int size) { for (unsigned int i = 0; i < size; ++i) if (required_element == array[i]) return i; return (-1); } unsigned int find_char(char required_element, char array, unsigned int size) { for (unsigned int i = 0; i < size; ++i) if (required_element == array[i]) return i; return (-1); }

Na, kiekviena funkcija atskirai yra panaši į Python versiją, bet kodėl jos yra trys? Ar statinis programavimas tikrai pasimetė?

Taip ir ne. Yra keletas programavimo būdų, iš kurių vieną dabar apsvarstysime. Tai vadinama bendruoju programavimu, o C++ kalba jį gana gerai palaiko. Pažvelkime į naują versiją:

Statinis sprendimas (bendrasis programavimas, C++):

Šablonas unsigned int find(T reikalingas_elementas, std::vektorius masyvas) ( for (nesigned int i = 0; i< array.size(); ++i) if (required_element == array[i]) return i; return (-1); }

gerai! Tai neatrodo daug sudėtingesnė nei Python versija ir nereikalauja daug rašymo. Be to, mes turime visų masyvų įgyvendinimą, o ne tik 3, reikalingus problemai išspręsti!

Atrodo, kad ši versija yra būtent tai, ko mums reikia – gauname ir statinio spausdinimo, ir kai kuriuos dinaminio spausdinimo privalumus.

Puiku, kad tai apskritai įmanoma, bet gali būti dar geriau. Pirma, apibendrintas programavimas gali būti patogesnis ir gražesnis (pavyzdžiui, Haskell kalba). Antra, be apibendrinto programavimo galima naudoti ir polimorfizmą (rezultatas bus prastesnis), funkcijų perkrovimą (panašiai) ar makrokomandas.

Statika dinamikoje

Taip pat reikėtų paminėti, kad daugelis statinių kalbų leidžia dinamiškai įvesti tekstą, pavyzdžiui:

  • C# palaiko dinaminį pseudo tipą.
  • F# palaiko sintaksinį cukrų operatoriaus ? forma, kurio pagrindu galima įgyvendinti dinaminio spausdinimo imitaciją.
  • Haskell – dinaminį spausdinimą užtikrina modulis Data.Dynamic.
  • Delphi – per specialų Variant tipą.

Be to, kai kurios dinamiškai įvestos kalbos leidžia pasinaudoti statinio spausdinimo pranašumais:

  • Common Lisp – tipo deklaracijos.
  • Perl – nuo ​​5.6 versijos, gana ribota.

Stiprus ir silpnas spausdinimas

Stipriai įvestos kalbos neleidžia maišyti skirtingų tipų objektų išraiškose ir neatlieka jokių automatinių konversijų. Jos dar vadinamos „stipriai įvestomis kalbomis“. Anglų kalbos terminas yra stiprus rašymas.

Silpnai įvestos kalbos, atvirkščiai, skatina programuotoją maišyti skirtingus tipus vienoje išraiškoje, o pats kompiliatorius viską sumažins iki vieno tipo. Jos dar vadinamos „laisvai spausdintomis kalbomis“. Anglų kalbos terminas yra silpnas rašymas.

Silpnas spausdinimas dažnai painiojamas su dinaminiu spausdinimu, kuris yra visiškai neteisingas. Dinamiškai spausdinama kalba gali būti silpnai arba stipriai spausdinama.

Tačiau nedaugelis žmonių skiria spausdinimo griežtumą. Dažnai teigiama, kad jei kalba įvedama statiškai, kompiliuodami galite pastebėti daugybę galimų klaidų. Jie tau meluoja!

Kalba taip pat turi būti stipri. Iš tiesų, jei kompiliatorius, užuot pranešęs apie klaidą, tiesiog prideda prie skaičiaus eilutę arba, dar blogiau, iš vieno masyvo atima kitą, kokia mums nauda, ​​kad visos tipų „patikros“ bus kompiliavimo metu. etapas? Teisingai – silpnas statinis spausdinimas yra dar blogesnis nei stiprus dinaminis spausdinimas! (na tokia mano nuomone)

Taigi ar silpnas spausdinimas neturi jokių pranašumų? Tai gali atrodyti taip, bet nepaisant to, kad esu karštas stipraus spausdinimo šalininkas, turiu sutikti, kad silpnas spausdinimas turi ir privalumų.

Norite sužinoti, kurios?

Stipraus spausdinimo pranašumai
  • Patikimumas – vietoj neteisingo elgesio gausite išimtį arba kompiliavimo klaidą.
  • Greitis - Vietoj paslėptų konversijų, kurios gali būti gana brangios, su stipriu spausdinimu turite jas parašyti aiškiai, o tai verčia programuotoją bent jau žinoti, kad ši kodo dalis gali būti lėta.
  • Programos veikimo supratimas – vėlgi, vietoj numanomo tipo liejimo, programuotojas viską rašo pats, vadinasi, maždaug supranta, kad eilutės ir skaičiaus palyginimas nevyksta savaime ir ne burtų keliu.
  • Tikrumas – kai rašote transformacijas ranka, tiksliai žinote, ką konvertuojate ir į ką. Taip pat visada žinosite, kad dėl tokių konversijų gali sumažėti tikslumas ir gauti neteisingi rezultatai.
Silpno rašymo pranašumai
  • Patogus naudoti mišrius reiškinius (pavyzdžiui, iš sveikųjų ir realiųjų skaičių).
  • Atsiribojimas nuo spausdinimo ir susitelkimas į užduotį.
  • Įrašo trumpumas.

Gerai, išsiaiškinome, pasirodo, silpnas spausdinimas turi ir privalumų! Ar yra būdų, kaip silpno spausdinimo pranašumus perkelti į stiprų spausdinimą?

Pasirodo, yra net du.

Netiesioginis tipo liejimas, nedviprasmiškose situacijose ir neprarandant duomenų

Oho... Gana ilga mintis. Leiskite man dar labiau sutrumpinti iki „riboto numanomo konvertavimo“. Ką reiškia nedviprasmiška situacija ir duomenų praradimas?

Vienareikšmiška situacija – tai transformacija ar operacija, kurios esmė iš karto aiškėja. Pavyzdžiui, dviejų skaičių pridėjimas yra vienareikšmė situacija. Bet skaičių konvertuoti į masyvą nėra (galbūt bus sukurtas vieno elemento masyvas, galbūt tokio ilgio masyvas, pagal numatytuosius nustatymus užpildytas elementais ir galbūt skaičius bus konvertuotas į eilutę, o tada į masyvą simbolių).

Prarasti duomenis dar lengviau. Jei realųjį skaičių 3,5 konvertuosime į sveikąjį skaičių, prarasime dalį duomenų (tiesą sakant, ši operacija irgi dviprasmiška – kaip bus atliktas apvalinimas? Aukštyn? Žemyn? Trupmeninę dalį atmesti?).

Konversijos dviprasmiškose situacijose ir konversijos su duomenų praradimu yra labai, labai blogos. Programavime nėra nieko blogesnio už tai.

Jei netikite manimi, mokykitės PL/I kalbos ar net tiesiog pasižiūrėkite jos specifikaciją. Jame yra VISŲ duomenų tipų konvertavimo taisyklės! Tai tik pragaras!

Gerai, prisiminkime apie ribotą numanomą konversiją. Ar yra tokių kalbų? Taip, pavyzdžiui, Pascal galite konvertuoti sveikąjį skaičių į tikrąjį skaičių, bet ne atvirkščiai. Taip pat yra panašių mechanizmų C#, Groovy ir Common Lisp.

Gerai, sakiau, kad vis dar yra būdas gauti keletą silpno spausdinimo stipria kalba pranašumų. Ir taip, jis egzistuoja ir vadinamas konstruktoriaus polimorfizmu.

Paaiškinsiu tai naudodamas nuostabios Haskell kalbos pavyzdį.

Polimorfiniai konstruktoriai atsirado pastebėjus, kad saugios numanomos konversijos dažniausiai reikalingos, kai naudojami skaitiniai literalai.

Pavyzdžiui, reiškinyje pi + 1 nenorite rašyti pi + 1.0 arba pi + float(1) . Aš tiesiog noriu parašyti pi + 1!

Ir tai daroma Haskell dėl to, kad pažodinis 1 neturi konkretaus tipo. Ji nėra nei vientisa, nei tikra, nei sudėtinga. Tai tik skaičius!

Dėl to, rašydami paprastą funkciją suma x y , padauginus visus skaičius nuo x iki y (su prieaugiu 1), gauname iš karto kelias versijas - sveikųjų skaičių suma, realiųjų suma, racionaliųjų suma, kompleksinių skaičių suma ir net visų tų skaičių tipų, kuriuos jūs pats apibrėžėte, suma.

Žinoma, ši technika taupo tik naudojant mišrius posakius su skaitiniais literalais, ir tai tik ledkalnio viršūnė.

Taigi galime teigti, kad geriausias sprendimas yra balansuoti ant ribos tarp stipraus ir silpno spausdinimo. Tačiau jokia kalba dar nesukuria tobulos pusiausvyros, todėl labiau linkstu į stipriai įvestas kalbas (pvz., Haskell, Java, C#, Python), o ne silpnai įvestas kalbas (pvz., C, JavaScript, Lua, PHP).

Aiškus ir numanomas spausdinimas

Aiškiai įvesta kalba reikalauja, kad programuotojas nurodytų visų deklaruojamų kintamųjų ir funkcijų tipus. Angliškas terminas yra aiškus spausdinimas.

Kita vertus, netiesiogiai įvesta kalba skatina pamiršti tipus ir palikti tipus daryti kompiliatoriui arba vertėjui. Anglų kalbos terminas yra implicit typing.

Iš pradžių galite manyti, kad numanomas spausdinimas yra tolygus dinaminiam, o aiškus – statiniam, bet vėliau pamatysime, kad taip nėra.

Ar kiekvienas tipas turi pranašumų ir vėlgi, ar yra jų derinių ir ar yra kalbų, kurios palaiko abu metodus?

Aiškaus rašymo pranašumai
  • Kiekviena funkcija turi parašą (pavyzdžiui, int add(int, int)), todėl lengva nustatyti, ką funkcija atlieka.
  • Programuotojas iš karto užrašo, kokio tipo reikšmes galima išsaugoti tam tikrame kintamajame, todėl nereikia jo atsiminti.
Netiesioginio spausdinimo pranašumai
  • Trumpasis žymėjimas - def add(x, y) yra aiškiai trumpesnis nei int add(int x, int y) .
  • Atsparumas pokyčiams. Pavyzdžiui, jei funkcijoje laikinas kintamasis buvo to paties tipo kaip įvesties argumentas, tai aiškiai įvestoje kalboje, keičiant įvesties argumento tipą, reikės pakeisti ir laikinojo kintamojo tipą.

Gerai, aišku, kad abu metodai turi ir pliusų, ir minusų (kas ko nors kito tikėjosi?), tad ieškokime būdų, kaip sujungti šiuos du būdus!

Aiškus spausdinimas pagal pasirinkimą

Yra kalbų su numanomu spausdinimu pagal numatytuosius nustatymus ir galimybe nurodyti reikšmių tipą, jei reikia. Vertėjas automatiškai išves tikrąjį išraiškos tipą. Viena iš šių kalbų yra Haskell, aiškumo dėlei leiskite pateikti paprastą pavyzdį:

Be aiškios tipo specifikacijos pridėti (x, y) = x + y – aiški tipo specifikacija pridėti:: (Sveikasis skaičius, Sveikasis skaičius) -> Sveikasis skaičius pridėti (x, y) = x + y

Pastaba: aš tyčia naudojau nepakartojamą funkciją ir taip pat sąmoningai parašiau asmeninį parašą vietoj bendresnio priedo:: (Num a) -> a -> a -> a , nes Norėjau parodyti idėją nepaaiškindamas Haskell sintaksės.

Hm. Kaip matome, jis labai gražus ir trumpas. Funkcijai įrašyti reikia tik 18 simbolių vienoje eilutėje, įskaitant tarpus!

Tačiau automatinio tipo išvados yra gana sudėtingas dalykas ir netgi tokia šauni kalba kaip Haskell kartais nepavyksta. (pavyzdys yra monomorfizmo apribojimas)

Ar yra kalbų, kuriose pagal numatytuosius nustatymus naudojamas aiškus spausdinimas ir, jei reikia, numanomas? Con
tikrai.

Netiesioginis spausdinimas pagal pasirinkimą

Naujasis C++ kalbos standartas, vadinamas C++11 (anksčiau vadintas C++0x), įvedė automatinį raktinį žodį, leidžiantį kompiliatoriui pagal kontekstą nustatyti tipą:

Palyginkime: // Rankiniu būdu nurodant tipą unsigned int a = 5; nepasirašytas int b = a + 3; // Unsigned tipo automatinė išvestis in a = 5; auto b = a + 3;

Neblogai. Tačiau įrašų nedaug sumažėjo. Pažvelkime į pavyzdį su iteratoriais (jei nesuprantate, nebijokite, svarbiausia atkreipti dėmesį į tai, kad dėl automatinio išvesties įrašymas labai sumažėja):

// Rankiniu būdu nurodant std::vektoriaus tipą vec = atsitiktinisVektorius(30); for (std::vector::const_iterator it = vec.cbegin(); ...) ( ... ) // Automatinė tipo išvada auto vec = randomVector (trisdešimt); for (auto it = vec.cbegin(); ...) ( ... )

Oho! Tai yra santrumpa. Gerai, bet ar galima padaryti kažką panašaus į Haskell, kur grąžinimo tipas priklauso nuo argumentų tipų?

Ir vėl atsakymas yra taip, dėka raktinio žodžio decltype kartu su auto:

// Rankinis tipas int divide(int x, int y) ( ... ) // Automatinė tipo išvada auto divide(int x, int y) -> decltype(x / y) (... )

Ši žymėjimo forma gali neatrodyti labai gerai, tačiau kartu su bendru programavimu (šablonai / bendrieji dalykai), numanomas spausdinimas arba automatinė tipo išvada daro stebuklus.

Kai kurios programavimo kalbos pagal šią klasifikaciją

Pateiksiu nedidelį populiarių kalbų sąrašą ir parašysiu, kaip jos skirstomos į kiekvieną „spausdinimo“ kategoriją.

JavaScript – Dinaminis / Silpnas / Netiesioginis rubinas - Dinaminis / Stiprus / Netiesioginis Python - Dinaminis / Stiprus / Netiesioginis Java - Statinis / Stiprus / Aiškus PHP - Dinaminis / Silpnas / Netiesioginis C - Statinis / Silpnas / Aiškus C++ - Statinis / Pusiau stiprus / Aiškus „Perl“ – dinaminis / silpnas / netiesioginis tikslas-C – statinis / silpnas / aiškus C# – statiškas / stiprus / aiškus „Haskell“ – statiškas / stiprus / numanomas bendras Lisp – dinaminis / stiprus / numanomas

Galbūt kažkur suklydau, ypač su CL, PHP ir Obj-C, jei turite kitokią nuomonę apie kurią nors kalbą, rašykite komentaruose.

Išvada

GERAI. Greitai bus šviesu ir jaučiu, kad apie spausdinimą nėra ką daugiau pasakyti. O kaip? Ar tema be dugno? Ar daug liko nepasakyta? Prašome pasidalinti naudinga informacija komentaruose.

Būtinos sąlygos

Griežtas spausdinimas reiškia, kad yra įvykdytos šios privalomos sąlygos:

  1. Bet koks duomenų objektas (kintamasis, konstanta, išraiška) kalboje visada turi griežtai apibrėžtą tipą, kuris yra fiksuotas programos kompiliavimo metu (statinis tipavimas) arba nustatomas vykdymo metu (dinaminis tipavimas).
  2. Kintamajam galima priskirti tik tokią reikšmę, kurios duomenų tipas yra toks pat kaip ir kintamasis, perduodant parametrus ir grąžinant funkcijų rezultatus.
  3. Kiekvienai operacijai reikalingi griežtai apibrėžtų tipų parametrai.
  4. Numanomas tipo konvertavimas neleidžiamas (ty vertėjas bet kokį bandymą naudoti kito tipo reikšmę, nei deklaruota kintamajam, parametrui, funkcijai ar operacijai, laiko sintaksės klaida).

Jei griežtai laikomasi griežtų spausdinimo reikalavimų, net tie duomenų tipai, kurių reikšmių sudėtis ir leistinos operacijos yra identiški, yra nesuderinami. Jei programai reikia priskirti vieno duomenų tipo reikšmę kito tipo kintamajam, tai galima padaryti, bet tik aiškiai taikant specialią tipo konvertavimo operaciją, kuri tokiais atvejais dažniausiai yra programavimo kalbos dalis (nors gali formaliai negali būti viena, o teikiama standartinių bibliotekų).

Rašymas programavimo kalbomis

Nuorodos

taip pat žr


Wikimedia fondas. 2010 m.

Pažiūrėkite, kas yra „stiprus rašymas“ kituose žodynuose:

    Duomenų tipas yra pagrindinė programavimo teorijos sąvoka. Duomenų tipas apibrėžia reikšmių rinkinį, operacijų, kurios gali būti taikomos toms reikšmėms, rinkinį ir galbūt būdą, kaip įgyvendinti verčių saugojimą ir atlikti operacijas. Bet kokia... ... Vikipedija

    Duomenų įvedimas Tipo sauga Tipo išvada Dinaminis spausdinimas Statinis spausdinimas Stiprus spausdinimas Minkštas spausdinimas Priklausomi tipai Anties spausdinimas Pagrindinis straipsnis: Stiprus spausdinimas Dinaminis spausdinimas yra plačiai naudojama... ... Vikipedija

    Duomenų įvedimas Tipo sauga Tipo išvada Dinaminis spausdinimas Statinis spausdinimas Stiprus spausdinimas Minkštas spausdinimas Priklausomi tipai Anties spausdinimas Pagrindinis straipsnis: Stiprus spausdinimas Statinis spausdinimas yra plačiai paplitusi technika... ... Wikipedia

    Dinaminis spausdinimas yra metodas, plačiai naudojamas programavimo kalbose ir specifikacijų kalbose, kai kintamasis susiejamas su tipu reikšmės priskyrimo, o ne kintamojo deklaravimo momentu. Taigi, įvairiose srityse... Vikipedija

    Duomenų įvedimas Tipo sauga Tipo išvada Dinaminis spausdinimas Statinis spausdinimas Stiprus spausdinimas Minkštas spausdinimas Priklausomi tipai Duck spausdinimas Tipo išvada programuojant yra kompiliatoriaus funkcija... ... Wikipedia

    Duomenų įvedimas Tipo sauga Tipo išvada Dinaminis spausdinimas Statinis spausdinimas Stiprus spausdinimas Minkštas spausdinimas Priklausomi tipai Duck tipavimas Priklausomas tipas kompiuterių moksle ir logikoje, tipas, kuris priklauso nuo reikšmės. Išlaikomieji... ... Vikipedija

    - (taip pat randamas terminas duomenų tipas) yra pagrindinė programavimo teorijos sąvoka. Duomenų tipas apibrėžia reikšmių rinkinį, operacijų, kurios gali būti taikomos tokioms reikšmėms, rinkinį ir galbūt būdą, kaip įgyvendinti reikšmių saugojimą ir... ... Vikipedija

    Duomenų tipas Turinys 1 Istorija 2 Apibrėžimas 3 Būtinybė naudoti duomenų tipus... Vikipedija

    Šis terminas turi kitas reikšmes, žr. ML (reikšmės). ML Semantika: daugiaparadigma: funkcinė, imperatyvioji, modulinė Pasirodė: 1973 m. Autorius (-ai): Robin Milner et al University of Edinburgh ... Wikipedia