1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
| program Project1;
type
{Тип основных данных (значения десятичных разрядов).}
TData = 0..9;
{Тип указателя на элемент списка.}
TPElem = ^TElem;
{Тип элемента списка.}
TElem = record
Data : TData; {Основные данные.}
PNext : TPElem; {Указатель на следующий элемент списка.}
end;
{Тип, описывающий однонаправленный список.}
TDList = record
Cnt, Sign : Integer; {Количество разрядов и знак числа.}
PFirst, PLast : TPElem; {Указатели на первый и на последний элементы списка.}
end;
{
--------------------------------------------------
Подпрограммы для работы со списком.
--------------------------------------------------
}
{Начальная инициализация списка. Внимание! Эту процедуру можно выполнять
только в отношении пустого списка! Иначе - будут утечки памяти.}
procedure Init(var aList : TDList);
begin
with aList do begin
Cnt := 0;
Sign := 1;
PFirst := nil;
PLast := nil;
end;
end;
{Добавление элемента в конец однонаправленного списка. Функция возвращает
указатель на добавленный элемент.}
function Add(var aList : TDList; const aData : TData) : TPElem;
var
Res : TPElem;
begin
Inc(aList.Cnt);
New(Res);
Res^.Data := aData;
Res^.PNext := nil;
if aList.PFirst = nil then
aList.PFirst := Res
else
aList.PLast^.PNext := Res;
aList.PLast := Res;
Add := Res;
end;
{Удаление однонаправленного списка из памяти и инициализация.}
procedure Free(var aList : TDList);
var
PNext, PDel : TPElem;
begin
PNext := aList.PFirst;
while PNext <> nil do begin
PDel := PNext;
PNext := PNext^.PNext;
Dispose(PDel);
end;
Init(aList);
end;
{Составление списка из строки.
Цифры числа хранятся в списке так: вначале - младшие разряды, в конце - старшие.
Т. е., если у нас, например, имеется число: 12345, то в списке оно будет
храниться так: 5 - 4 - 3 - 2 - 1.}
function StrToList(const aStr : String; var aList : TDList) : Boolean;
const
D = ['0'..'9'];
var
i, Len, z : Integer;
begin
Free(aList);
Len := Length(aStr);
z := Ord('0');
for i := Len downto 1 do
if aStr[i] in D then
Add(aList, Ord(aStr[i]) - z)
else if i = 1 then begin
if aStr[i] = '-' then
aList.Sign := -1
else if aStr[i] <> '+' then
Free(aList);
end else begin
Free(aList);
Break;
end;
StrToList := aList.PFirst <> nil;
end;
{Список - в строку.
Цифры числа хранятся в списке так: вначале - младшие разряды, в конце - старшие.
Т. е., если у нас, например, имеется число: 12345, то в списке оно будет
храниться так: 5 - 4 - 3 - 2 - 1.}
function ListToStr(const aList : TDList) : String;
var
PElem : TPElem;
S, Res : String;
begin
if aList.PFirst = nil then begin
ListToStr := 'Число не задано';
Exit;
end;
Res := '';
PElem := aList.PFirst;
while PElem <> nil do begin
Str(PElem^.Data, S);
Res := S + Res;
PElem := PElem^.PNext;
end;
if aList.Sign < 0 then Res := '-' + Res;
ListToStr := Res;
end;
{
--------------------------------------------------
Подпрограммы для работы с длинной арифметикой.
--------------------------------------------------
}
{Сложение чисел, которые имеют одинаковые знаки.}
procedure Plus(const aL1, aL2 : TDList; var aRes : TDList);
const
Base = 10;
var
Res : TDList;
P1, P2 : TPElem;
Carry, Num : Integer;
begin
Free(aRes);
{Знак результирующего числа.}
aRes.Sign := aL1.Sign;
P1 := aL1.PFirst;
P2 := aL2.PFirst;
{Величина переноса в следующий старший разряд.}
Carry := 0;
{Вычисление продолжаем пока имеются разряды слагаемых чисел или имеется
НЕнулевое значение переноса.}
while (P1 <> nil) or (P2 <> nil) or (Carry <> 0) do begin
{Учитываем значение переноса.}
Num := Carry;
{Если имеется разряд числа aL1.}
if P1 <> nil then begin
{Добавляем значение разряда числа aL1.}
Num := Num + P1^.Data;
{Переход к следующему старшему разряду числа aL1.}
P1 := P1^.PNext;
end;
if P2 <> nil then begin
{Добавляем значение разряда числа aL2.}
Num := Num + P2^.Data;
{Переход к следующему старшему разряду числа aL2.}
P2 := P2^.PNext;
end;
{Вычислем величину переноса в следующий старший разряд.}
Carry := Num div Base;
{Вычисленный разряд добавляем в результирующее число.}
Add(aRes, Num mod Base);
end;
end;
{Вычитание. Знаки чисел aL1 и aL2 должны быть разными. Из числа aL1 вычитается
число aL2. При этом модуль числа aL1 должен быть не меньше модуля числа aL2.
Т. е., в отношении модулей должно выполняться уловие: |aL1| >= |aL2|.
aL1 - "уменьшаемое".
aL2 - "вычитаемое".
aRes - разность.}
procedure Minus(const aL1, aL2 : TDList; var aRes : TDList);
const
Base = 10;
var
P, P1, P2 : TPElem;
Carry, Num : Integer;
begin
Free(aRes);
{Результирующее число будет иметь тот же знак, что и знак уменьшаемого.}
aRes.Sign := aL1.Sign;
P1 := aL1.PFirst;
P2 := aL2.PFirst;
P := nil;
{Величина заёма из следующего старшего разряда.}
Carry := 0;
{Перебор всех разрядов aL1 и aL2.}
while P1 <> nil do begin
{Величина текущего разряда aL1 с учётом заёма.}
Num := Carry + P1^.Data;
{Переход к следующему старшему разряду aL1.}
P1 := P1^.PNext;
{Если в aL2 есть соответствующий разряд.}
if P2 <> nil then begin
{Выполняем вычитание значения разряда aL2.}
Num := Num - P2^.Data;
{Переход к следующему старшему разряду aL2.}
P2 := P2^.PNext;
end;
{Заём из следующего старшего разряда.}
Carry := 0;
{Если разность разрядов получилась отрицательной, то требуется заём из
следующего старшего разряда.}
if Num < 0 then begin
Num := Base + Num;
Carry := -1;
end;
{Добавляем вычисленный разряд в результирующее число.
При этом, если значение разряда не равно нулю, то запоминаем
указатель на него. Таким образом, после вычисления всех разрядов
результирующего числа мы будем иметь указатель на элемент, который
содержит самый старший значащий разряд. Это нам позволит в дальнейшем
удалить старшие нулевые незначащие разряды. Т. е., если в результате
вычислений мы получили число: "0000123", то указатель P будет указывать
на элемент, который содержит цифру "1". Зная этот указатель,
мы сможем удалить старшие незначащие разряды - т. е. мы удалим
ведущие нули "0000".}
if Num <> 0 then
P := Add(aRes, Num mod Base)
else
Add(aRes, Num mod Base);
end;
{Здесь мы удаляем из числа старшие незначащие разряды. Т. е., если мы получили
число "0000123", то незначащими старшими разрядами являются ведущие нули: "0000".
Вот их мы здесь и будем удалять (если они есть).}
if P <> aRes.PLast then begin
{Устанавливаем указатель на младший незначащий ноль.
Если P = nil, то число имеет вид: "00000". В этом случае нам понадобится
сохранить один ноль в записи числа. Поэтому, в этом случае, устанавливаем
указатель на первый элемент списка (это младший ноль).}
if P = nil then
P := aRes.PFirst;
{Исключаем из списка элементы, содержащие незначащие нули.}
aRes.PLast := P;
P := P^.PNext;
aRes.PLast^.PNext := nil;
{Удаляем из памяти элементы, содержащие незначащие нули.}
while P <> nil do begin
P1 := P;
P := P^.PNext;
Dispose(P1);
end;
end;
end;
{Сложение с учётом знаков.}
procedure Calc(const aL1, aL2 : TDList; var aRes : TDList);
var
P1, P2 : TPElem;
F : Boolean;
begin
{Если знаки чисел одинаковые, то выполняем процедуру сложения.}
if aL1.Sign = aL2.Sign then begin
Plus(aL1, aL2, aRes);
Exit;
end;
{Здесь мы оказываемся, если знаки чисел разные. В этом случае мы должны
применить процедуру вычитания. В процедуре вычитания модуль первого аргумента
должен быть не меньше модуля второго аргумента. Поэтому сначала
определим выполняется ли условие: |aL1| >= |aL2|.
Если F = True - значит, |aL1| >= |aL2|.
Если F = False - значит, |aL1| < |aL2|.}
if aL1.Cnt > aL2.Cnt then
{Если количество разрядов в aL1 больше, чем в aL2, то |aL1| > |aL2|.}
F := True
else if aL1.Cnt < aL2.Cnt then
{Если количество разрядов в aL1 меньше, чем в aL2, то |aL1| < |aL2|.}
F := False
else begin
{Если в aL1 и aL2 равное количество разрядов, то для выяснения соотношения
модулей требуется сравнить соответствующие разряды этих чисел.}
F := True;
P1 := aL1.PFirst;
P2 := aL2.PFirst;
{Сравниваем разряды чисел.}
while P1 <> nil do begin
if P1^.Data > P2^.Data then
F := True
else if P1^.Data < P2^.Data then
F := False;
{Переход к следующему старшему разряду.}
P1 := P1^.PNext;
P2 := P2^.PNext;
end;
end;
if F then
Minus(aL1, aL2, aRes)
else
Minus(aL2, aL1, aRes);
end;
var
L1, L2, L3 : TDList;
S : String;
F : Boolean;
begin
{Начальная инициализация списков.}
Init(L1);
Init(L2);
Init(L3);
repeat
repeat
Writeln('Задайте первое число:');
Readln(S);
F := StrToList(S, L1);
if not F then
Writeln('Неверный ввод. Повторите.');
until F;
repeat
Writeln('Задайте второе число:');
Readln(S);
F := StrToList(S, L2);
if not F then
Writeln('Неверный ввод. Повторите.');
until F;
Calc(L1, L2, L3);
Writeln('Сумма: ', ListToStr(L3));
{Удаление списков из памяти.}
Free(L1);
Free(L2);
Free(L3);
Writeln('Списки удалены из памяти.');
Writeln('Повторить - Enter. Выход - любой символ + Enter.');
Readln(S);
until S <> '';
end. |