Страницы

Поиск по вопросам

понедельник, 3 июня 2019 г.

Особенности возврата результата reverse из функции в Perl

Для предисловия делаем тестовый код, который переводит строку в верхний регистр, немного комбинируя способы вызова и возврата из функций:
#!/usr/bin/perl $str="Abc"; $s1=a($str); $s2=b(a($str)); $s3=c($str); $s4=b(c($str)); print "str=$str; a()=$s1; b(a())=$s2; c()=$s3; b(c())=$s4
";
sub b { return $_[0]; } sub a { my($tmp)=$_[0]; return uc($tmp); } sub c { my($tmp)=$_[0]; return $tmp=uc($tmp); }
Результат работы абсолютно предсказуемый:
str=abc; a()=ABC; b(a())=ABC; c()=ABC; b(c())=ABC
А теперь абсолютно то же самое сделаем, что бы развернуть строку задом наперед, используя функцию reverse вместо uc
#!/usr/bin/perl $str="Abc"; $s1=a($str); $s2=b(a($str)); $s3=c($str); $s4=b(c($str)); print "str=$str; a()=$s1; b(a())=$s2; c()=$s3; b(c())=$s4
";
sub b { return $_[0]; } sub a { my($tmp)=$_[0]; return reverse($tmp); } sub c { my($tmp)=$_[0]; return $tmp=reverse($tmp); }
И тут мы получаем абсолютно неожиданный результат:
str=Abc; a()=cbA; b(a())=Abc; c()=cbA; b(c())=cbA
При передаче результата работы функции a, как параметра, в функцию b (которая просто возвращает полученное значение) на выходе мы получаем не перевернутую строку. При этом, практически идентичная функция c ведет себя так, как и было задумано. Собственно вот это присваивание $tmp=reverse($tmp) при return я придумал, когда боролся с неожиданным поведением с возвратом после reverse. Когда первый раз увидел это, ставил печать в функцию b, у нее на входе именно такое значение как она и возвращает и которое собственно напечатано в результате.
UPD: Проверил print b(reverse($str)); возвращает Abc !!!
Добавлю, что такой результат на Perl v5.12.4, на других версиях не проверял.
Вопрос: почему при возврате значения которое вернула reverse в случае прямого его использования поведение ожидаемое, а при передаче этого значения как параметра в другую функцию результат такой, как будто никакого reverse не было выполнено ? Глюк это или фича ?


Ответ

Результат абсолютно ожидаемый и предсказуемый. Функция reverse работает по разному в скалярном и списочном контексте. В списочном контексте она разворачивает список, в скалярном - строку.
А теперь вопрос. Если у нас есть список с одной строки, то что вернет для него reverse? Естественно, саму строку.
При вызове b(a()) перл пытается понять контекст. Функция b возвращает просто свой первый аргумент, а получает на вход - список (она же функция, а аргументы у нее не были специфицированы). Поэтому, функция a должна возвращать список. Отсюда, return возвращает список. А это значит, что контест списочный. В списочном контексте аргумент считается списком и естественно строка стает списком с одним элементом.
В функции c подобного не происходит - там явно виден скалярный контекст.
P.S. код ужасен. Не пишите так. Как минимум используйте use strict; use warnings и my

Комментариев нет:

Отправить комментарий