Отправляет email-рассылки с помощью сервиса Sendsay

Pascal с нуля by [CPM]

  Все выпуски  

Pascal с нуля by [CPM] Выпуск №14


Информационный Канал Subscribe.Ru

[Ø] Рассылка "Pascal с нуля" by [CPM]. Выпуск №14 [Ø]

::[Ø]BEGIN[Ø]::

Автор: v()v@#[CPM] mailto: vovanc@bigmir.net

Intro

Доброе время суток, уважаемые подписчики!!

После долгого молчания хочу с радостью сообщить вам, что рассылка живёт, что выпуски и
дальше будут выходить.

Очень трудно было вспоминать после месяца отдыха Паскаль. Пришлось поднимать исходники,
читать статьи, долго и упорно вспоминать, напрягать мозги, чтоб обрести прежние
навыки программирования. Теперь я в форме и могу не только писать проги, но и вести
рассылку!!!! Осталось только собрать всех остальных, т.е. MedL'a, Ustas'a,
но думаю, что этот выпуск сделаю с Ustas'ом. Куда без дизайнера? Вам ведь не хочется
получить текстовую версию рассылки?

Итак, начнём наш выпуск. Я думаю, что не стоит повторять то, что мы уже учили, ведь у вас
есть прошлые номера нашей рассылки, а если нет, то вы можете взять их тут:
http://subscribe.ru/archive/comp.soft.prog.pascalcpm
Этот выпуск будет посвящён работе с функциями, он будет больше, чем те, которые выпускались,
потому что данная тема очень обширна и плюс ко всему мы ещё не рассмотрели все способы
передачи параметров. Для вас это не будет сложно, если вы внимательно рассматривали работу
процедур, о которой мы рассказывали в прошлом выпуске. Если вы недостаточно хорошо поняли
изложенное в прошлом выпуске, то я советую вам повторно перечитать его. Этот выпуск тоже
очень важен. Не освоить его - остаться на среднем уровне программирования, а то и ниже,
если вы относились к ранним выпускам без должного внимания, а ведь это далеко не весь
материал. Мы ещё не писали серьёзных программ, не изучали работу с файлами, графику,
создание собственных модулей, некоторые типы данных и многое другое.

Функции. Часть Первая. Теория

   "Образование не может заменить интеллект.
   Это неуловимое качество лишь отчасти может быть
   определено способностью решать загадки. Но
   заключается оно в способности творить новые загадки,
   отражающие то, о чём свидетельствуют чувства,
   выстраивая парадоксы определений".
   Фрэнк Герберт. Дом глав Дюны.

Определение.
Функция - это такая подпрограмма, результатом выполнения которой обязательно является
некоторое значение.
Функции и процедуры очень похожи. Единственное существенное различие - то, что
функция возвращает какое-либо значение. Кроме того, результат её выполнения связывается
с её именем. Другими словами: функция предполагает обязательную передачу информации в
программу. Она состоит из заголовка и тела ф-ии.

Рассмотрим заголовок ф-ии:
1) Заголовок начинается со служебного слова Function
2) После него идёт список передаваемых параметров в круглых скобках
(может отсутствовать)
3) Далее следует тип возвращаемого значения.

Общий вид описания заголовка функции:

Function [NAME] ([PARAMETRS]):[TYPE];

После заголовка идёт тело ф-ии. Оно может содержать свой блок объявления констант,
переменных, типов, после следует само тело функции.

Общий вид функции(скелет), включая заголовок:

Function [NAME] ([PARAMETRS]):[TYPE];
type
[TYPES]
var
[VARs]
const
[CONSTS]
begin
[OPERATORS]
end;

Сразу хочется заметить, что среди всех операторов в теле функции должен быть оператор,
в котором имени функции присваивается значение для возврата.

Вызовы функции могут быть использованы в любых выражениях, как элементы вывода
в операторе вывода, как параметры в подпрограмме(процедуре, функции), хотя она скорее
похожа на переменную, значение которой зависит от передаваемых параметров, доступная только
для чтения; функции должны также стоять справа от знака присваивания. В этом её отличие
от процедуры, вызов которой всегда представляет собой самостоятельный оператор.

Но ведь правила существуют для того, чтобы их нарушать! Начиная с Турбо Паскаля версии 6.0
в его состав добавлена директива $X+, которая включает расширенный синтаксис. Она
позволяет использовать функцию как процедуру, то есть игнорируется возвращаемое значение и
её вызов может представлять самостоятельный оператор. Также это директива включает
поддержку строк, завершающихся нулём, которые могут быть длиной до 65535 символов!!!
Подробней почитайте в Help'е. На этом, я думаю, не стоит останавливаться, это не сложно.

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

Функции. Часть Вторая. Практика

   "Многое из того, что мы делаем естественно,
   становится сложным только тогда, когда мы
   превращаем это в предмет приложения нашего
   интеллекта. Возможно знать о предмете 
   столько, что станешь совершенно невежественным".
   Фрэнк Герберт. Дом глав Дюны.

Я буду писать примеры с объяснениями в том порядке, в котором они изложены в теории:
заголовок функции, тело.....

Заголовок.

Function CPM(var x:integer; y:word): boolean;

Функция, имя которой CPM, в которую передаются 2 параметра: один передаётся по ссылке,
второй - по значению. Возвращает эта функция результат типа Boolean (true, false).

Теперь самостоятельно разберите эти две функции, вернее их заголовки:
1) function CMPStrings(var str1,str2:string): boolean;
2) function GetHour: byte;

Общий вид функции, простейшая функция.

function summa(first,second:integer): longint;
var
result: longint;
begin
result:= inc(first,second);
summa:= result; {!!!!!!!!!!!!!!!!!!!!!!!!}
end;

Главное в программировании - использование интуитивно понятных имён функции, процедур,
переменных.... В нашем случае уже из названия видно, что речь пойдёт о сложении
чего-то с чем-то. Если мы посмотрим на список передаваемых параметров, то сможем
увидеть, что в функцию передаётся два параметра типа integer, а возвращаемое значение -
longint. Дальше мы видим блок объявления переменных (создан для наглядности). Также могут
присутствовать блоки объявления типов и констант. Дальше в этой простейшей функции ищется
сумма первого и второго параметра, а результат помещается в локальную переменную типа
longint. После этого в переменную с именем функции заносится значение суммы, что является
возвращаемым значением (помечено восклицательными знаками). Строки
result:= inc(first,second);
summa:= result;
можно заменить одной
summa:=inc(first,second);

В функции обязательно должен быть оператор присваивания, при чём слева от знака
присваивания должно идти имя функции, а слева - значение или выражение (типы должны
совпадать или должна выполнятся операция приведения типов).

Несколько примеров использования функций, сравнение процедуры с функцией.

Как же использовать функции? - Очень просто!

0) Создаём функцию


function minimum(x,y:integer): integer;
begin
if x>y then minimum:=y else minimum:=x;
end;

1) Используем
а) как значение, присваиваемое переменной (в нашем случае переменная должна быть типа
integer.
x_int:= minimum(5,2);
переменной x_int будет присвоено значение 2.
б) в любых выражениях
x_int:=5+3*minimum(x1_int,x2_int);
в) как элемент вывода в операторе вывода
write(minimum(x1_int,x2_int));
на экран будет выведено значение одной из переданных переменных
г) как параметр, передаваемый в функцию или процедуру.
x1_int:=minimum(1,2);
x2_int:=minimum(3,4);
x_int:= minimum(x1_int,x2_int);
тут мы с помощью функции нахождения минимального из двух чисел нашли минимальное из 4.
Это можно было сделать проще:
x_int:= minimum(minimum(1,2),minimum(3,4));

С использованием функций разобрались, но если есть вопросы - пишите, мой мейл ниже.

Для сравнения функции с процедурой напишем две программы, которые должны выполнять одно
действие - выводить максимум из двух чисел. Одна должна делать это с помощью функции, а
вторая - процедуры.

0)

uses crt;
var
x,y:real;
{переменные для ввода}
function maximum(a,b:real):real;
begin
if a>b then maximum:=a else maximum:=b;
end;
{Функция}
begin
clrscr;
{традиционное начало}
WriteLn('Введите два числа');
ReadLn(x,y);
{получить два числа}
WriteLn(maximum(x,y));
{вывести максимальное, используя функцию, как элемент вывода}
readkey;
end.

1)

uses crt;
var
x,y:real;]
{переменные для ввода}
procedure maximum(a,b:real);
begin
if a>b then WriteLn(a) else WriteLn(b);
end;
{процедура}
begin
clrscr;
WriteLn('Введите два числа');
ReadLn(x,y);
{получить два числа}
maximum(x,y);
{вывести максимальное}
readkey;
end.

Что всё-таки лучше использовать?
Как по мне следует оформлять подпрограмму как функцию стоит в том случае, когда от её
работы ожидается какой-либо результат или вы собираетесь использовать её в выражениях.
Если же подпрограмм должна просто выполнять последовательность действий, то лучше
оформить её в виде процедуры. Если же вам нужны функция и процедура со схожими
предназначениями, например, вам надо процедура, которая сортирует массив и функция,
которая сортирует массив и возвращает минимальное значение - можно использовать
функцию, как процедуру, заюзав директиву $X+.
Рассмотрим пример её использования:

{$X+}
{Включаем расширенный синтаксис Паскаля (6.0 и выше) соответствующей директивой}
uses crt;
var
x,y:real;
{переменные для ввода}
function maximum(a,b:real):real;
begin
if a>b then begin
WriteLn(a);
maximum:=a;
end
else begin
WriteLn(b);
maximum:=b;
end;
end;
{функция выводит максимум и возвращает максимум}
begin
clrscr;
WriteLn('Введите два числа');
ReadLn(x,y);
{получить два числа}
maximum(x,y);
{юзаем как процедуру - выведет минимум из двух введённых}
WriteLn;
WriteLn;
{для наглядности}
x:=maximum(10,15);
{юзаем как функцию - на экран выведется 15}
write(x);
{проверяем, а действительно ли всё в порядке (в переменной х должно быть 15)}
readkey;
end.

Для выхода из подпрограммы в произвольном месте используёте команду EXIT

Procedure WasteTime;
Begin
Repeat
If KeyPressed Then Exit;
Write('Xx');
Until False;
End;
Begin
WasteTime;
writeln('Нагло содрано с HELPa');
End.

Функции и процедуры. Передача параметров(продолжение)

   "Готовность понять - чаще всего просто
   рефлекторная реакция и наиболее опасная
   форма понимания. Она - теневой экран на 
   вашей способности учиться. Некоторые законы
   функционируют именно так,загоняя вас в тупик.
   Будте осторожны. Ничего не понимайте.
   Всё понимание временно".
   Фрэнк Герберт. Дом глав Дюны.


В прошлой статье я рассказывал про передачу параметров в процедуры. Я рассматривал передачу
массива, передачу по значению, передачу по ссылке. Как упоминалось выше -
способы передачи параметров в процедуры и функции одинаковы. Но мы ещё упустили несколько
способов передачи.

Бестиповые параметры-переменные.

При данном способе передачи параметров передаваемым(фактическим) параметром может быть
любая переменная или константа произвольного типа. Нельзя передавать выражение,
так как при данном способе передача параметров происходит по ссылке, то есть в
подпрограмму передаётся не значение переменной(константы), а её адрес в памяти.

Приведём пример описание формального бестипового параметра.

procedure Some(var some; x:integer);

В данном случае some - имя формального бестипового параметра.
Очень важно иметь понятие о том - что такое формальный, а что такое фактический параметр.
Если вы до сих пор этого не поняли - перечитайте ещё раз то, что написано выше про
бестиповые параметры.
Объединяя всё вышесказанное в одно очень дурное определение, получаем что-то типа этого:
Если вы объявили формальный бестиповый параметр, соответствующий ему фактический параметр
может быть ссылкой на переменную или типизированную константу произвольного типа.

Было бы всё хорошо, если бы мы знали с параметром какого типа работаем в подпрограмме,
но там мы имеем бестиповый параметр - он не совместим ни с какой переменной какого-либо
типа, это означает, что мы не можем её использовать ни в каких конструкциях или выражениях.
Есть два типа преодоления этой проблемы:

0) Использовать операцию приведения типов

Function cmp_val(var v1,v2; size:word):boolean;
{функция сравнивает значения двух переменных любого типа}
type
bytes = array [1..65000] of byte;
{задаём тип (будет использоваться в операциях приведения типов), размер элемента - byte,
так как будем сравнивать побайтно}
var
i:word;
{переменная-счётчик (для цикла)}
begin
cmp_val:=true;
{пока что у нас всё совпадает, но если мы найдём хоть один байт первой переменной,
который не совпадает с соответствующим ему байтом второй переменной, это строка превратится
в такую: cmp_val:=false;}
for i:=1 to size do
if bytes(v1)[i] <> bytes(v2)[i] then begin
cmp_val:=false;
exit;
end;
{побайтно сравниваем переменные, используя операцию приведения типов. Фактически мы
сравниваем два массива array [1..65000] of byte, но сравниваем только size элементов.
Если хоть не совпадают, то возвращаем ЛОЖЬ и выходим, юзая EXIT}
end;
var
s1,s2,s3:string;
x,y:word;
{переменные значения которых будем сравнивать}
begin
x:=9999;
y:=9999;
s1:='CPM forever';
s2:='CPM rulez';
s3:='CPM rulez';
{начальные значения для сравнения}
writeln;
{для красоты}
writeln(cmp_val(s1,s2,length(s1)));
{сравниваем первые две строки. Функция length возвращает длину строки, имя которой
указано в скобках. На экране должна появиться строка 'FALSE'}
writeln(cmp_val(s3,s2,length(s2)));
{сравниваем две одинаковых строки. На экране - 'TRUE'}
writeln(cmp_val(s1[1],s2[1],4));
{Сравниваем первые 4 символа первой и второй строк: 'CPM ' и 'CPM '. На экране 'TRUE'}
writeln(cmp_val(s1[5],s2[2],4));
{Сравниваем символы с 5-ого по 9-ый первой строки и со 2-ого по 6-ой второй строки.
На экране 'FALSE'}
writeln(cmp_val(x,y,sizeof(word)));
{Две переменные типа word. Функция sizeof возвращает размер (в байтах) типа в скобках.
На экране 'TRUE'}
readln;
end.

1) Описать локальную переменную конкретного типа и связать её в памяти с бестиповым
параметром.
Я могу просто скопировать предыдущую программу, немного скорректировать её и получить
программу, иллюстрирующую данной способ обхода вышеописанной проблемы, но я
хочу показать некий подводный камень, с которым вы можете столкнуться, юзая бестиповые
параметры и поэтому напишу здесь другую программу.

uses crt;
procedure copy_val(var v1,v2; size:word);
{на этот раз я решил сделать процедуру с бестиповым параметром. Эта процедура
копирует содержимое одной переменной произвольного типа в другую.}
type
bytes = array [1..65000] of byte;
{опишем тип. просто так}
var
_v1:bytes absolute v1;
_v2:bytes absolute v2;
{два локальных байтовых массива, которые содержат в себе передаваемые переменные, разбитые
на байты}
i:word;
{переменная-счётчик}
begin
for i:=1 to size do
_v2[i] := _v1[i];
{у нас два массива. копирование одного в другой мы уже проходили}
end;
var
s1:string[11];
{переменная для отловки подводного камня}
s2,s3:string;
{ещё две для того, чтоб показать принцип работы}
begin
clrscr;
s1:='CPM forever';
s2:='CPM rulez forever';
s3:='CPM rulez';
{начальные значения}
writeln;
copy_val(s1,s3,length(s1)+1);
writeln('s1=',s1);
writeln('s2=',s2);
writeln('s3=',s3);
writeln;
{копируем значение первой строки в третью, результат на экране. всё в порядке}
copy_val(s2,s3,length(s2)+1);
writeln('s1=',s1);
writeln('s2=',s2);
writeln('s3=',s3);
writeln;
{копируем значение второй строки в третью, результат на экране. всё в порядке}
copy_val(s2,s1,length(s2)+1);
writeln('s1=',s1);
writeln('s2=',s2);
writeln('s3=',s3);
{копируем значение второй строки в первую и видим, что нас ждёт огромный облом.
вторая строка искажена. это связано с тем, что вторая строка в сегменте данных
расположена сразу после первой. Под первую зарезервировано 11 байт, а под вторую 255.
Когда мы пытаемся копировать 17 байт второй строки в 11 байт первой, то мы вылазим
за предел первой и записываем оставшиеся 6 байт с s2[0] по s2[5], что приводит к искажению
второй строки, которая становится вида 'reverulez forever', а после этой надписи идёт ещё
94 пробела. Дело в том, что в s2[0] хранится кол-во букв в строке s2, но при перезаписи
туда попадает код буквы 'o', который равен 111, после этого вторая строка выводится
не как строка из 17 букв, а как строка из 111 букв}
readkey;
end.

Данный способ передачи параметров даёт вам практически полную свободу действий при передаче
параметров, но, как видите тут есть и свои "подводные камни", которые нужно уметь обходить.

Для того, чтобы начать изучения следующего типа передачи параметров нам надо определиться с
понятием процедурного типа.

Процедурный тип. Переменные процедурного типа.

Turbo Pascal позволяет рассматривать подпрограммы как объекты, что даёт возможность
присвоить таковую переменной или передать в качестве параметра подпрограмме. Для
того, чтоб заюзать эту возможность есть такая штуковина, как процедурный тип. Как
и все остальные типы, процедурный тип объявляется в разделе объявления типов (type).
Рассмотрим общий вид описания процедурного типа.

type
[NAME] = procedure([PAR]);
{тип для процедуры}
[NAME] = function([PAR]):[TYPE];
{для функции}

Описание процедурного типа очень похоже на описание заголовка подпрограммы.
Рассмотрим несколько примеров описания процедурного типа.

type
MyFirst = function(x,y:integer):longint;
procCPM = procedure(var v1,v2; s:integer);
NULPar = procedure;
OBANA = procedure(var x,y:byte; summa:MyFirst);

Имена параметров не имеют в дальнейшем никакого значения. Значение имеют только типы.
Когда мы определили процедурный тип, мы можем определять переменные данного типа.
Несколько примеров:

var
s: MyFirst;
OPA: OBANA;

Переменным процедурного типа, как и переменным любого другого типа можно присвоить
какое - либо значение. Это может быть идентификатор (имя) подпрограммы или переменная
такого же процедурного типа.
Несколько примеров:

s:=summa;
OPA:=TADA;

После таких действий мы можем сделать вызов функции summa так:
s(x,y);
или процедуры TADA так:
OPA(a,b,s);

Как я уже говорил - имена параметров не имеют значения, но как и в случае с любыми
переменными важное значение играют типы. Чтоб присвоить процедурной переменной значение
другой процедурной переменной или идентификатора нужно:
0) процедурные типы должны иметь одинаковое количество параметров
1) параметры в соответствующих позициях должны быть одного типа
10b) для функций - возвращаемые значения должны быть одного типа
11b) подпрограмма должна быть объявлена как far или откомпилирована с директивой $F+
100b) не должна быть стандартной процедурой или функцией
101b) не должна быть вложенной interrupt или inline (об этом ниже) подпрограммой

Переменная процедурного типа хранит адрес подпрограммы в памяти и занимает 4 байта - 2
под сегмент, 2 под смещение.

Теперь перейдём непосредственно к параметрам процедурного типа.

Как передать параметр процедурного типа?
0ffh) Создать процедурный тип
0feh) Создать подпрограмму под этот тип или переменную
0fdh) Создать подпрограмму, в которую надо передать параметр данного процедурного типа
0fch) Передать идентификатор или переменную процедурного типа как параметр в подпрограмму.

Напишем программу, которая проиллюстрирует вышесказанное:

uses crt;
const
n=10;
{число элементов в массиве}
type
ArrCPM = array[1..n] of integer;
{тип под массив, чтоб можно было передавать в процедуры}
ProcCPM = procedure(x,y:word; var v:ArrCPM);
{процедурный тип}
{$F+}
{директива, которая делает подпрограммы "дальней" - чтоб использовать эти процедуры как
параметры}
Procedure min(a,b:word; var g:ArrCPM);
var
temp:integer;
begin
if g[a]>g[b] then begin
temp:=g[b];
g[b]:=g[a];
g[a]:=temp;
end;
end;
Procedure max(a,b:word; var g:ArrCPM);
var
temp:integer;
begin
if g[a]<g[b] then begin
temp:=g[b];
g[b]:=g[a];
g[a]:=temp;
end;
end;
{Процедуры, которые передаем в процедуру сортировки. Первая переставляет в начало массива
минимальный элемент, а вторая - максимальный}
{$F-}
{дальше нам не нужна эта директива}
procedure sort(var a:ArrCPM; f:ProcCPM);
{процедура сортировки. сортирует по убыванию или возрастанию в зависимости от
передаваемой процедуры}
var
i:word;
j:word;
{переменные под счётчик}
begin
for i:=1 to n-1 do
for j:=i to n do
f(i,j,a);
{в алгоритме сортировки тут должна быть проверка и перемена мест элементов,
которая у нас забита в соответствующие процедуры. Что и определяет порядок сортировки}
end;
{подпрограммы закончились. Ниже - тело программы}
var
somearr:ArrCPM;
{массив}
i:word;
{под счётчик}
begin
randomize;
{генератор}
clrscr;
{чистим}
for i:=1 to n do somearr[i]:=random(2000)-999;
{заполняем массив}
for i:=1 to n do write( somearr[i]:5);
{выводим на экран}
writeln;
sort(somearr,min);
{сортируем по возрастанию}
for i:=1 to n do write( somearr[i]:5);
{выводим на экран}
sort(somearr,max);
{Сортируем по убыванию}
writeln;
for i:=1 to n do write( somearr[i]:5);
{выводим}
readkey;
end.
{Конец}

Я советую вам поэкспериментировать со всеми типами передачи параметров подпрограммам,
это даёт вам очень большие возможности в программировании, существенно облегчая процесс.
Если вы думаете, что это конец выпуска - вы ошибаетесь. Мы не рассмотрели ещё одно -
объявления подпрограмм, а это ещё несколько килобайт текста. Как подумаю, что всё это
писать мне - хочется выпить кофе.


Объявление подпрограмм.

Общий вид описания подпрограммы с объявлением:
[заголовок]; [объявление];

Часть 1. NEAR and FAR.

Существует две модели вызова, которые поддерживает Язык Турбо Паскаля - near и far (ближняя и дальняя). У каждой из них свои преимущества и недостатки. Ближний вызов процедуры выполняется быстрее и занимает меньше места по сравнению с дальним. Дальний же вызов можно вызывать из любого модуля, в то время, когда ближние могут быть вызваны только из того модуля, в котором они объявлены. Процедуры, объявленные в программе имеют ближнюю модель вызова и могут использоваться только внутри программы.


В некоторых случаях использование дальней модели вызова просто необходимо, например, когда надо присвоить подпрограмму переменной процедурного типа. Для указания дальней модели вызова можно использовать директиву $F, которая в состоянии $F+ активирует дальнюю модель вызова, а в состоянии $F- включает режим, в котором Паскаль автоматически определяет (не всегда корректно) модель вызова. По умолчанию $F-.


{$F+}
[подпрограмма]
{$F-}

В этом случае активируется дальняя модель для вызова данной подпрограммы.


Для задания требуемой модели заголовок подпрограммы может содержать директивы near или far. Они перекрывают установку директивы $F и автоматический выбор модели компилятором.


Часть 2. Forward (опережающее объявление).


Если для подпрограммы включена эта директива, то её вызов может быть сделан в теле другой подпрограммы. Для того чтобы сделать опережающее объявление подпрограммы, надо:
0) описать заголовок подпрограммы с указанием директивы Forward.
1) в том же блоке программы, где описан этот заголовок (не обязательно сразу после него) создать подпрограмму с точно таким же заголовком.


После этих нехитрых действий можно делать вызов данной подпрограммы в теле другой подпрограммы. Ниже небольшой пример.


Function summa(x,y:integer):longint; Forward;

Function raznost(x, y: integer): longint;
Begin
raznost:=summa(x,-y);
End;

Function summa(x, y: integer): longint;
Begin
summa:=inc(x, y);
end;
....

Если появятся вопросы, присылайте их мне или кому-нибудь из команды на ящик. Все остальные объявления подпрограмм мы рассмотрим по мере надобности.


На этом заканчиваю этот выпуск. Думаю, что вам хватит материала на следующие 1-2 недели, пока я не сделаю новую.


Все вопросы присылайте на ящик.

::[Ø]END[Ø]::


Дизайнерское слово Ustasа

Автор: Ustas[CPM] mailto: Ustas1715@yandex.ru

 "All Days I Dream About  Pascal…."
     KoЯn

Приветствую Вас!
Возрадуйтесь, почитатели Великого Паскаля ибо скоро заработает наш ресурс на cpm232.nm.ru, где будут выложены все выпуски, а также исходники. И даже форум будет.


Наша команда [CPM]:

Дизайн, верстка и корректировка>>> Ustas[CPM]

Пишет >>> v()v@#[CPM]
ICQ:88880172

Тоже Пишет >>> MedL[CPM]ICQ:88883515

© 2004 Kiev Ukraine [CPM] Group 

http://subscribe.ru/
http://subscribe.ru/feedback/
Подписан адрес:
Код этой рассылки: comp.soft.prog.pascalcpm
Отписаться

В избранное