Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Інформатика програма 9 класу.doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
529.92 Кб
Скачать

§ 11. Списки

        1. Поняття про динамічну пам’ять, вказівники та динамічні змін­­ні. Розрізняють звичайну (статичну) та динамічну організації пам’яті комп’ютера. В оперативній пам’яті можна роз­міс­тити обме­же­ну кількість даних, зокрема, змінних. Коли змінні оголошують у розділі var, система надає їм певний обсяг пам’яті, навіть якщо не всі змінні будуть використані у програмі. Пам’ять, надана змінним, вивільняється лише після виконання програми. Однак є задачі, де заздалегідь невідомо, скільки змінних потрібно для їхнього розв’я­зу­вання, а отже, який обсяг пам’яті слід заре­зерву­вати. У цьому випадку, а також, якщо заздалегідь знають, що даних буде багато, застосовують динамічну організацію пам’яті. Принцип дина­міч­ної організації пам’яті полягає в тому, що змін­ні займають пам’ять за необхідністю, опрацьовуються і в по­тріб­ний момент вивільняють пам’ять. Такi змiннi називаються динамiчними.         Для роботи з динамiчними змiнними використовують тип даних — вка­зiв­ник. Якщо iм’я статичної змінної задає адресу даного в опе­­ра­­тив­ній пам’яті, то вказівник на динамічну змінну — лише тип даного, а не його розташування в пам’яті. Тип даних вказівник описують за допомогою символу ^ у розділі type так:

            type <назва типу> = ^<базовий тип>;

        Конкретні вказівники на динамічні змінні оголошують, як звичайно, у розділі var:

            var <список вказівників на змінні> : <назва типу>;

        Приклад. Розглянемо описи типів вказівників і оголошення вка­зів­ників на динамічні змінн

type VkazNaCili = ^integer; VkazNaMasyv = ^ array [1..100] of real; VkazNaZapys = ^Zapys; var c1, c2 : VkazNaCili; mas1, mas2 : VkazNaMasyv; zap1, zap2 : VkazNaZapys;

        На етапі компіляції пам’ять для масивів та записів тут не надається (але сам вказівник займатиме в пам’яті 4 байти). Пам’ять для даних, про можливість появи яких попереджає вказівник, буде надана на етапі виконання програми за допомогою процедури new:

            new(<вказівник на змінну>);

        Тільки тепер утворилася динамічна змінна, ім’я якої має такий вигляд:

            <вказівник на змінну>^ .

        Розрізняють операції над вказівником на динамічну змінну та операції над самою динамічною змінною.         З динамічною змінною можна виконувати операції, визначені для даних відповідного базового типу.         Над вказівниками визначені дві операції переадресації

            1) <вказівник 1> := <вказівник 2>;             2) <вказівник> := nil;

а також процедури, зокрема, new та dispose.         У результаті виконання першої команди переадресації вказів­ник 1 буде містити адресу тієї ж ділянки пам’яті, що й вказівник 2, тобто вони вказуватимуть на одне й те ж дане.         У результаті виконання другої команди присвоєння вказівник не вказуватиме на конкретне дане (він стає вільним - nil).         Після опрацювання динамічної змінної пам’ять можна вивіль­ни­ти за допомогою процеду

            dispose(<вказівник на динамічну змінну>) .

        Приклад. Розглянемо програму Vkazivnyky і її графічну ілюст­рацію на рис. 1

program Vkazivnyky; var c1, c2 : ^integer; begin new (c1); new (c2); c1^ := 5; c2^ := 7; writeln(c1^,c2^); c1 := c2; writeln(c1^,c2^); c2 := nil; dispose(c2); writeln(c1^); end.

{Оголошуємо два вказівники} {Резервуємо память для цілого числа} {Резервуємо память для цілого числа} {Змінна c1^ отримує значення 5} {Змінна c2^ отримує значення 7} {Виводимо 5 та 7} {Переадресація} {Виводимо 7 та 7} {Вказівник c2 занулюємо} {Память, надану для c2, вивільняємо} {Виводимо 7}

            Рис. 1. Графічна інтерпретація дій з вказівниками         Довідка. За допомогою динамічних змінних можна розв’язати за­да­чу почергового опрацювання одною програмою деякої кількості ве­ликих масивів (якщо усі масиви ввести в пам’ять одночасно немо­жливо). Задачу розв’язують так. Оголошують потрібну кіль­кість вка­зів­ників на масиви, на­прик­лад, var mas1, mas2,... : ^array... Створюють new(mas1) і опрацьовують динамічні змінні: mas1^[1], mas1^[2], ...,mas1^[i],... Вивільня­ють пам’ять: dispose(mas1). Ство­рюють і опрацьовують елементи другого масиву: mas2^[i] і т. ін.         2. Поняття про список. Розглянемо структуру даних — од­но­напра­влений (однозв’язний) список.         Список — це скінченна сукупність даних одного типу, між яки­ми налагоджено зв’язок. Елемент (однонаправленого) списку скла­да­ється з двох частин: самого даного (даних) та вказівника на на­ступний елемент списку. Для опису такої структури викори­сто­вують тип даних запис і тип даних вказівник таким чином:

type <назва елемента списку> = ^<запис>; <запис> = record <поле даного> : <тип даного>; <поле вказівника> : <назва елемента списку> end;

        Приклад. Розглянемо файл, у якому є дані про ріки і на­зве­мо його Riky.pas (див. задачу з § 10). Тип запису про річку на­зве­мо rika і поставимо йому у відповідність елемент списку такого типу:

type elspysku =^rika; rika = record nazva : string[11]; dov : integer; pl : longint; dali : elspysku; end; var element, pershyj, poperednij, novyj : elspysku;

        Тут element — вказівник на поточний елемент списку, element^ — динамічна змінна типу запис, element^.dov — динамічна змінна типу integer, яка набуває значення довжини річки, а element^.dali — вказівник на наступний елемент списку. Звідси випливає, що element^.dali^.dov — це довжина наступної річки, а element^. dali^. dali — вказівник на ще наступну річку і т. ін.         Задача. Є файл даних на диску і ще одне дане, яке треба до­­лу­­чити до файлу. На базі старого файлу створити новий файл з цим даним на початку.         Розглянемо один із способів розв’язування задачі. Дані слід ввес­ти з файлу в оперативну пам’ять, опрацювати і створити на носії інший файл. Під опрацюванням будемо розуміти вилучення даних чи записування додаткових даних тощо. Щоб ввести всі дані з файлу в оперативну пам’ять, хотілося б ви­ко­­ристати структуру даних — масив. Але кількість даних у фай­лі заздалегідь невідома, тому звичайний масив використати не мож­на. Навіть якщо кількість даних відома (або якщо використати ди­на­мічний масив, який ми тут не розглядаємо), то працювати з таким масивом нераціонально. Адже, щоб вста­ви­­ти в середину ма­си­­ву новий елемент, потрібно зміщувати «хвіст масиву». Отже, для розв’язування задачі потрібна інша структура даних. Така струк­тура даних є, і це є список.         Програма SpysokRik розв’язує поставлену задачу і демонструє основні прийоми опрацювання списку. Елементи списку опрацьо­вують один за одним за допомогою циклу. Спочатку за допомогою циклу створють список і вводять у нього дані з файлу. Специфіка циклу у цій програмі така: після його завершення буде створено зайвий (останній) елемент списку. Його слід ліквідувати, зазда­ле­гідь оголосивши ще один вказівник (на попередній елемент списку) і прийнявши poperednij^.dali:= nil. Суттєва перевага списків поля­гає саме в тому, що вилучити зафіксований (тобто вибраний згідно з деякою умовою) елемент можна за допомогою одної команди пере­адре­сації вигляду poperednij^.dali := zafix^.dali.         Виводимо список на екран. Створюємо новий елемент списку і вводимо в його поля дані, наприклад, так: Дніпро (робимо п’ять пропусків і натискаємо клавішу вводу) 2201 (натискаємо клавішу вводу) 504000 (натискаємо клавішу вводу).         Новий елемент зробимо першим у списку, тобто поставимо його перед першим елементом існуючого списку. Знову ж таки перевага списку над масивом відчутна: щоб вставити новий елемент після зафіксованого, потрібно лише дві команди переадресації і жодної «брудної роботи з хвостом». Вказівник нового елемента пере­адре­совуємо туди, куди показував вказівник зафіксованого елемен­та, а вказівник зафіксованого елемента «втикаємо» в новий елемент:             novyj^.dali := zafix^.dali;             zafix^.dali := novyj;         Знайдіть у програмі місце, де новий елемент записується у спи­сок перед першим. Так роблять зміни у списку, знову пере­глядають його на екрані і виводять у файл Riky2.pas. Пере­кона­й­теся, що файл створено правильно. Не виходячи з середовища, за­собами меню File та Open відкрийте файл Riky2.pas і перегляньте його.         Розгляньте рис. 2 і програму SpysokRik. Виконайте програму та поекспериментуйте з нею.             Рис 2. Графічна інтерпретація списку і дій з його елементами

program SpysokRik; uses Сrt; type    elspysku = ^rika;    rika = record    nazva : string[11];    dov : integer;    pl : longint;    dali : elspysku; end; var element, pershyj, poperednij, novyj : elspysku;    Myfile, Myfile2 : text; procedure StvorytySpysok(VAR Myfile : text); begin    new(element); pershyj := element;    while not eof(Myfile) do       begin       poperednij := element;       with element^ do readln (Myfile, nazva, dov, pl);       new (element^.dali);       element := element^.dali       end;    {Вилучаємо останнiй зайвий елемент}    poperednij^.dali := nil; end; procedure VyvestyNaEkran; begin    writeln(’Створено такий список:’); writeln;    element := pershyj;    while element <> nil do       begin       with element^ do       writeln(nazva:11, dov:8, pl:12);       element := element^.dali       end; end; procedure StvorytyNovyjElement; begin    new(novyj);    writeln; writeln (’Створюємо новий елемент’);    with novyj^ do       begin       write(’Введiть назву – 11 символiв: ’); readln(nazva);       write(’Введiть довжину рiчки: ’); readln(dov);       write(’Введiть площу басейну: ’); readln(pl)       end;    writeln end; procedure VyvestyuFile (var Myfile : text); begin    element := pershyj;    while element <> nil do       begin       with element^ do writeln(Myfile, nazva:11, dov:8, pl:12);       element := element^.dali       end;    writeln; writeln (’Список занесено у файл. Кiнець роботи’) end;    {Кінець розділу процедур} begin   {Основна програма}    clrscr;    assign (Myfile, ’riky.pas’);    assign (Myfile2, ’riky2.pas’);    reset (Myfile);    StvorytySpysok (Myfile);    VyvestyNaEkran;    StvorytyNovyjElement;    {Додаємо до списку новий елемент,    новий елемент робимо першим}    element := pershyj;    novyj^.dali := element;    pershyj := novyj;    VyvestyNaEkran;    rewrite (Myfile2);    VyvestyuFile (Myfile2);    close (Myfile);    close (Myfile2);    repeat until keypressed end.

        3. Ще раз про стек та чергу. Стек — це структура даних, у якій елемент, записаний останнім, зчитують (він є доступний до опрацюван­ня) першим. Принцип «останній прийшов — перший пішов» використовується в багатьох технічних пристроях і побуті: зга­дайте ріжок від автомата, посадку пасажирів у вагон з одними две­рима тощо. Стек використовують у програмуванні для реалізації рекурсії. Рекурсія виконується так: спочатку усі виклики нагромад­­жуються (аналогія така: пружина стискається), а потім викону­ються вкладені процедури чи функції (пружина розпрям­ляється).         Черга — це структура даних, у якій елемент, записаний пер­шим, зчитують першим. Тут діє принцип «перший прийшов — пер­ший пішов», добре відомий з побуту: черга за квитками тощо. Максимально допустимі розміри стеку і черги — важливі хара­к­теристи­ки реалізації мови програмування, які визначають коло задач, які можна розв’язати. Стеки та черги описують і створюють у пам’яті за допомогою списків. Відповідні описи і приклади процедур їх опрацювання можна відшукати в довід­никах.         Завдання. Розв’яжіть задачу № 21, застосувавши список для встав­ляння у файл нового елемента після елемента з номером i mod 4 + 1.