Кіріспе
Кейбір бағдарламалау тілдерінің ерекшелігі Көп жолдау немесе мультиметодтар – кейбір бағдарламалау тілдерінің мүмкіндігі, онда функция немесе әдіс орындалу кезінде (динамикалық) аргументтерінің бірінен көптің түріне немесе, жалпы жағдайда, олардың басқа да атрибуттарына қарай динамикалық түрде таңдалады. Бұл бір жолдау полиморфизмінің кеңейтілген түрі болып табылады, онда функция немесе әдіс шақырылымы әдіс шақырылған нысанның түріне қарай динамикалық түрде таңдалады. Көп жолдау динамикалық шақыруды бір немесе бірнеше аргументтерінің біріктірілген ерекшеліктерін пайдаланып, тиісті функцияға немесе әдіске бағыттайды.
Multiple dispatch or multimethods is a feature of some programming languages in which a function or method can be dynamically dispatched based on the run time (dynamic) type or, in the more general case, some other attribute of more than one of its arguments. This is a generalization of single dispatch polymorphism where a function or method call is dynamically dispatched based on the derived type of the object on which the method has been called. Multiple dispatch routes the dynamic dispatch to the implementing function or method using the combined characteristics of one or more arguments.
Жіберуді түсіну
Компьютерлік бағдарламалық қамтамасыз етуді әзірлеушілер бастапқы кодты әдетте түрліше аталатын блоктарға ұйымдастырады: кіші бағдарламалар, процедуралар, кіші программалар, функциялар немесе әдістер. Функциядағы код оны шақыру арқылы орындалады – функцияның атына сілтеме жасайтын код бөлігін орындау. Бұл басқаруды уақытша шақырылған функцияға жүктейді; функция орындалғаннан кейін басқару әдетте шақырушыдағы сілтемеден кейінгі нұсқаулыққа қайта оралады. Функция атаулары көбінесе функцияның мақсатын көрсететіндей етіп таңдалады. Кейде бірнеше функцияға бірдей атау беру қажет болады, әсіресе олар ұқсас міндеттерді орындайтын болса, бірақ әртүрлі кіріс деректерімен жұмыс істейтін болады. Мұндай жағдайларда, функцияны шақыру орнындағы атау сілтемесі орындалатын код блогын анықтау үшін жеткіліксіз. Оның орнына, функцияға берілетін аргументтердің саны мен түрі бірнеше функцияның ішінен дұрысын таңдау үшін қолданылады. Әдеттегі, яғни, бір жолдаулы объектіге бағытталған бағдарламалау тілдерінде әдіс шақырылғанда (Smalltalk-та хабар жіберу, C++-та мүшелік функцияны шақыру), оның аргументтерінің бірі ерекше қарастырылады және осы аттағы әдістердің (мүмкін көптеген) қайсысы қолданылатынын анықтау үшін пайдаланылады. Көптеген тілдерде ерекше аргумент синтаксистік тұрғыдан көрсетіледі; мысалы, бағдарламалау тілдерінің бірі ерекше аргументті әдіс шақырылғанда нүктенің алдына қояды: special.әдіс(басқа, аргументтер, мұнда), сондықтан арыстан.дыбыс күркіреуін, ал қарға.дыбыс шырылдауын шығарады. Керісінше, көп жолдаулы тілдерде таңдалған әдіс – функция шақырылымының саны мен түріне сәйкес келетін әдіс болады. Функцияны/әдісті нақты шақыруда орындайтын ерекше аргумент болмайды. Common Lisp Object System (CLOS) – көп жолдаудың ерте және танымал мысалы. Көптік жөнелтуді қолданудың тағы бір маңызды мысалы – Julia бағдарламалау тілі. Көптік жөнелтуді функцияны жүктеуден ажырату керек, онда статикалық типтеу ақпараты, мысалы, терминнің жарияланған немесе қорытындыланған түрі (немесе субтиптеуі бар тілдегі негізгі түрі) белгілі бір шақыру орнында қандай мүмкіндіктер қолданылатынын анықтау үшін пайдаланылады және бұл анықтама компиляция немесе байланыс уақытында (немесе бағдарлама орындалуы басталар алдындағы басқа уақытта) жасалады және содан кейін бағдарламаның орнатылуы немесе орындалуы үшін өзгермейді. C++ сияқты көптеген тілдер берік функцияны жүктеуді ұсынады, бірақ динамикалық көптік жөнелтуді ұсынбайды (C++ виртуалды функцияларды пайдалану арқылы тек бір рет жөнелтуге ғана рұқсат береді).
Деректер түрлері
Компиляция кезінде дерек түрлерін ажырата алатын тілдермен жұмыс істегенде, баламалардың арасынан таңдау жасау мүмкін болады. Компиляция уақытында таңдау үшін мұндай баламалы функцияларды жасау процесі әдетте функцияны жүктеу деп аталады. Дерек түрлерін анықтауды орындалу уақытына дейін кейінге қалдыратын (яғни, кеш байланыс) бағдарламалау тілдерінде, баламалы функциялардың арасынан таңдау функция аргументтерінің динамикалық түрлеріне сүйене отырып жасалуы керек. Осылайша таңдалған баламалы нұсқалары бар функциялар көбінесе мультиметодтар деп аталады. Функция шақыруларын динамикалық түрде орындаумен байланысты белгілі бір уақыт шығыны бар. Кейбір тілдерде жүктеу мен мультиметодтар арасындағы шекара бұлыңқы болуы мүмкін, мұнда компилятор нақты функция шақыруы үшін компиляция уақытында таңдау қолданылуы мүмкін бе, әлде баяурақ орындалу уақытында орындалу қажет пе, соны анықтайды.
Мәселелер
Динамикалық жөнелтудің, жалғыз және көптік түрлерінде, бірнеше мәлімделген мәселелері бар. Көптеген осы мәселелер жалғыз жөнелту үшін шешілген, ол ондаған жылдар бойы объектіге бағытталған бағдарламалау тілдерінде стандартты мүмкіндік болып келді, бірақ көптік жөнелту жағдайында бұл мәселелер күрделене түседі.
Экспрессивтілік және модульдік
Көптеген танымал бағдарламалау тілдерінде бастапқы код функционалдық бөліктерде (біз оларды осы жерде пакеттер деп атаймыз) жеткізіледі және орналастырылады; бұл ұғымның нақты терминологиясы тілдер арасында өзгеше болуы мүмкін. Әрбір пакетте бірнеше тип, мән және функция анықтамалары болуы мүмкін, пакеттер компиляция қадамы бар тілдерде көбінесе жеке-жеке құрастырылады және айналма тәуелділіктер болмауы мүмкін. Толық бағдарлама – бұл пакеттер жиынтығы, оның ішінде бір негізгі пакет, ол бірнеше басқа пакеттерге тәуелді болуы мүмкін, ал бағдарламаның өзі тәуелділік қатынасының транзитивті жабылуынан тұрады. «Көрсету мәселесі» деп аталатын мәселе, тәуелді пакеттегі кодтың негізгі пакетке бастапқы кодты өзгертпей, негізгі пакетте анықталған мінез-құлықтарды (функцияларды немесе дерек түрлерін) кеңейту мүмкіндігіне қатысты. Традициялық бір жолдамалы объектіге бағытталған тілдер жаңа дерек түрлерін қосуды оңай жасайды, бірақ жаңа функцияларды емес; дәстүрлі функционалдық тілдер көбінесе кері әсер етеді, ал көп жолдамалы тілдер, дұрыс іске асырылса, екеуіне де мүмкіндік береді. Көп жолдамалы тілді іске асыру үшін келесі қасиеттерге ие болу қажет:
Базалық пакеттің бастапқы кодын өзгертпей, әртүрлі пакеттерден бірнеше әдістің әртүрлі «жағдайларын» анықтау мүмкін болуы керек. Бағдарламаға басқа пакетті қосу, егер шақыруда осы пакетте анықталған дерек түрлері қолданылмаса, берілген көп әдіс шақыруының мінез-құлқын өзгертпеуі керек. Керісінше, егер дерек түрі белгілі бір пакетте анықталса, және сол түрді пайдаланатын көп әдіс кеңейтуі де сол пакетте анықталса, және осы типтің мәні (негізгі типке сілтеме арқылы немесе жалпы функцияға) сол пакетке тәуелділігі жоқ басқа пакетке жіберілсе, содан кейін көп әдіс осы мәнмен аргумент ретінде шақырылса, типті қамтитын пакетте анықталған көп әдіс жағдайы қолданылуы керек. Басқаша айтқанда, берілген бағдарламада, бірдей аргументтер жиынтығымен шақырылған бірдей көп әдіс, шақыру орнының орналасуына және берілген анықтаманың «көріну аймағында» немесе «көрінетін» болғандығына қарамастан, әрқашан бірдей іске асырылуға шешім қабылдауы керек.
Тиімділік
Бір жолдауды тиімді жүзеге асыру, оның ішінде объектілік кодқа жеке-жеке компиляцияланып, төмен деңгейдегі (тілді танымайтын) сілтемелеушімен байланыстырылатын, соның ішінде бағдарлама жүктелу/іске қосылу кезінде немесе тіпті қолданба кодының басқаруымен динамикалық түрде байланыстырылатын бағдарламалау тілдерінде жақсы белгілі. C++ және басқа ерте OO тілдерінде (әр сыныптың сол сыныптың виртуалды функцияларына сәйкес функция көрсеткіштерінің массиві бар) жасалған "vtable" әдісі, статикалық әдіс шақыруымен шамалас жылдамдыққа ие, тіпті оңтайландырылмаған жағдайда да O(1) үстеме шығынды және тек бір қосымша жадқа жүгінуді қажет етеді. Дегенмен, vtable әдісі функция атын, емес, аргумент түрін іздеу кілті ретінде пайдаланады және көп жолдау жағдайына дейін кеңейтілмейді. (Ол сондай-ақ, әдістердің объектіге бағытталған парадигмаға тәуелді, олар кез келген нақты дерек типінен тәуелсіз жеке емес, сыныптардың мүмкіндіктері болып табылады). Көп жолдауды тиімді жүзеге асыру мәселесі әлі де зерттеу нысаны болып қалады.
Іс жүзіндегі қолдану
Бірнеше рет жіберудің тәжірибеде қаншалықты жиі қолданылатынын бағалау үшін Muschevici және авторлар динамикалық диспетчерді қолданатын бағдарламаларды зерттеді. Олар 6 түрлі тілде жазылған, негізінен компиляторлар болатын 9 қолданбаны талдады: Common Lisp Object System, Dylan, Cecil, MultiJava, Diesel және Nice. Олардың нәтижелері 13–32% жалпы функциялардың бір аргументтің динамикалық түрін пайдаланатынын, ал 2.7–6.5% бірнеше аргументтің динамикалық түрін пайдаланатынын көрсетті. Қалған 65–93% жалпы функциялардың бір нақты әдісі (overrider) бар, сондықтан олар аргументтерінің динамикалық түрлерін пайдаланбайды. Зерттеу сондай-ақ жалпы функциялардың 2–20%-ы екі, ал 3–6%-ы үш нақты функцияны жүзеге асырғанын көрсетті. Нақты ауыстырушылары көбірек болған функциялар үшін бұл көрсеткіштер тез төмендейді. Көптік жіберу Джулия тілінде әлдеқайда кеңінен қолданылады, себебі көптік жіберу тілдің бастапқы дизайнындағы маңызды ұғым болды. Muschevici сияқты жалпы функция бойынша әдістердің орташа санына қатысты статистиканы жинағанда, Джулия стандартты кітапханасы Muschevici талдаған басқа тілдерге қарағанда екі еседен астам жүктеме мөлшерін пайдаланатыны, ал бинарлық операторлар үшін 10 еседен астам екені анықталды. Бұл объектіге бағытталған тілдердегі коварианттылық және контраварианттылық мәселесін формалдаудың және бинарлық әдістер мәселесін шешудің алғашқы мысалы болды.
Мысалдар
Бірнеше және бір жіберуді ажыратуды мысалмен түсіндіру оңайырақ болуы мүмкін. Егер ойында (пайдаланушыға көрінетін) нысандардың ішінде ғарыш кемелері мен астероидтер болса, екі нысан соқтығысқанда, қандай нысанның қандай нысанға тигеніне қарай бағдарламаның әртүрлі нәрселер жасауы қажет болуы мүмкін.