PascalABC.NET официальный канал
1.91K subscribers
599 photos
1 video
9 files
404 links
Официальный канал языка и системы программирования PascalABC.NET
Download Telegram
Особые значения вещественного типа

Среди особых значений вещественного типа различают Бесконечность (положительную и отрицательную) и значение NaN - Not a Number (не число), которое возникает при некоторых некорректных операциях - например, при извлечении корня из отрицательного числа

##
// +∞: результат деления на 0 или константа PositiveInfinity
var posInf := real.PositiveInfinity;
var posInf2 := 1.0/0.0;
Println($'+∞: {posInf} Проверка: {real.IsPositiveInfinity(posInf)}');
Println($'1/0 → {posInf2}');

// -∞: отрицательное деление на 0 или константа NegativeInfinity
var negInf := real.NegativeInfinity;
var negInf2 := -1.0/0.0;
Println($'-∞: {negInf} Проверка: {real.IsNegativeInfinity(negInf)}');
Println($'-1/0 → {negInf2}');

// NaN: «не число» – результат недопустимых операций
var nanVal := real.NaN;
var nanVal2 := Sqrt(-1);
Println($'NaN: {nanVal} Проверка: {real.IsNaN(nanVal)}');
Println($'Sqrt(-1) → {nanVal2}');
👍112
Машинное 𝜀

Машинным 𝜀 называется минимальное вещественное значение, для которого для которого 1 + 𝜀 > 1. Для типа real оно находится между 1e-15 и 1e-16.

Вот программа для вычисления машинного 𝜀

function MachineEpsilon: real;
begin
var eps := 1.0;
while 1.0 + eps > 1.0 do
eps := eps / 2.0;
Result := eps * 2.0;
end;

begin
var eps := MachineEpsilon;
Println('Машинный эпсилон =', eps);
Println('1 + eps = 1?', 1 + eps = 1);
Println('1 + eps/2 = 1?', 1 + eps/2 = 1);
end.
131👍1
Модуль TurtleABC

Появился модуль TurtleABC для Linux

uses TurtleABC;

begin
SetScale(4);
SetOrigin(5,-35);
SetColor(Color.Blue);
Down;
loop 4 do
begin
Forw(20);
Turn(90);
end;

Up;
MoveTo(-30, 30);
Down;
SetColor(Color.Red);
loop 3 do
begin
Forw(15);
Turn(120);
end;
end.
🔥7👍62😈1
Различия между модулями и библиотеками

Данная таблица приводится в курсе Основы программирования в начале второго месяца обучения.

Все весьма сложные концепции разбираются в этом курсе подробно, эффективно и на ранних этапах
👍11
Чему равен корень из -1

А действительно - чему?

Это зависит от типа. Если тип - real, то это не число. В смысле число, которое называется NaN (Not a Number).

Если тип - Complex - то вполне себе нормальное комплексное число, примерно равное i - комплексной единице.
12
Температура и цвет

Здесь демонстрируется использование вложенной условной операции, а также метода расширения Between для целых (по умолчанию он включает концы)

begin
var t := ReadInteger; // температура

var color :=
if t.Between(-100, 0) then 'blue' else
if t.Between(1, 15) then 'green' else
if t.Between(16, 30) then 'yellow' else
if t.Between(31, 100) then 'red' else
'black';

Println($'Temperature color: {color}');
end.
9
Решение задачи о 8 ферзях в PascalABC.NET

Классический алгоритм с рекурсией и проверкой конфликтов по столбцам и диагоналям. Каждое найденное расположение выводится на экран в виде массива: board[row] = column.

const N = 8;

var board := new integer[N]; // board[row] = столбец ферзя в этой строке

procedure Place(row: integer);
begin
for var col := 0 to N-1 do
begin
// проверяем конфликты со всеми ранее поставленными ферзями
var conflict := False;
for var prevRow := 0 to row-1 do
if (board[prevRow] = col) or // тот же столбец
(abs(board[prevRow] - col) = abs(prevRow - row)) then // та же диагональ
begin
conflict := True;
break;
end;

if not conflict then
begin
board[row] := col; // ставим ферзя

if row = N-1 then
board.Println // выводим решение
else Place(row+1); // продолжаем рекурсию
end
end;
end;

begin
Println('Все решения задачи о 8 ферзях:');
Place(0);
end.


Один из самых коротких известных алгоритмов. Замените N на 10, 12, 16 и посмотрите, что будет на больших шахматных досках :)
👍74
Линейная модель + активация = простейший ИИ в PascalABC.NET

Реализуем элементарный предсказатель на основе линейной комбинации признаков и функции активации.

Такой принцип лежит в основе логистической регрессии и нейронных сетей.

function LinearCombination(x, w: array of real; b: real): real;
begin
Result := b;
for var i := 0 to x.Length - 1 do
Result += x[i] * w[i];
end;

function Sigmoid(z: real) := 1 / (1 + Exp(-z));

function Predict(x, w: array of real; b: real; activation: real -> real) :=
activation(LinearCombination(x, w, b));

begin
var weights := [0.8, -0.3, 0.5, -0.2];
var bias := 0.1;
var x := [45.0, 120.0, 5.5, 24.5];

var probability := Predict(x, weights, bias, Sigmoid);
Println('Вероятность диабета:', probability);
end.


📌 В этом примере показано:
🔹 как считать линейную комбинацию x·w + b;
🔹 как применить сигмоиду — популярную функцию активации;
🔹 как через функциональные переменные передавать функцию в другую функцию;
🔹 как построить простейшую модель предсказания.

🧠 Это уже мини-модель машинного обучения — всё на чистом PascalABC.NET, без библиотек.
👍94
Градиентный спуск на PascalABC.NET

Хотим предсказывать итоговую оценку студента по двум параметрам:

x1 — часы подготовки
x2 — часы сна

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

Функция ошибки на одном наблюдении:
L = (w1*x1 + w2*x2 + b - y)**2


Частные производные:
dL/dw1 = 2*x1*(w1*x1 + w2*x2 + b - y)
dL/dw2 = 2*x2*(w1*x1 + w2*x2 + b - y)
dL/db = 2*(w1*x1 + w2*x2 + b - y)


Используем средний градиент по всей выборке.
function Loss(x1, x2, w1, w2, b, y: real) := 
(w1*x1 + w2*x2 + b - y)**2;

function dL_dw1(x1, x2, w1, w2, b, y: real) :=
2 * x1 * (w1*x1 + w2*x2 + b - y);

function dL_dw2(x1, x2, w1, w2, b, y: real) :=
2 * x2 * (w1*x1 + w2*x2 + b - y);

function dL_db(x1, x2, w1, w2, b, y: real) :=
2 * (w1*x1 + w2*x2 + b - y);

begin
var x: array of array of real := (
(2,6),(4,5),(5,7),(1,4),(3,6),
(6,5),(5,6),(7,8),(3,5),(4,7)
);
var y: array of real := (70,78,82,62,75,85,80,90,72,79);
var n := x.Length;

var w1 := 0.1; var w2 := 0.1; var b := 0.1;
// скорость обучения
var lr := 0.002;
var epochs := 20000;

for var epoch := 0 to epochs-1 do
begin
var grad_w1 := 0.0;
var grad_w2 := 0.0;
var grad_b := 0.0;
var total_loss := 0.0;

for var i := 0 to n-1 do
begin
var x1 := x[i][0];
var x2 := x[i][1];
var yi := y[i];
total_loss += Loss(x1,x2,w1,w2,b,yi);
grad_w1 += dL_dw1(x1,x2,w1,w2,b,yi);
grad_w2 += dL_dw2(x1,x2,w1,w2,b,yi);
grad_b += dL_db (x1,x2,w1,w2,b,yi);
end;

w1 -= lr * grad_w1 / n;
w2 -= lr * grad_w2 / n;
b -= lr * grad_b / n;

if (epoch+1) mod 5000 = 0 then
Println($'Эпоха {epoch+1}: ошибка={total_loss/n:0.0000};
w1={w1:0.000}; w2={w2:0.000}; b={b:0.000}');
end;
end.


Это базовый пример градиентного спуска без библиотек.
🔥82🤡2👍1
Рост пользователей из Китая

По данным mail.ru с июля 2025 года наблюдается стремительный рост пользователей PascalABC.NET из Китая
🤩63👍1
Task / Task<integer> в PascalABC.NET

Task — это задача, которая может выполняться параллельно с основной программой.

Когда мы запускаем задачу через Task.Run, мы передаём внутрь лямбда-выражение без параметров, которое что-то вычисляет и в конце пишет:

Result := ...


В данном случае лямбда возвращает целое значение, и поэтому Task.Run создаёт объект класса Task<integer>.

Что такое t1.Result

t1.Result — это способ получить результат работы задачи.

Если задача ещё не закончила работу — при обращении к t1.Result программа приостановится, подождёт завершения задачи и потом вернёт её результат.

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

Пример: две задачи считают параллельно

uses System.Threading.Tasks;

begin
var t1 := Task.Run(()->
begin
var s := 0;
for var i:=1 to 5_000_000 do
s += i;
Result := s // => t1 будет Task<integer>
end);

var t2 := Task.Run(()->
begin
var s := 0;
for var i:=1 to 5_000_000 do
s += i*i;
Result := s // => t2 будет Task<integer>
end);

// Ожидание завершения + получение результата
var r := t1.Result + t2.Result;
Println(r);
end.


Что происходит
🔹 Создаются две независимые задачи
🔹 Каждая считает свою сумму
🔹 Они работают одновременно
🔹 Когда мы обращаемся к t1.Result и t2.Result, программа ждёт и получает результат
🔹 Затем выводим сумму двух результатов
3👍3
Перегрузка операций - теперь без extensionmethod

Теперь в PascalABC.NET для перегрузки операций не надо писать extensionmethod

Операции перегружаются как обычные функции с именем operator@ где @ - значок операции

#новое
👍11
Чему равен синус?

Проверим. Для этого просто напечатаем синус 👀

А синус оказывается - это функция 🤔
14👍32
🧠 ML: нормализация признаков в PascalABC.NET

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

function Normalize(a: array of real): array of real;
begin
var mean := a.Average;
var std := Sqrt(a.ConvertAll(x -> (x-mean)**2).Average);
Result := a.ConvertAll(x -> (x-mean)/std);
end;

function NormalizeCols(a: array[,] of real): array[,] of real
:= MatrByCol(a.Cols.ConvertAll(col -> Normalize(col)));

begin
var a := Matr([1.1,2,3],[4.3,5,6],[7.0,8,9],[5.7,2,8]);
NormalizeCols(a).Print(7,1)
end.


Нормализация столбцов — стандартный шаг при работе с датасетами: она улучшает сходимость моделей, делает признаки сопоставимыми и устраняет влияние масштаба. Благодаря функциональному стилю PascalABC.NET такие операции можно выразить очень компактно.
👍71
Copy для массивов и матриц как метод расширения

Для создания копии массивов и матриц пользуемся методом расширения Copy
4👍3
Массивы и списки - это последовательности

В PascalABC.NET есть специальный тип-последовательность - набор элементов, которые можно перебрать один за другим циклом foreach.

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

function SumSquares(s: sequence of integer): integer;
begin
Result := 0;
foreach var x in s do
Result += x*x;
end;

begin
SumSquares(Arr(1,2,3)).Print;
SumSquares(Lst(2,3,4)).Print;
end.
6👍1
Количество простых чисел

Как вычислить количество простых чисел, меньших заданного n?

Используем LINQ и функциональное программирование.

Два способа: обычный и с распараллеливанием.

##
function IsPrime(x: integer) := (2..x.Sqrt.Round).All(i -> x.NotDivs(i));

var n := 4000000;
(2..n).Where(IsPrime).Count.Println;
MillisecondsDelta.Println;

(2..n).AsParallel.Where(IsPrime).Count.Println;
MillisecondsDelta.Println;


Linq автоматически распараллеливает проверки для разных чисел на простоту по различным потокам.

Скорость распараллеленной версии - примерно в 6 раз меньше - значит, процессор шестиядерный.
👍9❤‍🔥1
Умножение матриц в стиле LINQ

Для умножения матриц используем метод Zip и срезы матрицы по строке и столбцу

function Dot(a1, a2: array of real): real := a1.Zip(a2,(x,y) -> x*y).Sum;

function Mult(a, b: array[,] of real): array[,] of real :=
MatrGen(a.RowCount, b.ColCount, (i,j) -> Dot(a[i,:],b[:,j]));

begin
var a := Matr([1.1,2,3],[4.3,5,6],[7.0,8,9],[5.7,2,8]);
var b := Matr([4.0,5],[8.2,4],[3.0,9]);
a.Print(7,1); Println;
b.Print(7,1); Println;
Mult(a,b).Print(7,1);
end.

Результат:

    1.1    2.0    3.0
4.3 5.0 6.0
7.0 8.0 9.0
5.7 2.0 8.0

4.0 5.0
8.2 4.0
3.0 9.0

29.8 40.5
76.2 95.5
120.6 148.0
63.2 108.5
👍5🤩2
🔧 Замена текста внутри квадратных скобок

Если требуется заменить текст внутри [...], можно воспользоваться методами строк IndexOf, Remove и Insert.

Первую открывающую скобку ищем до цикла, а в самом цикле переходим к следующей уже со строки позиции конечной скобки.
begin
var s := 'Клад зарыт на [юго-западе] под [пальмой]';
var replaceStr := 'скрыто';

var startPos := s.IndexOf('[');
while startPos <> -1 do
begin
var endPos := s.IndexOf(']', startPos + 1);
if endPos = -1 then break;

s := s.Remove(startPos + 1, endPos - startPos - 1);
s := s.Insert(startPos + 1, replaceStr);

startPos := s.IndexOf('[', endPos); // безопасно в PascalABC.NET
end;

Println(s);
end.

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

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

begin
var data := [1, 2, 3, 2, 4, 5, 3, 6, 7, 1, 8, 9, 4, 10];
data.Println;

var unique, multi: set of integer;

foreach var num in data do
begin
if num in multi then
continue;
if num in unique then
begin
unique -= num;
multi += num;
end
else unique += num;
end;

unique.Order.Println;
end.


Ровно один раз встретившиеся элементы выводятся в отсортированном виде.
👍9
Решето Эратосфена - максимально компактно и понятно

Решето Эратосфена — это один из наиболее эффективных алгоритмов для нахождения всех простых чисел до заданного предела n. Он отличается высокой производительностью и простотой реализации.

Алгоритм обладает асимптотической сложностью O(n * log ⁡log ⁡n), что делает его значительно быстрее простейших методов определения простоты через деление.

##
function SieveOfEratosthenes(n: integer): array of integer;
begin
var isPrime := [False] * 2 + [True] * (n - 1);

for var i := 2 to ISqrt(n) do
if isPrime[i] then
for var j := i * i to n step i do
isPrime[j] := False;

Result := (2..n).Where(i -> isPrime[i]).ToArray;
end;

SieveOfEratosthenes(1000).Println;


А что такое ISqrt(n) - читайте в справке!
4👍2🔥1