LINUX.ORG.RU

do «file.pl» и области видимости

 , ,


1

3

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

Но сохранить - это только четверть дела, ведь самое важное - правильно загрузить сохранённое.

Конструкция eval с чтением целиком файла отлично работает, всё ОК. Но у eval есть большой недостаток - он медлительный и лучше его в принципе использовать только для перехвата ошибок без такого дикого треша, как повторная интепретация строк кода прямо в рантайме.

Ну и решил я загрузить файлик конструкцией do «file.pl»

И жестоко обломался, потому что variable scope содержимого файлика почему-то отличается от scope'а вызывающей программы, что по сути конечно немного треш и угар. То есть если даже сказать в основной программе «my $a='hello';» и потом загрузить файлик, содержащий «$a='Hows that';», то в $a, внезапно, останется строка 'hello'.

$ echo -e '#!/usr/bin/perl\n$a="bye";' > a.dat
$ cat a.dat
#!/usr/bin/perl
$a="bye";
$ perl -e 'my $a="hello"; do "a.dat"; print "$a\n";'
hello
$ perl -e 'our $a="hello"; do "a.dat"; print "$a\n";'
bye
$ perl -e 'my $a="hello"; open(FH,"<a.dat"); undef $/; eval <FH>; print "$a\n";'
bye

Вопрос: как сделать include файла на языке perl в программе на языке perl, чтобы variable scope всего включаемого совпадал со scope вызывающей программы? Возможно ли это сделать без eval?

★★★★★

Странно, по описанию это одно и то же (http://perldoc.perl.org/functions/do.html):

do 'stat.pl' is largely like eval `cat stat.pl`;
except that it's more concise, runs no external processes, keeps track of the current filename for error messages, searches the @INC directories, and updates %INC if the file is found.

Можно объявить переменную с помощью our, но это когда знаешь какие переменные.

disarmer ★★★ ()

Оформи файл с хэшем как модуль, экспортируй оттуда всё, что нужно

Gvidon ★★★★ ()
Ответ на: комментарий от disarmer

Я в итоге пока так и сделал, объявил our. Просто странно, что нельзя сделать inline include файла, непременно должна быть какая-то пляска с областями видимости переменных, прямо ну никак без этого нельзя :)

DRVTiny ★★★★★ ()
Ответ на: комментарий от disarmer
[disarmer@desktop ~]$ perl -e 'my $a="hello"; eval `cat a.dat`; print "$a\n";'
bye
[disarmer@desktop ~]$ perl -e 'my $a="hello"; eval {`cat a.dat`}; print "$a\n";'
hello

Хмм

disarmer ★★★ ()
use Storable; # Storable - persistence for Perl data structures
outtaspace ★★★ ()

потому что variable scope содержимого файлика почему-то отличается от scope'а вызывающей программы

«Почему-то». Документацию нонче читать не принято?

It also differs in that code evaluated with «do FILENAME» cannot see lexicals in the enclosing scope;

То есть если даже сказать в основной программе «my $a='hello';» и потом загрузить файлик, содержащий «$a='Hows that';», то в $a, внезапно, останется строка 'hello'

$ cat test1.pl 
#!/usr/bin/perl
do 'test2.pl';
print "$a\n";
$ cat test2.pl 
$a = 'hello';
$ ./test1.pl 
hello

Намёк понятен?

redgremlin ★★★★★ ()
Ответ на: комментарий от disarmer
[disarmer@desktop ~]$ perl -e 'my $a="hello"; require "a.dat"; print "$a\n";'
hello

То, что надо

disarmer ★★★ ()
Ответ на: комментарий от outtaspace

Привязаться к половине cpanа с помощью mojo это несомненно модульно =)

disarmer ★★★ ()
Ответ на: комментарий от disarmer

А что мешает скопипастить этот код? Там всего 1 метод нужен, без ооп-шной обвязки и прочего.

outtaspace ★★★ ()
Ответ на: комментарий от outtaspace

И чем parse лучше стандартного eval'а, которому передаётся всё содержимое считанного файла на интерпретацию?

DRVTiny ★★★★★ ()
Ответ на: комментарий от outtaspace

Аа, это да, почти классический eval, но ТС хочет перезаписывать глобальную область видимости, php-like require =)

disarmer ★★★ ()

If the file is successfully compiled, «do» returns the value of the last expression evaluated.

[tmp]> cat tmp.pl
{ a => 42 }
[tmp]> perl -E '$a = do "tmp.pl"; say $a->{a}'
42
Jini ★★ ()
Ответ на: комментарий от DRVTiny

И чем parse лучше стандартного eval'а, которому передаётся всё содержимое считанного файла на интерпретацию?

Причины использовать это, могут быть разными, у модуля много возможностей.

Конкретно для тебя, не осилившего eval (иначе зачем спрашивать?), это простой API.

Возможность сохранять данные. Да, ты не просил, но фича есть. Быстрое клонирование структур и еще много фич. Это часть батарейки (в этом случае отсутствует дихотомия стандартное/нестандартное).

outtaspace ★★★ ()
Ответ на: комментарий от outtaspace

иначе зачем спрашивать?

parse делает более простые вещи, чем eval, но на Perl, а eval внутренне реализован на Си. Поэтому по идее/по логике особо большой разницы в неадекватности и того, и другого решения быть не должно. Мало того, в свой подключаемый файл я уже на 100% намерен забивать и пользовательские функции обработки полей таблицы, а значит - только eval или do, никакой parse с этим не справится.

Вы когда-нибудь слышали о команде source (она же просто ".") в BASH? Вот мне нужно тоже самое, но на Perl. Да и в большинстве языков программирования банальные инклюды не создают никаких специальных областей видимости переменных, по сути везде инклюд - это просто кусочек кода, вынесенный из основного файла прежде всего для того, чтобы не загромождать код декларациями статичных данных.

DRVTiny ★★★★★ ()
Ответ на: комментарий от Jini

В частном случае работает, но у меня там несколько массивов и один сложный хэш. Это никакой не пакет, по сути просто конфиг основной программы, написанный на Perl.

DRVTiny ★★★★★ ()
Ответ на: комментарий от DRVTiny

Это никакой не пакет, по сути просто конфиг основной программы, написанный на Perl.

Но кто мешает использовать модуль, либо использовать в качестве конфига Storable, json и т.д.?

router ★★★★★ ()
Ответ на: комментарий от DRVTiny

Это никакой не пакет, по сути просто конфиг основной программы, написанный на Perl.

Ну так и вызывай его первой командой, в чём проблема? В чём величие идеи сначала определить переменные, а потом запускать конфиг?

redgremlin ★★★★★ ()
Ответ на: комментарий от redgremlin

Ну так и вызывай его первой командой,

??? Какой командой

DRVTiny ★★★★★ ()
Ответ на: комментарий от DRVTiny

Вы когда-нибудь слышали о команде source (она же просто ".") в BASH?

Стоп. Я понял о чем идет речь. Даже так скажу - понял и меня сильно удивила такая потребность. Вот например у меня есть обработчики системных сигналов (term, die, hup) которые я хочу переиспользовать в скриптах, делаю:

require 'signal_handlers.pl';

Никаких eval мне не нужно в таком случае.

Но! Во всех остальных случаях, я пишу модули, благо в Perl они великолепны. Никаких eval, никаких do, только use или require. Чего и тебе советую.

Еще я привел в качестве примера parse(), из моджо. Там тоже eval, как раз для конфигов подходит, хороший пример модульности. Чтобы хранить сложный код, а не структуры данных: инклуды и юзы.

outtaspace ★★★ ()
Последнее исправление: outtaspace (всего исправлений: 1)
Ответ на: комментарий от DRVTiny

Не используй области, просто фигач как $var = «foo». Не используй strict, warnings. Либо ты профи, либо нубас. А так ты строишь из себя хрен знает кого, а сам нифига не знаешь и не хочешь разбираться. Команды в перле как и везде не имеют телепатического интерфейса, чтобы угадывать, что кодеру надо.

Твое решение это cat + eval

gh0stwizard ★★★★★ ()
Ответ на: комментарий от gh0stwizard

По-моему ты дурак.

Я чётко объяснил, что мне нужно. Если тебе нечего предложить - иди в Ж.

DRVTiny ★★★★★ ()
Ответ на: комментарий от DRVTiny

У тебя там мега программа в 10 тыс. строк? Еще раз, для мелкой фигни использовать my/our нет смысла, т.к. нигде и ничего не пересекается. Если уж делать граммотно, то забудь про модифицирование внутренних переменных через do/eval. В таком случае пишешь или используешь готовый парсер того же ini-like.

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

gh0stwizard ★★★★★ ()
Ответ на: комментарий от DRVTiny

И кстати, откуда ты взял что eval медлительный? На каких операциях? В твоем примере — да, медлительный, но не настолько, чтобы ощущать тормоза. В средних/больших функциях eval/do практически ничего не замедляют. Если в этом случае для тебя критичны 1-5 процентов — значит ты выбрал не тот подход/язык и пиши-ка на сях/плюсах.

gh0stwizard ★★★★★ ()
Ответ на: комментарий от gh0stwizard

Не понимаю, чем use strict-то не устроил?

Я пока людям ничего не отдаю, кроме собственно результата работы программы - готового отчёта.

Насчёт do ты навёл меня на очевидную в принципе мысль о том, что коль скоро do принимает в качестве параметра ПЕРЕМЕННУЮ, то он и делает абсолютно то же самое, что eval, то есть прямо в рантайме интепретирует код.

Отсутствие в Perl возможности подключать файлы не в рантайме, на этапе компиляции, когда фактически выполняется уже код, скомпонованный из нескол ких файлов - полностью вымораживает.

По-моему очевидно, что с точки зрения банального удобства иногда бывает нужно вынести из программы те же статические данные, чтобы они просто не мозолили глаза, снижая читаемость кода.

DRVTiny ★★★★★ ()
Ответ на: комментарий от gh0stwizard

Для Perl есть даже полноценный комплиятор - почему бы не писать на нём эффективный код? Это же не PHP, который изначально безнадёжен.

DRVTiny ★★★★★ ()
Ответ на: комментарий от DRVTiny

Отсутствие в Perl возможности подключать файлы не в рантайме, на этапе компиляции, когда фактически выполняется уже код, скомпонованный из нескол ких файлов - полностью вымораживает.

И тогда бы программы запускались по несколько секунд, здорово. И чем это отличается от рантайма + молчание до завершение всех загрузок/проверок. Нет, ты еще скажи, что файлы и парсинг должны происходить параллельно (на всех 8 ядрах, да?), а разрешение конфликтов телепатическим путем. Это задачу пока нормально ни в одном яп не решили, может сам решишь?

Если тебя что-то не устраивает в перле, то выбирай другой яп. Например, яву, там как раз сначала все грузиться, а потом выполняется.

gh0stwizard ★★★★★ ()
Ответ на: комментарий от DRVTiny

Что? «Компилятор» на перл не превращает код перла в чисто машинный код. Скомпилированная программа на перл этот тот же перл, только запихнутый в виртуальный файл-диск. Все, что попало в подобный «статический» файл: сам перл, модули и ряд других файлов. Все, больше там ничего нет. Программа также как и обычный перл грузит модули из своего виртуального диска, производится также парсинг файлов, проверки и т.д., а также просто запускает указанный ей по умолчанию файл runme.pl. Плюс имеется возможность делать все тоже самое и на внешних дисках, ибо перл как был, так и остался.

И если ты не знаешь в чем профит php и ругаешь его по чем зря, когда кругом масса добротного софта на нем, то это значит, что пора писать свой, идеальный яп.

gh0stwizard ★★★★★ ()
Последнее исправление: gh0stwizard (всего исправлений: 1)
Ответ на: комментарий от gh0stwizard

когда кругом масса добротного софта на нем

system-config-something в RH? Вот уж никогда он мне не казался добротным. Почему-то :)

Веб-софтом предпочитаю не пользоваться, мне милее старые добрые GUI. Помню ещё времена, когда OS/2 в 50Мб умещалась и работала на Pentium I без MMX весьма шустро. Да что там говорить, у меня на Celeron 500 на VirtualPC Warp4 работал вполне сносно даже при фоновой нагрузке. Интернет - зло, а если ещё и большая часть кода интепретируется на стороне сервера, а не браузера, то зло уже вовсе феерическое.

DRVTiny ★★★★★ ()
Ответ на: комментарий от outtaspace

А ниче, что %SIG в глобальном пространстве? И именно поэтому работает require. На самом деле require происходит также в рантайме. Различие от eval, do в том, что последние делают массу полезных (но в данном случае излишних вещей): игра через local с перловыми системными переменными: $@, $! и другими, а также возврат результата выполнения (do) или конечного значения (eval).

Ответ почему do не работает с внешними неглобальными переменными здесь не был дан. В обобщенном виде происходит простая загрузка файла и его парсинг, ровно также как делает require. Чтобы увидеть и поменять значение в главном файле достаточно использовать пространство имен модулей: $main::var = «new value»; Именно на этой логике строится весь перл.

И еще раз, eval и do для файла внутри по сути используют require. Просто есть немного других плюх. В контексте парсинга строки логика слегка меняется.

Это не баг, а фича (с)

gh0stwizard ★★★★★ ()
Ответ на: комментарий от gh0stwizard

Очень странно что этот комент мне посвящен. Как раз все вышеперечисленное мне известно.

outtaspace ★★★ ()
Ответ на: комментарий от gh0stwizard

Эмм... Спасибо.

Надо бы не потерять этот тред и периодически перечитывать.

Прошу извинить меня за грубость, я чего-то на взводе был, но теперь понимаю, что точно был неправ.

DRVTiny ★★★★★ ()
Ответ на: комментарий от DRVTiny

Все в порядке. Критиковать ЯП все умеют :)

Ларри сделал язык очень умным. Поэтому, версия новичка и версия профи для такой задачи весьма одинакова:

$ cat do-test.dat
$var = "hello, world";

$ cat do-test.pl
#!/usr/bin/env perl -W

$var = "hello";

do "do-test.dat";
#require "do-test.dat";

print $var, "\n";

И вот новичек решил, что надо писать все в strict. Но решение довольно тривиально. Для перла 5.16.3 и выше:

$ cat do-test-strict.pl
#!/usr/bin/env perl -W

use strict;
use warnings;

my $var = "hello";

do 'do-test.dat';

print $var, "\n";
# $main::var equals to $::var
$main::var and # shut up annoing only-once
    print $main::var, "\n";

Файл do-test.dat остался тем же. Мануал по теме: http://perldoc.perl.org/perlsub.html#Private-Variables-via-my()

gh0stwizard ★★★★★ ()
Ответ на: комментарий от gh0stwizard

После прочтения мануала, делаем так (можно и our), на вкус и цвет фломастеры...

#!/usr/bin/env perl -W

use strict;
use warnings;

use vars
    '$var', # variable for...
    '$foo', # ...
    '$bar', # bla-bla
;

do "dotest.pm";
print $var, "\n";
gh0stwizard ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.