Разработка

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

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

0	2	1
0	3	1
1	2	1
1	3	1
2	3	1

должен получиться массив ([0, 2, 1], [0, 3, 1], [1, 2, 1], [1, 3, 1], [2, 3, 1]).

Приступаем к программированию:

Perl
use Capsule; sub resistance { } open my $circuitFile, '<', shift; print resistance(map { chomp; [split /\s+/] } <$circuitFile>), "\n"; close $circuitFile;

В трёх последних строках программы файл, чьё имя передано в программу в командной строке, открывается, считывается и закрывается. Замысловатая предпоследняя строка использует оператор построчного чтения из дескриптора <…> в списочном контексте — как второй параметр встроенной процедуры map. Напомним, что процедура map получает список, выполняет одну и ту же операцию над всеми элементами списка и возвращает новый список, заполненный результатами операции. В нашем случае операция такая: удаление символа конца строки, разбиение строки по разделителям (пробелам) и возврат ссылки на полученный при разбиении массив. Полученный массив ссылок на массивы и будет передаваться прямо в процедуру resistance.

А теперь переходим к программированию процедуры resistance. Первым делом объявляем массивы @I и @U для неизвестных величин — токов и потенциалов. Поскольку эти величины будут выступать в роли переменных в линейных уравнениях, массивы следует заполнить объектами класса Capsule:

Perl
my (@I, @U); for my $i(0..$#_) { $I[$i]=Capsule->new; $U[$_]//=Capsule->new for @{$_[$i]}[0, 1]; }

Этот цикл перебирает номера строк в двумерном массиве, переданном в процедуру как параметр, и содержащем описание схемы. Для каждой строки создаётся капсула для тока. Кроме того, для номеров узлов, встречающихся в первом и втором столбце строки, создаётся по капсуле для потенциала. Важно учитывать, что один и тот же номер узла может появиться в разных строках таблицы. Было бы глупо создавать новую капсулу каждый раз, когда её номер встречается повторно. Именно поэтому мы вместо оператора присваивания = используем оператор условного присваивания //=. Такое присваивание состоится лишь тогда, когда элементу массива $U[$_] ещё не было присвоено никакого значения. Повторные попытки присвоить значение будут проигнорированы, а конструктор Capsule->new даже не будет вызываться.

Пока мы ещё не забыли про уравнение U 0 = 0 , нужно включить его:

Perl
Capsule::equation(1, $U[0], 0);

А теперь задаём уравнения, вытекающие из закона Ома:

Perl
for my $i(0..$#I) { Capsule::equation(-1, $U[$_[$i][0]], 1, $U[$_[$i][1]], -$_[$i][2], $I[$i], 0); }

Выражения $_[$i][0] и $_[$i][1] задают номера узлов, которые соединяются $i-м резистором. Коэффициент $_[$i][2] — сопротивление $i-го резистора, он умножается на $i-й ток. Мы внимательно проследили за знаками коэффициентов при неизвестных величинах в этом уравнении.

Следующий цикл посвящён составлению и решению уравнений для токов, выражающим закон сохранения заряда. Уравнение для $i-го узла будет составлено не сразу, так что подготовим для него массив @equation:

Perl
for my $u(0..$#U) { my @equation=();

Внутренний цикл перебирает резисторы и добавляет в левую часть уравнения только те токи, которые протекают через $u-й узел. При этом вытекающий ток берётся с минусом, а втекающий — с плюсом:

Perl
for my $i(0..$#I) { if($_[$i][0]==$u) { push @equation, -1, $I[$i]; } elsif($_[$i][1]==$u) { push @equation, 1, $I[$i]; } }

Не забываем про дополнительные единичные токи: втекающий в нулевом узле схемы, и вытекающий в первом:

Perl
if($u==0) { push @equation, 1; } elsif($u==1) { push @equation, -1; } else { push @equation, 0; }

К этому моменту левая часть уравнения в массиве @equation полностью укомплектована. Обрабатываем уравнение и завершаем цикл:

Perl
Capsule::equation(@equation); }

Теперь, когда все уравнения заданы, можно поинтересоваться их решением, точнее, искомым значением U 1 . Оно и возвращается из процедуры:

Perl
return $U[1]->value;

Процедура resistance готова, а вместе с ней готова и программа.

Информатика-54© А. Н. Швец