Две основных задачи, которые необходимо решить нашей программе — считывание
описания схемы из файла и нахождение сопротивления. Решению второй из них
посвящается процедура 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])
.
Приступаем к программированию:
Perluse Capsule; sub resistance { … } open my $circuitFile, '<', shift; print resistance(map { chomp; [split /\s+/] } <$circuitFile>), "\n"; close $circuitFile;
В трёх последних строках программы файл, чьё имя передано в программу
в командной строке, открывается, считывается и закрывается. Замысловатая
предпоследняя строка использует оператор построчного чтения из дескриптора
<…> в списочном контексте — как второй параметр встроенной
процедуры map
. Напомним, что процедура
map
получает список, выполняет одну и ту же операцию над
всеми элементами списка и возвращает новый список, заполненный результатами
операции. В нашем случае операция такая: удаление символа конца строки,
разбиение строки по разделителям (пробелам) и возврат ссылки на полученный при
разбиении массив. Полученный массив ссылок на массивы и будет передаваться
прямо в процедуру resistance
.
А теперь переходим к программированию процедуры
resistance
. Первым делом объявляем массивы
@I
и @U
для неизвестных величин — токов
и потенциалов. Поскольку эти величины будут выступать в роли переменных
в линейных уравнениях, массивы следует заполнить объектами класса
Capsule
:
Perlmy (@I, @U); for my $i(0..$#_) { $I[$i]=Capsule->new; $U[$_]//=Capsule->new for @{$_[$i]}[0, 1]; }
Этот цикл перебирает номера строк в двумерном массиве, переданном в процедуру
как параметр, и содержащем описание схемы. Для каждой строки создаётся капсула
для тока. Кроме того, для номеров узлов, встречающихся в первом и втором
столбце строки, создаётся по капсуле для потенциала. Важно учитывать, что один
и тот же номер узла может появиться в разных строках таблицы. Было бы глупо
создавать новую капсулу каждый раз, когда её номер встречается повторно. Именно
поэтому мы вместо оператора присваивания = используем оператор
условного присваивания //=. Такое присваивание состоится лишь
тогда, когда элементу массива $U[$_]
ещё не было присвоено
никакого значения. Повторные попытки присвоить значение будут проигнорированы,
а конструктор Capsule->new
даже не будет вызываться.
Пока мы ещё не забыли про уравнение , нужно включить его:
PerlCapsule::equation(1, $U[0], 0);
А теперь задаём уравнения, вытекающие из закона Ома:
Perlfor 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
:
Perlfor my $u(0..$#U) { my @equation=();
Внутренний цикл перебирает резисторы и добавляет в левую часть уравнения только
те токи, которые протекают через $u
-й узел. При этом
вытекающий ток берётся с минусом, а втекающий — с плюсом:
Perlfor my $i(0..$#I) { if($_[$i][0]==$u) { push @equation, -1, $I[$i]; } elsif($_[$i][1]==$u) { push @equation, 1, $I[$i]; } }
Не забываем про дополнительные единичные токи: втекающий в нулевом узле схемы, и вытекающий в первом:
Perlif($u==0) { push @equation, 1; } elsif($u==1) { push @equation, -1; } else { push @equation, 0; }
К этому моменту левая часть уравнения в массиве @equation
полностью укомплектована. Обрабатываем уравнение и завершаем цикл:
PerlCapsule::equation(@equation); }
Теперь, когда все уравнения заданы, можно поинтересоваться их решением, точнее, искомым значением . Оно и возвращается из процедуры:
Perlreturn $U[1]->value;
Процедура resistance
готова, а вместе с ней готова
и программа.