Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

osn_progr_final

.pdf
Скачиваний:
36
Добавлен:
12.02.2016
Размер:
3.27 Mб
Скачать

set(key, s); continue;

}

if((val = get( key )) == NULL) /* КЛЮЧ :знайти зна-

чення */

printf( "немає такого ключа\n");

else{ printf( "значення "); printf(VALFMT, val- >val);

putchar('\n');

}

}

/* роздруковка таблиці за допомогою ітератора */ for( cl = resetiter(&ci) ; cl ; cl = nextpair(&ci))

printcell(cl), putchar('\n');

}

Приклад 2 Складені описувачі Напишемо програму, яка:

а) підраховує кількість всіх допустимих в мові С складених описувачів заданої довжини б) генерує довільний складений описувач заданої довжини

в) друкує всі допустимі складені описувачі заданої довжини При цьому у допустимих описувачах в цій задачі будемо враховувати

лише тривіальні ознаки масивів та функцій-дужки [ ], ( ). Розглянути поняття довжини як кількість кроків інтерпретації.

Якщо позначити через V-ознаку вказівника, М-масива і F-функції, то з точки зору правил розшифровки довільна послідовність виду FFVVMFVV задає схему утворення описувача (інтерпретуємо її зліва направо). Причому описувач буде допустимим, якщо в цій послідовності немає комбінацій виду FM (масив функцій), FF (функція повертає функцію), MF. Схему утворення допустимого складеного опису-

вача можемо зобразити так:

 

V- вказівник

M-масив

F-функція

V-V

M-V

F-V

V-M

M-M

 

V-F

 

 

Бачимо, що якщо останньою була ознака вказівника, то вона може “утворити” в подальшому усі три ознаки, масиву - дві і, нарешті, ознака функції може утворити лише одну ознаку вказівника.

Тоді для підрахунку кількості всіх можливих описувачів можемо використати лише три змінних v, m, f типу long, які б зберігали в

121

собі на кожному кроці кількість описувачів, у яких останньою при інтерпретації була б відповідно ознака вказівника, масиву та функції. При цьому неважко помітити, що ці кількості за 1 крок зростають за правилом:

v1=v+m+f;

m1=m+v;

f1=v;

Оформимо відповідний підрахунок у вигляді функції:

long kilkist(int n)

{

long v=1, m=1, f=1, v1,m1,f1; int i;

for(i=1;i<n;i++)

{

v1=v+m+f;

m1=m+v;

f1=v;

v=v1;

m=m1;

f=f1;

}

return (v+m+f);

}

Тоді main-функція має вигляд:

#include<stdio.h> void main(void)

{

int n;

printf(“введіть довжину процесу інтерпретації описувача n>=1\n”);

scanf(“%d”,&n);

printf(“шукана кількість: %ld\n”,kilkist(n));

}

Нескладний аналіз показує, що код, записаний в тілі циклу for в функції kilkist() може бути спрощений і записаний без використання допоміжних змінних v1, m1 ,f1.

Тоді функція має вигляд: long kilkist(int n)

122

{

long v=1, m=1, f=1, v1; int i; for(i=1;i<n;i++)

{

v+=m+f; f=v-m-f; m+=f;

}

return (v+m+f);

}

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

Для цього, очевидно нам необхідно мати :

функцію, що генерує схеми

функцію-інтерпретатор, що будує конкретний описувач по заданій схемі

функцію, що формує список.

Враховуючи згадану вище схему, можемо вибрати різні структури даних. Наприклад, добре підходять в даному прикладі тернарні дерева. Але ми зупинимось на однозв”язному списку, одночасно демонструючи технологію роботи з списками.

Виберемо наступну структуру:

struct inter

{

struct inter *next; // вказівник на наступний елемент char *s; // поле, де зберігається інтерпретаційна схема char ost;// останній елемент поля s

};

Якщо поле ost відповідного елемента в списку містить ознаку вказівника, то утворюємо два нових екземпляра структури inter і записуємо їх в список зразу після відповідного елемента, модифікуючи і його поле s. Аналогічно, якщо ознака масиву–один елемент, функ- ції–лише модифікуємо біжучий.

#include<stdio.h>

123

#include<alloc.h>

#include<conio.h>

#include<string.h> struct inter

{

struct inter *next; // вказівник на наступний елемент char *s; // поле, де зберігається інтерпретаційна схема char ost;// останній елемент поля s

};

/*

Параметр функції flag набуває два значення: 0 та 1. Якщо він рівний 0, то функція модифікує саму структуру, що передається через вказівник p, дописуючи символ x як останній символ поля s, не створюючи нової при цьому нової структури в пам”яті. Якщо ж flag рівний 1, то утворюється нова структура, у якої поле s є конкатенацією поля s структури, що передається через вказівник p та символа x.

*/

struct inter* vstavka(char x, struct inter *p,int flag)

{

if(p==NULL)/*Якщо p==NULL проводиться ініціалізація полів структури*/

{

p=(struct inter*)malloc(sizeof(struct inter)); p->s=(char*)malloc(sizeof(char)+1); p->s[0]=x;

p->s[1]='\0'; p->ost=x; p->next=NULL; return p;

}

else/*інакше-проводимо аналіз прапорця*/

{

int size=strlen(p->s); if(flag!=0)

{struct inter* p1=(struct inter*)malloc(sizeof(struct inter));

p1->s=(char*)malloc(size+2); strcpy(p1->s,p->s); p1->s[size]=x; p1->s[size+1]='\0';

124

p1->ost=x;

p1->next=NULL; return p1;

}

else

{

char *srob=(char*)malloc(size+2); strcpy(srob,p->s);

srob[size]=x;

srob[size+1]='\0'; strcpy(p->s,srob); p->ost=x;

return p;

}

}

}

/* Функція-інтерпретатор */

void interpret(char *s, char ident)

{

int i,j,sv=0,sv1=0,slen=strlen(s),identlen=1; for(i=0;i<strlen(s);i++)

{

if (s[i]=='v' && s[i+1]!='v') sv++; if (s[i]=='v') sv1++;

}

char *sinterp= (char*)malloc((slen-sv1)*2 +identlen+sv1+sv*2);

/* sinterp - масив, де зберігається проінтерпретований варіант */

int sost=identlen-1;

/*індекс останнього проініціалізованого елемента масиву*/

sinterp[0]=ident;

sinterp[1]='\0'; for(i=0; i<slen; i++)

{

switch(s[i])

{

case 'v':

/*все беремо в дужки і ставимо зліва зірочку*/ if( s[i+1]!='v'&& i<slen-1)

125

{

for(j=sost; j>=0; j--) sinterp[j+2]=sinterp[j]; sinterp[0]='('; sinterp[1]='*'; sinterp[sost+3]=')'; sost+=3;

}

else

{

for(j=sost; j>=0; j--) sinterp[j+1]=sinterp[j]; sinterp[0]='*'; sost+=1;

}

break; case 'm':

sinterp[sost+1]='[';

sinterp[sost+2]=']';

sost+=2;

break; case 'f':

sinterp[sost+1]='(';

sinterp[sost+2]=')';

sost+=2;

break;

default: printf("вхідний масив невірний");

}

}

for( i=0;i<=sost;i++) printf("%c",sinterp[i]); printf("\n");

//char c=getch(); free(sinterp);

}

При розробці функції інтерпретації врахуємо, що при інтерпретації ознаки вказівника отриману конструкцію необхідно взяти в дужки (інакше порядок інтерпретації буде іншим).

126

/* функція, що формує список. Значення полів s– інтерпретаційні послідовності */

struct inter* form(int n)

{

struct inter* spysok,*current; int i; spysok=vstavka('v',NULL,0); current=spysok; current->next=vstavka('m',NULL,0);

current->next->next=vstavka('f',NULL,0);

for(i=1;i<n;i++)

{

current=spysok;

while(current!=NULL)

{

//printf("%s\n",current->s); //char c=getch();

switch(current->ost)

{

struct inter* currob; case 'v':

currob=vstavka('m',current,1); currob->next=vstavka('f',current,1); current=vstavka('v',current,0); currob->next->next=current->next; current->next=currob; current=currob->next->next;

break; case 'm':

currob=vstavka('m',current,1);

current=vstavka('v',current,0); currob->next=current->next; current->next=currob; current=currob->next;

break; case 'f':

127

current=vstavka('v',current,0); current=current->next;

break;

default:

printf("main список ініціалізований неправильно\n");

}

}

}

return spysok;

}

main()

{

struct inter *spysok,*current; int n;

char c;

printf("введіть n\n"); scanf("%d",&n); spysok=form(n); current=spysok; while(current!=NULL)

{

printf("%s\n",current->s); interpret(current->s,'a'); printf("\n");

c=getch(); current=current->next;

}

free(spysok);

}

128

6 ПРЕДСТАВЛЕННЯ ЧИСЕЛ У КОМП'ЮТЕРІ.

Ми вже говорили, що програми в ході свого виконання здійснюють зміни стану інформаційного середовища чи, що те ж саме, обробляють дані. Тому велике значення має питання про представлення інформації всередині комп'ютера. Грамотно використовувати його можливості, не знаючи способу представлення, складно і це може призвести до появи серйозних помилок.

Як приклад цього, розглянемо наступну програму:

#include <iostream.h> int main(void)

{

float h,x; int i,m;

m =512;

for(i = m - 1; i<= m + 1;i++)

{

h =1./i; x =0.;

for(int j= 1;j<= i;j++) x =x + h;

printf(" x=%10.8f\n",x);

}

return 0;

}

Неважко бачити, що програма для трьох сусідніх значень підсумовує i раз величину 1/i. Здавалося б, кінцевий результат її роботи, рівний і*(1/і) повинен дорівнювати одиниці. Перевіримо. Запустивши програму, побачимо на екрані:

5110.99999666

5121.00000000

5130.99999666

Бачимо, що точне значення відповідає лише значенню i==512. Тому необхідно розібратися з представленням чисел «усереди-

ні» комп'ютера.

6.1 СИСТЕМИ ЧИСЛЕННЯ

Числа, якими ми звикли користуватися, називаються десятковими і тому арифметика з ними також називається десятковою. Кожне

129

число можна скласти з набору цифр, що містить 10 символів - цифр . Якщо припустити, що алфавітом є множина {0,1,2,3,4,5,6,7,8,9}, а число – це слово, то зрозуміло, що назва десяткове походить від кількості букв алфавіту.

Візьмемо, наприклад, число 358. Цей запис означає, що в числі три сотні, п'ять десятків і вісім одиниць. Отже, можна записати на-

ступну рівність:

358 = 300 + 50 + 8 = 3 * 102 + 5 * 101 + 8 * 100

Відзначимо, що цифри, які утворять наше число, множаться на послідовні степені числа 10. Як видно, цифри збільшуються на десять у степені на одиницю меншу порядкового номера цифри при нумерації справаналіво.

Таким чином, усяке написане нами число може бути представлене у вигляді спеціального розкладу по степенях числа 10 і для запису використовується набір з десяти цифр.

Позначимо i-у цифру числа через ai. Тоді число можна записати в наступному вигляді: anan-1…...a2a1. Такий запис числа можна пред-

ставити так:

anan-1…...a2a1 = an *10n-1 + an-1 * 10n-2 + …... + a2 * 101 + a1 * 100

де ai - це символ з алфавіту {0,1,2,3,4,5,6,7,8,9}.

Як бачимо, число десять є основою відповідного представлення чисел. Воно називається основою системи числення, а сама система числення називається десятковою.

Під системою числення розуміється спосіб представлення будьякого числа за допомогою деякого алфавіту символів, що називаються цифрами.

Наочність представлення чисел і порівняльна простота виконання арифметичних операцій характерні для позиційних систем числення.

Система числення називається позиційною, якщо одна і та сама цифра має різне значення в залежності від того, яку позицію вона займає в послідовності цифр, що зображують число. Причому, це значення міняється в залежності від позиції однозначно, за деяким законом. Як приклад непозиційної системи числення - римська система представлення чисел.

Кількість p символів алфавіту, що використовуються у позиційній системі числення, називається її основою.

У загальному випадку в системі з основою p будь-яке число X може бути представлене у вигляді полінома з основою p :

130

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]