Арифметика и математические функции



PASCAL ABC. Руководство для начинающих

Программирование на языке pascal abc на одноимённой среде

Васька Мырт

Октябрь 2017, Краснодар

Выжимка самого основного для самосистематизации знаний.

________________________________________________________________________

Раздел 1. Основы.

 

Технические моменты: установка среды PascalABC

Чтобы начать писать программы на языке pascal abc, нам нужно сначала установить соответствующую среду программирования. Среда программирования- это программа, в которой пишутся программы.

Мы будем для краткости говорить "паскаль", хотя это не совсем так- это лишь некий диалект паскаля с некими примесями и доработками. Сам изначально чистый язык программирования "паскаль" отличается от языка "паскаль абс" - но эти отличия очень маленькие и для новичка не имеют практически никакого значения.

Подробнее о pascal abc можно почитать на официальном сайте: pascalabc.net

Традиционно язык паскаль используют для обучения программированию школьников и иногда ещё даже студентов. Хотя эта технология уже устарела, она принадлежит больше нулевым годам. Но всё равно до сих пор на 2017 год данный язык остаётся основным для того чтобы знакомить школьников с азами программирования. Поэтому я и решил написать по нему пособие.

...

Чтобы скачать среду разработки, переходим по ссылке:

http://pascalabc.net/downloads/PascalABCNETWithDotNetSetup.exe

Устанавливаешь как обычную программу, оставляя всё по умолчанию. И далее запускаешь.

У тебя уже автоматически создаётся новый проект, т.е. ты можешь сразу приступить к написанию программы.

Чтобы сохранить проект, ты нажимаешь стандартно ctrl + s. Либо файл -> сохранить. Все файлы и программы по умолчанию сохраняются в папке PABCWork.NET на диске С.

Данная среда разработана нашими русскими людьми, поэтому тут есть справка на русском в самой программе. Нажимаешь помощь -> справка или просто F1.

Также в той же вкладке "помощь" есть ещё одна колонка: "изучаем pascal abc". Там есть уже готовые тестовые программы- ты можешь их открыть и посмотреть код. На основе этого кода ты поймёшь как та или иная вещь вообще работает.

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

Вообще pascal abc изучать достаточно легко, потому что тут есть понятная документация на русском языке, собранная в справке. Но в этом пособии мы всё же рассмотрим какие-то наиболее ключевые основы по шагам, потому что сразу так во всём этом разобраться человеку с улицы может быть тяжело. И данный текстовой труд будет служить неким проводником.

 

Вывод информации

Напишем же теперь первую простую программу, которая будет выводить на экран текст. В любой программе мы пишем 2 главных слова: начало и конец. Или по-английски begin и end. Между этими 2 словами будет находиться вся программа.

Писать большими буквами или маленькими- тут это не имеет никакого значения. Ты можешь писать как тебе нравится: BEGIN, Begin или даже BeGiN. После слова end мы ставим точку.

Зелёными отмечены комментарии- они никак не влияют на работу программы и нужны лишь для программиста- для пояснения тех или иных участков кода. Комментарии начинаются с двух возрастающих вправо слэшей: //

Для вывода информации используется 2 команды: write и writeln. Первая просто выводит, а вторая после вывода делает ещё и перевод на новую строку.

Например, слово привет мы можем вывести так: write('Привет');

Если мы хотим чтобы потом на новой строке мы вывели другой текст, нужно тогда написать writeln('Привет');

Для начала можешь по умолчанию для удобства использовать writeln, а потом далее ты поймёшь случаи когда нужно именно write.

...

Ниже будет пример программы, которая выводит 3 строки:

Чтобы запустить программу, нужно нажать на зелёную кнопочку наверху- при наведении всплывает подсказка "выполнить". Или просто F9.

Внизу в окне вывода ты можешь видеть результат:

Но это просто интерпретатор- он переводит твой код, написанный на паскале, в машинный код- который понимает именно машина- и выводит результат в рамках этой среды. Чтобы получить исполняемый exe файл, делают т.н. компиляцию. Чтобы скомпилировать программу, выбираем программа -> компилировать (Ctrl + F9). Или программа -> перекомпилировать все.

Можно также выполнить без связи с оболочкой (Shift + F9). Вылезет консольное окно:

Саму же скомпилированную программу Program1.exe ты найдёшь в папке PABCWork.NET на диске С.

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

Можно выводить не только текст, но и числа, например: write(143);

Точка запятой ставится в конце каждого действия- помним об этом. При выводе текста заносим этот текст в одинарные кавычки.

Можно выводить и целые арифметические выражения: writeln( 3 + 4*5 );

Количество пробелов и вообще их наличие не принципиально- пробелы ставятся просто для наглядности, чтобы всё не превращалось в одну большую непонятную кашу. Также делаются отступы (Tab) - их ты уже мог видеть выше на скриншотах.

 

Переменные и ввод данных

Для ввода данных нам нужно сначала занести эти данные в ячейки памяти. Эти ячейки памяти бывают для разных типов: для целых чисел (integer), вещественных или любых (real), а также для строк (string) и логических переменных (boolean). Мы для начала посмотрим только на числах.

Для создания ячейки памяти (переменной) не важно какого типа пишется сначала кодовое слово var. Это сокращение от variable- переменная. Кстати, писать этот var можно не только внутри begin-end (главная часть программы), но и до бегина.

Вот пример создания переменной целого типа:

var x : integer;

Тут x- это имя переменной. Мы можем писать и словами, необязательно только одну букву. Могут быть даже целые предложения- только пробел использовать нельзя. Либо цифры могут быть- но главное чтобы начиналось с буквы.

Вот пример создания 3 целых переменных:

var Peremennaya1, new_var_number, ThirdVar : integer;

...

Можно писать var на 1 строке, а далее на другой строке уже объявлять (инициализовать переменные). Например так:

var

a,b,c : real;

sum : integer;

...

Мы можем этой переменной либо присвоить начальное значение, либо запросить ввод значения с клавиатуры.

Присвоение выглядит так:

var a : integer; // создали ячейку памяти для хранения целых чисел

a:= 5; // внести в ячейку с именем a число равное 5

Знак присвоения- это двоеточие и затем равно. Дробные числа мы пишем через точку, например b:= 8.4;

Могут быть и отрицательные числа: x:= -5.141;

...

Для ввода переменных используется команда read (прочесть). Есть также readln, но разницы я не заметил.

Ниже пример программы, в которой мы вводим число, а затем выводим его удвоенное значение:

Можно написать простой калькулятор: мы вводим 2 числа, а программа выводит их сумму, разность и т.п.

Ниже будет пример:

Можно вводить и выводить строки- для этого используется тип данных string. Например, это может выглядеть так:

Можно спокойно вводить и выводить на русском, т.е. кириллицей- с этим проблем тут нет.

 

Арифметика и математические функции

С основными арифметическими действиями и так уже всё понятно:

+ прибавить

- отнять

* умножить

/ поделить

Но есть свои нюансы. Если мы поделим 2 целых числа, то у нас выведется дробное. Если мы хотим, чтобы выводилась только целая часть, то пишем не 19/3, а 19 div 3. Для получения остатка пишем 19 mod 3.

19/3= 6.3333333

19 div 3 = 6

19 mod 3 = 1 (т.е. в итоге 6 целых и 1 в остатке)

т.к. 19/3 = 6 + 1/3. Тут 6- это целая часть (div), а 1- остаток (mod).

...

Есть также типовые математические функции:

sqrt(x) - арифметический корень числа х. Например, если мы хотим присвоить переменной значение корня из 15, пишем так:

var x:real; x:= sqrt(15);

Вывести можно так: writeln( sqrt(28.57) );

Аналогично всё делается с другими математическими функциями. Аргумент у нас указывается в скобках. Иногда может быть и несколько аргументов.

1. Арифметический корень: sqrt(x)

2. Степень: power(a,b) - тут происходит a^b, т.е. a в степени b.

3. Десятичный логарифм: log10(x)

4. Натуральный логарифм: ln(x)

5. Двоичный логарифм: log2(x)

6. Логарифм, который возвращает комплексное число- т.е. результатом будет именно комплексное число: log(x)

7. Превращение дробного числа в целое- т.е. обрубается дробная часть: trunc(x)

8. Синус, косинус, тангенс соответственно: sin(x), cos(x), tan(x)

9. Обратные тригонометрические функции: arcsin(x), arccos(x), arctan(x)

10. Модуль числа или абсолютное значение: abs(x)

 

Условия

Часто в программе происходит разделение сценариев: если происходит такое-то событие, то действуем так- а иначе действуем уже вот так. Или просто делаем что-то только если соблюдается такое-то условие. Это также называется ветвлениями.

Условия в паскале реализуются через команду if (если). Например, вводим 2 числа. И программа должна вывести то, которое больше. Если оба числа равны, то так и выведем.

Т.е. что тут происходит? Если a>b, то выводим a (ну т.к. оно больше). Иначе если a<b, то выводим b. Иначе (во всех оставшихся случаях) выводим что числа равны.

Можно else вообще не использовать, т.к. это часто путает как сам по себе, так и то что тут не нужны точки с запятой, т.к. всё идёт как бы как одно такое длинное действие.

Если после условия у нас несколько действий, то нужно использовать уже начало (начало условия) и конец через кодовые слова begin и end. Только тут после end уже ставится точка с запятой. Точка ставится только после главного end.

Например, напишем программу, которая будет решать квадратное уравнение. Сначала мы вводим 3 коэффициента: старший член, средний и свободный.

var a,b,c : real; read(a,b,c);

Далее мы считаем дискриминант- от его знака будет зависеть исход решения. Можно выделить для него отдельную переменную:

var d : real; d:= b*b - 4*a*c;

И далее пишем- если дискриминант меньше нуля, то вещественных корней нет. Если равен 0, то 1 корень (и считаем его). И т.д. и т.п. Ниже вся программа:

Мы можем проверять разные условия. Например, число чётное или нечётное. Если число чётное, то его остаток при делении на 2 равен нулю, это запишется так:

if (x mod 2 = 0) then write('Число чётное');

Ну а если x нечётное, то x mod 2 = 1, т.е. остаток при делении на 2 будет равен 1.

Если число делится на 3, это значит что остаток при делении на 3 равен нулю или запишем как x mod 3 = 0. И т.д. и т.п.

Если последняя цифра числа равна 5, то это также значит что остаток при делении на 10 равен 5. Запишется как x mod 10 = 5. Для последних 2 цифр уже смотрим остаток при делении на 100.

...

Условия можно объединять. Например, выведем число только в случае когда оно нечётное и при этом заканчивается на 7:

if (x mod 2 = 1) and (x mod 10 = 7) then write(x);

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

Здесь использована логическая операция И (and). Есть также ИЛИ (or) - когда должно соблюдаться хотя бы одно из двух. Для сложных логических выражений лучше использовать скобки.

И есть также третья стандартная логическая операция- отрицание (not). Например выведем число только если оно не заканчивается на 6:

if not (x mod 10 = 6) then write(x);

Условия можно заключать в логические (булевые) переменные. Это делается для удобства, а также играет роль как переключение рычагов в программе. Позже ты чаще будешь с этим встречаться. Например "не кончается на 6" можно вынести в отдельную переменную.

 var not_6 : boolean;

 not_6 := x mod 10 = 6;

 if not_6 then write(x);

Необязательно присваивать какие-то выражения. Можно просто писать true (истина) или false (ложь). И эти параметры (истина или ложь) как рычаги могут меняться.

С этими логическими переменными также можно производить логические операции- либо заменять их на соответствующие арифметические. Операции И (конъюнкция) соответствует умножение, операции ИЛИ (дизъюнкция) - сложение. Это возможно благодаря тому что для компьютера это не истина или ложь, а лишь единицы (есть сигнал) и нули (нет сигнала).

 

Циклы

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

Например, мы хотим сосчитать следующую сумму: 1*1 + 2*2 + ... + 1000*1000. Вводить мы будем это долго, но можно использовать следующий трюк: суммировать в цикле. Ввести переменную-накопитель для суммы и итератор. Делается это так:

 var sum : integer;

 sum:= 0;

 for var i:=1 to 1000 do sum:= sum + i*i;

 write(sum);

В первой строке мы создаём ячейку памяти для хранения частичных сумм. Это и есть переменная-накопитель. Во второй строке мы присваиваем начальное значение- сначала сумма равна нулю.

В третьей строке происходит сам цикл. Для некой переменной i (это итератор) от 1 до 1000 делать. И делаем мы что: считаем 0 + 1*1 и заносим в сумму- будет 1. На следующем этапе: 1 + 2*2 = 5. Далее 5 + 3*3 = 14. И т.д. повторяется 1000 раз.

Действие sum:= sum + i*i; можно писать более коротко: sum+= i*i;

Итератор можно было объявить отдельно. В итоге можно написать то же самое так:

 var sum, i : integer; sum:= 0;

 for i:=1 to 1000 do sum+= i*i;

 write(sum);

Для закрепления ещё один пример: высчитывание факториала: 1*2*3*...*n = n!

 var n:integer; read(n);

 var res : integer; res:= 1;

 for var i:=1 to n do res*= i;

 write(res);

В случае умножения накопитель уже начинается не с нуля, а с единицы. А в остальном то же самое. res:= res*i; заменено на более краткую запись res*=i; С нерассмотренными вычитанием и делением можно делать то же самое: -= и /= соответственно.

...

Цикл типа for используется когда мы точно знаем количество итераций. Например, мы можем 15 раз подряд вывести слово привет:

for var i:=1 to 15 do writeln('Привет');

Или даже так:

for var i:=1 to 15 do writeln(i, '-й привет');

Необязательно начинать с единицы. Можно было сделать от 2 до 16:

for var i:=2 to 16 do writeln(i-1, '-й привет');

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

for var i:=16 downto 2 do writeln(i, ' Привет');

Если у нас действий больше чем одно, то используются стандартные begin и end. Ниже пример:

 for var i:=1 to 10 do begin

if(i mod 2 = 1) then writeLN('ПРИВЕТ');

if(i mod 2 = 0) then writeLN('ЗДРАСТЕ');

end;

В примере выше будут чередоваться ПРИВЕТ и ЗДРАСТЕ.

...

Когда мы не знаем точное число повторений, используется цикл типа while (пока). Т.е. пока какое-то условие соблюдается, мы делаем это по кругу. Тут чаще действий больше одного и поэтому используем стандартные begin и end. Бесконечный цикл реализуется через пока(истина), т.е. всегда: while(true) или while(1).

Посмотрим на примере алгоритма Евклида для нахождения наибольшего общего делителя. Предположим, нам надо найти НОД 2 чисел: 108 и 48. Тогда ищем остаток: 108 mod 48 = 12. И теперь продолжаем то же самое для 48 и 12 пока не дойдём до нуля. Вот на второй итерации уже дойдём: будет 12 и 0. Тогда 12 и будет НОД.

Begin

var x,y : integer;

read(x,y);

var z : integer; // вспомогательная переменная

while(y<>0) do begin

z:= x;

x:= y;

y:= z mod x;

end;

write(x);

end.

Не равно мы пишем как меньше или больше, т.е. знак <> (нестрогие знаки неравенств <= и >= тоже возможны)

...

Есть также третий вид циклов, который очень редко используется- но он есть. Цикл с постусловием repeat-until. Может использоваться для отслеживания ошибок при неправильном вводе. Ниже пример:

 var x : integer;

 var pass : boolean;

Repeat

Begin

write('--> '); read(x);

pass:= (x>1) and (x<7);

if not pass then writeln('Ошибка ввода!');

end;

until pass;

Что тут происходит? repeat- это повторять, until- до. Т.е. мы повторяем до тех пор пока не случается что-то определённое. Повторять то что у нас заключено между begin и end. Для удобства введена логическая переменная pass- т.е. разрешено такое значение икса.

 

Функции и процедуры

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

Функции пишутся до главного begin-а. Синтаксис группы посмотрим на примере функции, которая считает квадрат числа, т.е. умножение числа само на себя.

function q2 (x:real) : real;

Begin

q2:= x*x;

end;

Сначала пишется кодовое слово function. Далее имя этой функции- в моём случае q2. Далее в скобках перечисляются аргументы- т.е. те переменные, от которых зависит значение функции. Далее указывается тип возвращаемого значения и ставится точка с запятой. Результат (значение) функции высчитывается между begin и end как показано выше.

Ниже пример как мы можем использовать уже нашу функцию в главной части программы- между главными begin и end.

Begin

writeln(q2(1.3));

end.

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

function mdl (x,y : real) : real;

Begin

mdl:= sqrt(x*x+y*y);

end;

 

function TarafCos (x,y, angle : real) : real;

Begin

angle/= (180/3.1415926535); // перевод из градусов в радианы

TarafCos:= sqrt(x*x+y*y - 2*x*y*cos(angle));

end;

 

Begin

for var i:=1 to 10 do writeln('mdl(',1,',',i,')= ',mdl(1,i));

writeln();

 

for var a:=1 to 12 do

Begin

var angle : real := a*15;

writeln('Сторона при ',angle,' градусов: ', tarafcos(1,1,angle));

end;

 

end.

Что тут может быть новое:

1. Чтобы сделать переход на новую строку без вывода текста, можно просто написать writeln();

2. В самом цикле где мы сделали var a:=1 нельзя эту переменную менять внутри цикла- поэтому там я сделал новую var angle.

3. Можно присвоить начальное значение переменной сразу после объявления в одной строке, как на примере с var angle выше.

4. Синусы и косинусы считают в радианах, а мы вводим в градусах. Для перевода градусной меры в радианную, нужно поделить где-то на 57, т.е. на 180/пи, где пи- это число пи, равное примерно 3.1415926535.

Результатом же программы будет следующее:

mdl(1,1)= 1.4142135623731

mdl(1,2)= 2.23606797749979

mdl(1,3)= 3.16227766016838

mdl(1,4)= 4.12310562561766

mdl(1,5)= 5.09901951359278

mdl(1,6)= 6.08276253029822

mdl(1,7)= 7.07106781186548

mdl(1,8)= 8.06225774829855

mdl(1,9)= 9.05538513813742

mdl(1,10)= 10.0498756211209

 

Сторона при 15 градусов: 0.261052384432684

Сторона при 30 градусов: 0.517638090190586

Сторона при 45 градусов: 0.76536686470944

Сторона при 60 градусов: 0.999999999974079

Сторона при 75 градусов: 1.21752285798776

Сторона при 90 градусов: 1.41421356234135

Сторона при 105 градусов: 1.58670668055058

Сторона при 120 градусов: 1.73205080753895

Сторона при 135 градусов: 1.8477590649968

Сторона при 150 градусов: 1.93185165255877

Сторона при 165 градусов: 1.98288972273688

Сторона при 180 градусов: 2

...

Идём дальше. Можно вынести это число пи в отдельную переменную на самый верх программы, а далее использовать для удобства. Например:

var pi : real := 3.1415926535;

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

function GradusToRadian (var angle : integer) : real;

begin

           GradusToRadian:= angle /= (180/pi);

end;

Далее- помимо функций у нас есть ещё и процедуры. Это те же функции, но они не возвращают никакого значения- пустые функции. В паскале они называются процедурами и вместе function мы пишем procedure. Например, перевод на новую строку можно вынести в отдельную процедуру.

procedure nl ();

Begin

writeln();

end;

Если аргументов нет, скобки мы всё равно ставим- это значит что мы создаём именно функцию или процедуру. При вызове тоже скобки ставим:

Begin

writeln(1);

nl();

writeln(2);

end.

Т.е мы по сути создали более короткую и удобную запись. Можно сделать ещё одну процедуру, которая будет делать перевод на n строк.

procedure nl (n : integer);

Begin

for var i:=1 to n do nl();

end;

И ниже использование:

Begin

writeln(1);

nl(5);

writeln(2);

end.

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

Вместо nl вообще могли сделать просто одно подчёркивание:

procedure _ ();

Begin

writeln();

end;

 

procedure _ (n : integer);

Begin

for var i:=1 to n do _();

end;

 

Begin

writeln(1);

_(5);

writeln(2);

end.

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

function deg (a:real; n:integer) : real;

Begin

var res : real := 1;

if n>0 then for var i:=1 to n do res*=a;

if n<0 then for var i:=1 to abs(n) do res/=a;

if n=0 then res:= 1;

deg:= res;

end;

 

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

Ниже пример функции двойного факториала с использованием рекурсии. Как считается двойной факториал:

5!!= 1*3*5, 6!!= 2*4*6, 7!!= 1*3*5*7- вот по такому принципу. Т.е. если аргумент чётный, то мы делаем так: 6!!= 1*2*3*2*2*2 = 3! * 2^3.  А если нечётный, то 5!!= 1*2*3*4*5 / (2*4) = 5! / (5-1)!!. Запишем это:

// обычный факториал

function fact (n:integer) : integer;

Begin

var res:integer:=1;

for var i:=1 to n do res*=i;

fact:=res;

end;

 

// двойной факториал при t=2, обычный при t=1

function fact (n,t:integer) : integer;

Begin

if t=1 then fact:= fact(n);

if t=2 then begin

if (n mod 2 = 0) then fact:= fact(n div 2)*trunc(power(2,n/2));

if (n mod 2 = 1) then fact:= fact(n) div fact(n-1,2);

end;

end;

 

Begin

for var i:=1 to 10 do writeln(i,'!!= ',fact(i,2));

end.

Здесь в двойном факториале мы используем целочисленное деление div, т.к. компилятор ругается что при делении 2 целых чисел получается вещественное, а нам нужно именно целое.

 

Статические массивы

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

Ниже пример создания массива целых чисел:

var mass : array[1..10] of integer;

Здесь mass- это название массива, array- кодовое слово. По-английски массив это и есть array. Далее 1..10 это значит что будут элементы с 1-го по 10-й номер, т.е. 10 элементов. Можно спокойно написать array[4..25] например. И далее тип элементов массива, в нашем случае integer- целые числа.

Далее в этот массив можно внести значения. Это могут быть как случайные значения, так и вводимые с клавиатуры, так и заранее определённые. Пусть в нашем массиве будут случайные числа от 0 до 99. Случайное число от 0 до 99 записать легко: random(100). Т.е. random(n) нам даёт случайное целое число от 0 до n-1. Перебор элементов массива обычно происходит в цикле:

for var i:=1 to 10 do mass[i]:= random(100);

Обращение к i-му элементу записывается ка mass[i]. Далее можно вывести например 4-й элемент:

writeln(mass[4]);

А можно и действия арифметические производить:

writeln(mass[2] + mass[3]*mass[7]);

Можно присвоить какому-нибудь элементу новое значение:

mass[2]:= 15;

А можно делать это и в цикле:

for var i:=1 to 10 do mass[i]:= i*i;

В данном случае у нас будут элементы: 1, 4, 9, 16 и т.д.

Для ввода с клавиатуры пишем так:

for var i:=1 to 10 do readln(mass[i]);

...

При работе с массивами есть типовые задачи. Например, нахождение максимального и минимального элемента. Как, например, найти максимальный? Возьмём первый элемент и скажем что он будет типа условно максимальный. Далее идём по следующим элементам- если встретим какой-то элемент, который больше нашего условного максимального, значит он станет новым максимальным. И так до конца массива. Вот пример:

var max:= mass[1];

for var i:=2 to 10 do if mass[i]>max then max:= mass[i];

writeln('Максимальное= ', max);

Поиск минимального аналогичен- можешь произвести его самостоятельно для тренировки. Ниже в данной главе ты ещё увидишь ответ. Тип переменной max мы не указывали, т.к. передался тип от mass[1] - компилятор это тоже переваривает.

А какой именно элемент максимальный? Ну 1-й, 2-й и т.д. Это можно найти попутно:

var max:= mass[1];

var poz:= 1;

 

for var i:=2 to 10 do

if mass[i]>max then begin

max:= mass[i];

poz:= i;

end;

                       

writeln('Максимальное= ', max);

writeln('Какой по счёту= ', poz);

Ниже пример нахождения среднего арифметического- т.е. это сумму всех элементов поделить на их количество.

var sum:=0;

for var i:=1 to 10 do sum+= mass[i];

writeln('Среднее= ', sum/10);

Опять же тут тип переменной sum не указали, потому что компилятор на основе того что мы присвоили ей 0 и так понимает что это целое. Можешь навести даже на неё мышкой и увидеть.

...

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

Первое, что приходит в голову: создаём новый массив и заносим туда уже отсортированные значения.

Пример: дан массив 14 35 51 17 28 (из 5 элементов)

Мы сначала ищем максимальный- это 51. Ставим его первым элементом в новом массиве. А в старом массиве мы этот максимальный должны убить- чтобы он не мешал находить 2, 3 и т.д. по счёту максимальные. Можно сделать его меньше, чем самое минимальное из всех чисел- 14. А мы запишем как 13. И все будем так записывать.

Под конец, если нам нужно чтобы новый массив назывался по-старому, мы можем перезаписать элементы. Ниже будет пример всей сортировки массива из 10 элементов по убыванию:

var min:= mass[1];

for var i:=1 to 10 do if mass[i]<min then min:= mass[i];

min-=1;

 

var massnew : array[1..10] of integer;

var max, poz : integer;

 

for var i:=1 to 10 do begin

max:= mass[1];

poz:= 1;

for var j:=2 to 10 do if mass[j]>max then begin

max:= mass[j];

poz:= j;

end;

massnew[i]:= max;

mass[poz]:= min;

end;

 

for var i:=1 to 10 do mass[i]:= massnew[i];

Чтобы развернуть уже отсортированный по убыванию массив по возрастанию, можно заново не сортировать, а просто перезаписать все элементы в обратном порядке.

Только нужно помнить чтобы данные не терялись- если у нас например массив:

51 35 28 17 14

То если записать mass[5]:= mass[1], то мы получим 51 35 28 17 51 и потеряем таким образом элемент который был пятым. Поэтому мы запишем всё в новый массив, а потом перезапишем в старый:

for var i:=1 to 10 do massnew[i]:= mass[11-i];

for var i:=1 to 10 do mass[i]:= massnew[i];

...

И ещё одна задача- поиск элемента в массиве. Т.е. каким именно по счёту данный элемент будет в массиве. Поиск бывает линейный и бинарный. Линейный- это простой и очевидный, на его примере и посмотрим. Т.е. мы тут просто идём подряд по элементам массива и сравниваем с тем который надо найти. Если не нашли, выведем что не нашли. Вот ниже пример на всё том же массиве:

write('Элемент с каким значением ищем: ');

var x : integer; read(x);

 

var find := false;

for var i:=1 to 10 do begin

if x=mass[i] then begin

writeln('Порядковый номер первого элемента с таким значением: ', i);

find:= true;

break;

end;

end;

if not find then writeln('Элемент не найден');

Здесь кодовое слово break- это выход из цикла. Т.е. когда мы элемент нашли, нет смысла дальше по массиву идти. Есть также команда continue- она пропускает текущую итерацию (т.е. переводится как "продолжить") и обычно используется с условием, например if x<10 then continue;

Мы создали логическую переменную find- она показывает, нашли мы элемент или нет. Потому что если мы нашли, то не надо выводить сообщение о том что мы якобы не нашли.

...

Массивы могут быть не только одномерными, но и многомерными- в частности, двумерные. По сути двумерный массив это таблица, её ещё иногда называют матрицей.

Создать двумерный массив можно так: var m:array[1..7, 1..5] of integer; А обращаться к элементу можно так: m[3,2];

 

Динамические массивы

В предыдущей главе мы рассмотрели статические массивы- их особенность и недостаток в том, что количество элементов чётко задано и не может меняться. И мы не можем ввести даже это количество элементов- т.е. мы в программе уже изначально должны задать что вот типа там 10 элементов и всё- не больше и не меньше. И так оно всегда и будет 10 элементов.

Но есть и динамические массивы- в них мы сами можем указать количество элементов с клавиатуры и менять это количество по ходу программы, это очень сильно расширяет возможности программирования.

Ниже пример создания динамического массива. Сначала мы создаём саму переменную массива:

var a : array of real;

Далее мы вводим- сколько именно элементов нам нужно:

var size : integer; read(size);

И далее выделяем память процедурой:

setlength(a,size);

Всё, массив готов. Можно заносить элементы и работать. Например, запросить с клавиатуры:

for var i:=0 to size-1 do read(a[i]);

При этом тут нумерация элементов массива происходит строго с нуля. Т.е. сначала идёт нулевой элемент, потом первый и т.д. Последний элемент будет под номером size-1.

Можно переопределить размер массива- например, сделать его больше. И тогда старые данные в массиве сохранятся.

setlength(a, size*2); // сделали массив в 2 раза длиннее

Можно и укоротить- понятно что последние элементы тогда будут удалены.

С помощью функции length можно получить длину массива. Вот пример:

writeln(length(a));

Таким образом, можно удобнее перебирать элементы в цикле:

for var i:=0 to length(a)-1 do что-то там

Ну и напоследок отметим, что переменная массива- это по сути ссылка на первый элемент массива, ссылка на некий адрес в оперативной памяти.

...

Можно делать переприсваивание элементов проще и короче- как в статическом массиве, так и в динамическом. Мы можем легко создать копию нашего массива:

var b := a;

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

Но в случае с динамическими массивами это будет не совсем копия, т.к. новый массив ведёт в ту же область оперативной памяти. И если мы в старом массиве поменяем, скажем, 3-й элемент, то и в новом 3-й элемент тоже поменяется. Чтобы этого избежать, нужно делать так:

var b:= copy(a);

Потестировать все эти процессы ты можешь на примере кода ниже:

Begin

var a : array of integer;

var n:= 4 + random(10);

setlength(a,n);

for var i:=0 to length(a)-1 do a[i]:= random(20);

 

for var i:=0 to length(a)-1 do write(a[i],' ');

writeln(); writeln();

 

var b:= copy(a);

a[3]*=2;

for var i:=0 to length(a)-1 do write(a[i],' ');

writeln();

for var i:=0 to length(a)-1 do write(b[i],' ');

end.

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

type numbers = array of real;

 

function poz_sum (a : numbers) : real;

Begin

var res:real:=0;

for var i:=0 to length(a)-1 do if a[i]>0 then res+= a[i];

poz_sum:= res; 

end;

 

А вот пример как мы можем эту функцию использовать:

 

var m : array of real;

setlength(m,5);

for var i:=0 to length(m)-1 do read(m[i]);

writeln(poz_sum(m));

Можно инициализировать элементы массива ещё при объявлении:

var a: array of integer := (1,3,5);

И также использовать функции работы с массивом как свойства и методы класса. Например, длину массива mass мы можем получить и так: mass.length.

Выделять память можно и с помощью команды new вместо setlength:

var mass := new integer [7] // создали динамический массив из 7 элементов (нумерация стандартно с нуля)

Но при этом при переобъявлении массива (если мы хотим например задать новый размер) все данные в нём теряются.

Есть также встроенные функции сортировки массивов:

System.Array.Sort(a) - сортирует массив a по возрастанию

System.Array.Reverse(a) - меняет в обратном порядке данные в массиве a

Кстати, сам массив мы можем выводить так же: writeln(a); Он будет выведен в таком например формате: [8,5,1]

...

Теперь о том как использовать динамический массив в функции не в качестве аргумента, а в качестве возвращаемого значения- т.е. чтобы результатом функции получался именно массив. Ниже пример функции, которая создаёт массив n случайных значений от 0 до 9 (n вносится)

type num = array of integer;

 

function rangen (n:integer) : num;

Begin

var res : array of integer;

setlength(res,n);

for var i:=0 to n-1 do res[i]:= random(10);

rangen:= res;

end;

А вот так мы можем её использовать:

var a := rangen(6);

Теперь пример, когда у нас и аргументы являются массивами, и сама функция, сам результат. Если массив представить в качестве вектора (список координат), то функция ниже считает как раз вектор, который является суммой двух вносимых векторов:

type num = array of real;

function sum (a,b:num) : num;

Begin

var res : array of real;

setlength(res,a.length);

for var i:=0 to res.length-1 do res[i]:= a[i]+b[i];

sum:= res;

end;

А вот так это можно использовать:

var x : array of real := (2.3,1,2);

var y : array of real := (0,-1,-1.1);

var c:= sum(x,y);

Вообще же массивы это огромная длинная тема, тут много различных возможностей и подробнее об этом мы поговорим ещё в 4-м разделе данного электроннописного труда. Но основные мы уже тут разобрали.

 

Работа со строковыми данными

Помимо привычных чисельных данных (integer, real) есть ещё строковые- типа string. Строка является массивом символов. Например, мы создаём строку:

var s : string := 'МУСЬКА';

Мы можем обратиться к первой букве так: s[1]; Ко второй букве s[2] и т.д. Нумерация тутн начинается с единицы. Количество символов в этом слове считаем как длину массива: length(s) или s.length - два варианта возможны.

Можно складывать строки:

var s : string := 'МУСЬКА';

var s1 : string := 'киса';

writeln(s+s1);

Выведет МУСЬКАкиса

По-другому сложение строк также называется конкатенацией.

Можно заменить какую-нибудь букву, например сделать не МУСЬКА, а ДУСЬКА, т.е. первую букву заменим: s[1]:= 'Д';

Иногда также может понадобиться перевод из строкового формата в числовой и наоборот. Из числового в строковый это когда мы файлы создаём с именами 1, 2, 3 и т.д. например. А из строковый в числовой, соответственно, наоборот- когда мы эти файлы с такими именами считываем. Но это только один из наиболее распространённых примеров применения.

В pascal abc есть уже встроенные функции для работы со всем этим. Ниже пример перевода из числа в строку:

var x:= 45;

var s := x.ToString;

write(s + 'kt');

А теперь из строки в число:

var s:= '932';

var t := s.ToInteger;

write(t*2);

 

Символы мы можем использовать просто как строковые данные, т.е. что 1 символ, что целое слово по сути в паскале одно и тоже. Вот пример:

var s:= '932';

var t := s[1];

write(t + s[2]);

________________________________________________________________________

Раздел 2. Графические модули и простые игры.

 

Модуль CRT

В pascal abc есть специальные модули, с которыми мы будем тут знакомиться. Первый модуль это CRT- это консольное окно. Запускается оно только без связи с оболочкой, т.е. через SHIFT + F9. Тут происходит работа с текстом, при этом есть некоторые графические возможности.

uses crt;

begin

// тут будет программа

end.

// CRT от английского Cathode Ray Tube – Электронно-лучевая трубка

...

Вот некоторые функции работы с модулем CRT:

ClrScr;   { Очистить экран }

TextColor(White); { Установить белый цвет букв }

TextBackGround(Blue); { Установить синий цвет фона }

GotoXY(36,13); { Поставить курсор в 36 колонку, 13 строку }

write(' Привет '); { Вывести текст }

ReadKey;      { Ожидать нажатия любой клавиши }

...

В качестве цвета может использоваться число от 0 до 15, но лучше пользоваться определенными в модуле CRT константами: 

Black Черный

DarkGray Темно-серый

Blue Синий

LightBlue Светло-синий

Green Зеленый

LightGreen Светло-зеленый

Cyan Небесно голубой

LightCyan Ярко-голубой

Red Красный

LightRed Светло-красный

Magenta Малиновый

LightMagenta Светло-малиновый

Brown Коричневый

Yellow Желтый

LightGray Светло-серый

White Белый

 

Модуль растровой графики

Растровая графика это графика, состоящая из точек. Для неё используется модуль graphABC.

Вот некоторые фигуры, которые мы можем рисовать:

line(x1,y1,x2,y2) - отрезок между точками (х1,у1), (х2,у2)

rectangle(x1,y1,x2,y2) - прямоугольник между диагональными точками (х1,у1), (х2,у2)

circle(x,y,r) - круг с центром в (х,y) и радиусом r

textout(x,y,s) - текст s начиная с точки (x,y)

...

Система координат устроена так что начало находится в левом верхнем углу. Ось Х идёт по горизонтали вправо, а ось Y- по вертикали вниз. Окно по умолчанию создаётся разрешением 640 на 480. Его можно спокойно увеличивать и уменьшать, оно не фиксировано.

Есть также константы: windowwidth= 640, windowheight= 480.

SetWindowSize(256,256) - установить размер окна 256 на 256.

...

Вот эта программа рисует 10 параллельных линий в цикле со сдвигом по горизонтали:

uses graphabc;

Begin

for var i:=0 to 9 do

line(50 + i*50, 100, 150 + i*50, 200);

end.

...

Фигуры можно закрашивать:

SetBrushColor(clRed) - закрасить фигуру красным.

SetPenColor(clRed) - закрашивает уже обводку.

Вот пример:

 SetBrushColor(clRed);

 SetPenColor(clRandom);

 circle(200,100,50);

А тут мы нарисуем круги друг в друге со случайными цветами:

 

Принцип тут на этих примерах понять очень легко.

Цвет можно задавать любой какой угодно с помощью функции rgb:

SetBrushColor(rgb(102,3,44));

 

Принцип тут по системе Red Green Blue. Тут 102- интенсивность красной компоненты, 3- зелёной, 44- голубой. Если поставить все по 255, то будет белый. Если все по 0, то чёрный.

 

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

 

clBlack – черный

clPurple – фиолетовый

clWhite – белый

clMaroon – темно-красный

clRed – красный

clNavy – темно-синий

clGreen – зеленый

clBrown – коричневый

clBlue – синий

clSkyBlue – голубой

clYellow – желтый    

clCream – кремовый

clAqua – бирюзовый

clOlive – оливковый

clFuchsia – сиреневый

clTeal – сине-зеленый

clGray – серый

clLime – ярко-зеленый

clLightGray – светло-серый

clMoneyGreen – цвет зеленых денег

clDarkGray – темно-серый

..... ..... .....

Вот ещё дизайновые возможности:

SetPenWidth(ширина) – устанавливает ширину обводки

SetPenStyle(стиль) – устанавливает стиль обводки (сплошной, пунктир и т.п.), возможные значения указаны в таблице (стиль применим только к ширине обводки 1 пиксель)

psSolid - сплошной

psClear - без обводки

psDash - пунктирами

psDot - точками

psDashDot - пунктир точка

psDashDotDot - пунктир и 2 точки

...

Задание стиля и цвета:

SetBrushColor(цвет) – устанавливает цвет кисти

SetBrushPicture(имя файла) – устанавливает в качестве образца для закраски кистью образец, хранящийся в файле, при этом текущий цвет кисти при закраске игнорируется.

ClearBrushPicture – очищает рисунок-образец, выбранный для кисти.

SetBrushStyle(стиль) – устанавливает стиль кисти, задаваемый параметром bs. 

Возможные стили кисти: bsSolid bsClear bsCross bsDiagCross bsHorizontal bsBDiagonal bsVertical bsFDiagonal

...

Ellipse(x1,y1,x2,y2) – рисует эллипс, заданный своим описанным прямоугольником с координатами противоположных вершин (x1,y1) и (x2,y2).

RoundRect(x1,y1,x2,y2,w,h) – рисует прямоугольник со скругленными краями; (x1,y1) и (x2,y2) задают пару противоположных вершин, а w и h – ширину и высоту эллипса, используемого для скругления краев.

Arc(x,y,r,a1,a2) – рисует дугу окружности с центром в точке (x,y) и радиусом r, заключенной между двумя лучами, образующими углы a1 и a2 с осью OX (a1 и a2 – вещественные, задаются в градусах и отсчитываются против часовой стрелки).

Pie(x,y,r,a1,a2) – рисует сектор окружности, ограниченный дугой (параметры процедуры имеют тот же смысл, что и в процедуре Arc).

Chord(x,y,r,a1,a2) – рисует фигуру, ограниченную дугой окружности и отрезком, соединяющим ее концы (параметры процедуры имеют тот же смысл, что и в процедуре Arc).

FloodFill(x,y,color) – закрашивает область одного цвета, начиная с точки (x,y) цветом color. 

 

Сапёр

Ниже рассмотрим как можно написать простую игру в pascalABC, например сапёр. Будем использовать модуль graphABC.

uses graphABC; // подключаем модуль

 

 

// поле создаания переменных

Var

mass: array [0..11,0..11] of integer; // поле из ячеек (0 и 11 взяты чтобы не выходить за границы массива- на самом деле поле 10 на 10)

pic: array [0..11] of Picture; // массив картинок (разные ячейки)

x,y,i,j,n:integer;

size:integer;

 

// если наткнулись на мину

procedure GameOver();

var i,j:integer;

Begin

for i:=1 to 10 do

for j:=1 to 10 do

pic[mass[i][j]].Draw(i*size, j*size); // рисование картинки

end;

 

 

// обработка нажатия мыши

 

procedure MouseDown(x,y,mb:integer);

Begin

 i:=x div size; // на какую по счёту именно ячейку мы нажали

 j:=y div size;

 

 if mb=1 then pic[mass[i,j]].Draw(i*size,j*size); // если левая кнопка мыши нажата

 if mb=2 then pic[11].Draw(i*size,j*size); // если нажали правую кнопку- тогда нарисуем флажок

 

 if (mb=1) and (mass[i,j]=9) then GameOver(); // если нажали на ЛКМ и попали на мину

 

end;

 

 

// главная часть программы

 

Begin

size:=32; // размер ячейки (картинка из 32х32 точек)

SetWindowSize(400,400); // создание окна 400х400

OnMouseDown:=MouseDown; // ожидаем нажатие мышкой на ту или иную ячейку

 

// загрузка картинок в массив картинок из файла

for i:=0 to 11 do

 pic[i]:=Picture.Create('images/'+i.ToString()+'.jpg');

 

// простановка мин случайным образом

for i:=1 to 10 do

 for j:=1 to 10 do

Begin

mass[i,j]:=10; // пока неоткрытая ячейка

if (random(5)=0) then mass[i,j]:=9; // в 1 из 5 случаев будет мина

end;

 

// сколько мин у нас по соседству в той ячейке, куда мы нажали

for i:=1 to 10 do

 for j:=1 to 10 do

Begin

n:=0; // счётчик

if mass[i,j]=9 then continue; // если это мина, то пропускаем данную итерацию

if mass[i+1,j]=9 then n:=n+1;

if mass[i,j+1]=9 then n:=n+1;

if mass[i-1,j]=9 then n:=n+1;

if mass[i,j-1]=9 then n:=n+1;

if mass[i+1,j+1]=9 then n:=n+1;

if mass[i-1,j-1]=9 then n:=n+1;

if mass[i-1,j+1]=9 then n:=n+1;

if mass[i+1,j-1]=9 then n:=n+1;

mass[i,j]:=n;

end;

 

 

// рисуем все ячейки (i*size, j*size- координаты ячейки)

for i:=1 to 10 do

 for j:=1 to 10 do

pic[10].Draw(i*size, j*size);

 

end

 

Модуль graphABC тут не закрывается, т.е. бесконечный цикл действует по умолчанию.

Все файлы для игр можно найти здесь в папке pascal abc: https://yadi.sk/d/_R2ESK3t3PHHrP

 

Программа рисования Paint

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

uses graphABC; // подключение модуля

var f:picture; c:color; // переменные для заднего фона и изменения цвета карандаша

 

// функция нажатия мыши- перемещение карандаша в координату (x,y)

procedure MouseDown(x,y,mb:integer);

Begin

 MoveTo(x,y);

end;

 

// событие движения при удержании кнопки мыши

procedure MouseMove(x,y,mb:integer);

Begin

 if mb=1 then LineTo(x,y); // ЛКМ- рисуем линию

 if mb=2 then Circle(x,y,10); // ПКМ- кружок с радиусом 10

end;

 

// событие отпускания мыши

procedure MouseUp(x,y,mb:integer);

Begin


Дата добавления: 2018-09-23; просмотров: 234; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!