Всю сложную работу по решению уравнения возложим на процедуру
solve
. Процедура получает коэффициенты уравнения и его
правую часть в списке параметров, и возвращает массив с числами, составляющими
решение:
Perlsub solve { … } die "$0: Требуется несколько положительных чисел в командной строке!\n" unless(@ARGV); die "$0: Не могу решить уравнение!\n" unless my @solution=solve(@ARGV); print "@solution\n";
Приступим к программированию процедуры.
Сохраним правую часть уравнения (последний элемент массива
@_
) в переменной $b
. Что касается массива
коэффициентов в левой части, приготовим для их хранения массив
@a
. Эти коэффициенты понадобятся нам лишь в составе
квантификаторов в регулярном выражении, причём уменьшенные на единицу. В таком
виде и отправим их в массив @a
. Массив @x
подготовим для решения; в конце процедуры он будет возвращён:
Perlmy $b=pop @_; my @a=map {$_-1} @_; my @x;
Следующий фрагмент кода посвящён составлению регулярного выражения в переменной
$re
:
Perlmy $re='^'; $re.="(Q*)\\$_\{".$a[$_-1].'}' for 1..@a; $re.='$';
А теперь, собственно, решение уравнения. В случае успешного сопоставления
с регулярным выражением массив @x
следует заполнить
значениями длин переменных $1
, $2
,
и т. д.
Вот это по-настоящему трудное место. Будь фрагменты строки, захваченные
при сопоставлении с регулярным выражением, элементами массива, всё было бы
очень просто: этот массив можно скопировать в массив @x
.
Здесь же нам потребуется перебирать переменные по их именам. Это задача была бы
неразрешима, если бы программы, написанные на Perl, не имели средств
для исследования собственной структуры во время своего исполнения. Такое
явление называется рефлексией, или
интроспекцией. Термин «рефлексия»,
переводящийся как «отражение», в психологии означает способность субъекта
к самопознанию, самоисследованию, самокопанию. Слово «интроспекция» дословно
переводится как «взор, обращённый внутрь». И то, и другое слово очень хорошо
передают суть явления. Интроспективная программа может прямо во время
исполнения узнать свою структуру, в частности, определить список имён своих
переменных и получить доступ к этим переменным по их именам. Точно так же
программа может узнать перечень имён определённых в ней процедур и вызывать их
по именам.
Тут читатель может спросить: что же интересного в том, что программа обращается к переменным и вызывает процедуры по их именам? Разве это не самое обычное явление? Но дело в том, что обращаться нужно не по именам, записанным в тексте программы программистом, а по именам, вычисленным во время исполнения программы.
Вот как это делается:
Perl$hello="HELLO!\n"; print $hello; печатаем значение переменной$hello
$name='hello'; print ${$name}; печатаем значение переменной, чьё имя содержится в переменной$name
Другое средство интроспекции — использование встроенной процедуры
eval
. Она, помимо прочего, может запустить код на языке
Perl, переданный в виде строки в эту процедуру как параметр.
Perleval "print \$$name;";
В этом примере процедуре eval
передаётся строка
print $hello;
(это значение выражения "print
\$$name;"
).
Применим оба этих средства интроспекции для решения нашей частной задачи —
организации перебора переменных $1
,
$2
, …:
Perlif(('Q' x $b)=~m/$re/) { push @x, length ${$_} for 1..@a; }
или
Perlif(('Q' x $b)=~m/$re/) { eval "push @x, length \$$_;" for 1..@a; }
Завершит тело процедуры solve
возврат массива
@x
:
Perlreturn @x;
Примечание | |
---|---|
Интроспекция не является основным приёмом программирования на языке Perl. Оба фрагмента (особенно второй) демонстрируют программирование на грани хулиганства. Однако по-другому в данной задаче никак. Интроспекция — чрезвычайно мощное и при этом чрезвычайно опасное средство. Программы, меняющие свою структуру, свою логику во время исполнения, нужны для реализации искусственного интеллекта. Такие программы можно сделать адаптивными, то есть наделить способностью к приспособлению в меняющихся условиях, подобно вирусу, перестраивающему свой организм для сохранения живучести в нашем сложном мире. В то же время программа, способная исполнять код, неизвестный в момент программирования, может быть по-настоящему опасной. Представьте такую программу dangerous-eval.pl: #!/usr/bin/perl use warnings; print eval shift, "\n"; Она берёт из командной строки параметр — заданный пользователем текст, и затем исполняет его как код на Perl, выводя на печать результат. С помощью такой программы пользователь может делать всё что угодно, даже если она предназначалась, по замыслу программиста, для решения какой-то частной задачи (например, для вычисления арифметических выражений, заданных в командной строке):
Вообще, если данные, введённые в программу пользователем, имеют шанс оказаться
в параметре, передаваемом в |