Кіріспе
Компьютерлік бағдарламалау стилі. Жалпылама бағдарламалау – алгоритмдерді кейін анықталатын дерек типтерінің негізінде жазу стилі, олар қажет болғанда нақты типтер параметрлер ретінде берілген кезде қолданылады. Бұл тәсіл 1973 жылы ML бағдарламалау тілінде алғаш пайда болды, ол қолданылған кезде жұмыс істейтін типтер жиынтығымен ғана ерекшеленетін ортақ функцияларды немесе типтерді жазуға мүмкіндік береді, соның арқасында қайталанатын код азаяды. Жалпылама бағдарламалау 1977 жылы Ada тілімен енгізілді. C++ үлгілері арқылы жалпылама бағдарламалау кәсіби кітапханаларды жобалаудың құралына айналды. Техникалар одан әрі жетілдірілді және 1994 жылы шыққан «Design Patterns» кітабында параметрленген типтер енгізілді. Андрей Александреску 2001 жылы жариялаған «Modern C++ Design: Generic Programming and Design Patterns Applied» кітабында жаңа әдістер ұсынылды. Кейіннен D тілі де осы идеяларды қолданысқа енгізді. Мұндай бағдарламалық құралдар Ada, C#, Delphi, Eiffel, F#, Java, Nim, Python, Go, Rust, Swift, TypeScript және Visual Basic NET тілдерінде «жалпыламалар» деп белгілі. ML, Scala, Julia және Haskell тілдерінде олар «параметрлік полиморфизм» деп аталады. (Haskell терминологиясында «жалпылама» термині байланысты, бірақ сәл өзгеше ұғымды да білдіреді.) «Жалпылама бағдарламалау» терминін Дэвид Муссер мен Александр Степанов бастапқыда одан да нақты мағынада қолданған, ол алгоритмдер мен деректер құрылымдарының нақты мысалдарынан дерек типтеріне қойылатын негізгі талаптарды абстракциялап, оларды тұжырымдамалар ретінде ресмилеуді және осы тұжырымдамалар негізінде жалпылама функцияларды жүзеге асыруды, әдетте жоғарыда сипатталғандай тілдің жалпылама механизмдерін пайдалануды білдіреді.
Generic programming is a style of computer programming in which algorithms are written in terms of data types to be specified later that are then instantiated when needed for specific types provided as parameters. This approach, pioneered by the ML programming language in 1973, permits writing common functions or types that differ only in the set of types on which they operate when used, thus reducing duplicate code. Generic programming was introduced to the mainstream with Ada in 1977. With templates in C++, generic programming became part of the repertoire of professional library design. The techniques were further improved and parameterized types were introduced in the influential 1994 book Design Patterns. New techniques were introduced by Andrei Alexandrescu in his 2001 book Modern C++ Design: Generic Programming and Design Patterns Applied. Subsequently, D implemented the same ideas. Such software entities are known as generics in Ada, C#, Delphi, Eiffel, F#, Java, Nim, Python, Go, Rust, Swift, TypeScript, and Visual Basic NET. They are known as parametric polymorphism in ML, Scala, Julia, and Haskell. (Haskell terminology also uses the term "generic" for a related but somewhat different concept.) The term generic programming was originally coined by David Musser and Alexander Stepanov in a more specific sense than the above, to describe a programming paradigm in which fundamental requirements on data types are abstracted from across concrete examples of algorithms and data structures and formalized as concepts, with generic functions implemented in terms of these concepts, typically using language genericity mechanisms as described above.
Жалпылау үшін бағдарламалау тілінің қолдауы
Жалпылау мүмкіндіктері жоғары деңгейдегі тілдерде кем дегенде 1970 жылдардан бері ML, CLU және Ada сияқты тілдерде болды, кейін көптеген нысанға негізделген және нысанға бағытталған тілдерде, соның ішінде BETA, C++, D, Eiffel, Java және DEC-тің қазір тоқтатылған Trellis Owl тілдерінде қолданыс тапты. Жалпылау әртүрлі бағдарламалау тілдерінде әртүрлі жүзеге асырылады және қолдау көрсетіледі; «жалпылау» термині де әртүрлі бағдарламалау контексттерінде әртүрлі мағыналарда қолданылады. Мысалы, Forth-та компилятор компиляция кезінде кодты орындай алады және жаңа компилятор кілт сөздерін және сол сөздердің жаңа нұсқаларын дереу құруға болады. Онда компилятордың жұмысын көрсететін аз ғана кілт сөз бар, сондықтан ол табиғи түрде жалпылау мүмкіндіктерін ұсынады, бірақ Форт мәтіндерінің көпшілігінде бұл туралы айтылмайды. Сол сияқты, динамикалық типтегі тілдер, әсіресе интерпретацияланатын тілдер, әдетте функцияларға мәндерді жіберу және мәндерді тағайындау типке тәуелсіз болғандықтан, жалпылауды автоматты түрде ұсынады, және мұндай әрекет көбінесе абстракция немесе кодты ықшамдау үшін қолданылады, бірақ бұл әдетте жалпылау деп аталынбайды, себебі ол тілде қолданылатын динамикалық типтеу жүйесінің тікелей салдары. Бұл термин функционалдық бағдарламалауда, әсіресе Haskell сияқты тілдерде қолданылады, олар құрылымдық типтік жүйені пайдаланады, онда типтер әрқашан параметрлік болып табылады және осы типтерге арналған нақты код жалпы болып келеді. Бұл қолданыстар кодты сақтау және абстракцияны жасаудың ұқсас мақсатына қызмет етеді. Массивтер мен құрылымдарды алдын ала анықталған жалпы типтер деп қарастыруға болады. Массив немесе құрылым типін әр пайдаланғанда жаңа нақты тип құрылады немесе бұрын құрылған тип қайта қолданылады. Массив элементтері мен құрылым элементтерінің типтері – параметрленген типтер, олар тиісті жалпы типті құру үшін қолданылады. Мұның бәрі көбінесе компиляторда орнатылған және синтаксисі басқа жалпы конструкциялардан өзгеше болады. Кейбір кеңейтілген бағдарламалау тілдері құрастырылған және пайдаланушы анықтаған жалпы типтерді біріктіруге тырысады. Бағдарламалау тілдеріндегі жалпылау механизмдерін кеңінен қарастыру. Жалпы бағдарламалау механизмдерінің тиімділігін салыстыратын нақты зерттеу үшін қараңыз.
Ададағы жалпылама дәрілер
Ada 1977–1980 жылдары жасалғаннан бастап-ақ генерикалық мүмкіндіктерге ие болды. Стандартты кітапхана көптеген қызметтерді ұсыну үшін генерикалық түрлерді пайдаланады. Ada 2005 стандартты кітапханаға C++ стандартты үлгі кітапханасынан шабыттанған толыққанды генерикалық контейнерлер кітапханасын қосты. Генерикалық бірлік – бір немесе бірнеше генерикалық формальды параметрлерді қабылдайтын пакет немесе кіші бағдарлама. Генерикалық формальды параметр – мән, айнымалы, тұрақты, тип, кіші бағдарлама, тіпті басқа, белгіленген генерикалық бірліктің мысалы бола алады. Генерикалық формальды типтер үшін синтаксис дискретті, қозғалатын нүктелі, бекітілген нүктелі, сілтеме (көрсеткіш) типтерін және т.б. ажыратады. Кейбір формальды параметрлерде әдепкі мәндер болуы мүмкін. Генерикалық бірлікті инстанциялау үшін бағдарламашы әрбір формальды параметрге нақты параметрлерді жібереді. Генерикалық мысал кез келген басқа бірлік сияқты жұмыс істейді. Генерикалық бірліктерді орындау уақытында, мысалы, цикл ішінде инстанциялауға болады.
C++ үлгілері
C++ жалпы бағдарламалау техникаларын қолдану үшін үлгілерді пайдаланады. C++ стандартты кітапханасы стандартты үлгі кітапханасын, немесе STL-ді қамтиды, ол жиі қолданылатын дерек құрылымдары мен алгоритмдерге арналған үлгілердің негізін ұсынады. C++ үлгілері үлгі метапрограммалау үшін де қолданылуы мүмкін, ол кодты орындау кезінде емес, компиляция кезінде бағалаудың бір жолы. Үлгілерді мамандандыру арқылы C++ үлгілері Тьюринг толықтығына ие.
Үлгілерді мамандандыру
C++ үлгілерінің қуатты мүмкіндігі – үлгілерді мамандандыру. Бұл, параметрленген типтің белгілі бір ерекшеліктеріне сүйене отырып, баламалы іске асыруларды ұсынуға мүмкіндік береді. Үлгілерді мамандандырудың екі мақсаты бар: белгілі бір оңтайландыру түрлерін іске қосу және кодтың көлемділігін азайту. Мысалы, сұрыптау үлгісінің функциясын қарастырайық. Мұндай функцияның негізгі міндеттерінің бірі – контейнердегі екі позициядағы мәндерді ауыстыру немесе алмастыру болып табылады. Егер мәндер үлкен болса (әрқайсысын сақтауға қажетті байттар саны тұрғысынан), онда алдымен нысандарға сілтемелердің жеке тізімін құру, содан кейін осы сілтемелерді сұрыптап, түпкілікті сұрыпталған тізбекті құру әдетте жылдамырақ болады. Алайда, егер мәндер өте кішкентай болса, онда қажет болған жағдайда мәндерді тікелей ауыстыру әдетте ең жылдам болады. Сонымен қатар, егер параметрленген тип өзінің индексінің бір түрі болса, жеке сілтемелер массивін құрудың қажеті жоқ. Үлгілерді мамандандыру, үлгіні жасаушыға әртүрлі іске асыруларды жазуға және әрбір іске асыру үшін параметрленген типтің қандай ерекшеліктерге ие болуы керектігін анықтауға мүмкіндік береді. Функциялық үлгілерден айырмашылығы, сыныптық үлгілерді ішінара мамандандыруға болады. Бұл, кейбір үлгі параметрлері белгілі болғанда, қалған үлгі параметрлерін жалпы қалдыра отырып, сыныптық үлгі кодынның баламалы нұсқасын ұсынуға болады дегенді білдіреді. Мысалы, бұл параметрленген типті көшірудің қымбат екенін болжайтын әдепкі іске асыруды (бастапқы мамандандыруды) құру үшін пайдаланылуы мүмкін, содан кейін көшірудің арзан типтері үшін ішінара мамандандыруды құру арқылы жалпы тиімділікті арттыруға болады. Мұндай сыныптық үлгінің клиенттері тек оның мамандандырылған нұсқаларын пайдаланады, компилятордың әр жағдайда бастапқы немесе ішінара мамандандыруды қолданғанын білудің қажеті жоқ. Сыныптық үлгілерді толық мамандандыруға да болады, яғни барлық параметрленген типтер белгілі болғанда баламалы іске асыру ұсынылуы мүмкін.
Эйфельдегі жалпылау
Жалпы сыныптар Эйфельдің бастапқы әдістемесі мен тіл дизайнынан бері оның құрамында болды. Эйфельдің негізгі жарияланымдары жалпы сыныптарды жасау және қолдануды сипаттау үшін «жалпыламалық» терминін пайдаланады.
Жавадағы жалпыламалар
Жава бағдарламалау тіліне 2004 жылы J2SE 5.0 құрамында жалпыламалар, яғни "T типіндегі контейнерлер" үшін қолдау қосылды. Java-да жалпыламалар тек компиляция кезінде типтік дұрыстығы үшін тексеріледі. Содан кейін жалпы типтік ақпарат "типті жою" процесі арқылы алынып тасталады, бұл ескі JVM нұсқаларымен үйлесімділікті сақтау үшін жасалады, нәтижесінде ол орындалу кезінде қолжетімді болмайды. Мысалы, `List<String>` тізімі шикі типті `List` түріне түрлендіріледі. Компилятор тізімнен элементтер алынған кезде оларды `String` типіне түрлендіру үшін типтік айырбастауларды (type casts) қосады, бұл C++ үлгілері сияқты басқа да іске асырулармен салыстырғанда өнімділікті төмендетеді.