Яка різниця між "моїм" і "нашим" в Перлі?


188

Я знаю, що myв Perl. Він визначає змінну, яка існує лише в області блоку, в якому вона визначена. Що робить our?

Чим ourвідрізняється від my?

Відповіді:


215

Відмінне запитання: чим ourвідрізняється від myі що робить our?

Підсумок:

Доступний з Perl 5 my- це спосіб декларувати непакетні змінні, які є:

  • приватний
  • нові
  • неглобальний
  • окремо від будь-якого пакету, так що змінна не може бути доступна у вигляді $package_name::variable.


З іншого боку, ourзмінні є змінними пакету, і таким чином автоматично:

  • глобальні змінні
  • точно не приватний
  • не обов’язково нове
  • можна отримати доступ за межами пакету (або лексичної області) з кваліфікованим простором імен, як $package_name::variable.


Оголошення змінної за допомогою ourдозволяє попередньо визначити змінні для того, щоб використовувати їх під тим, щоб use strictне отримувати попередження про помилки друку чи помилки під час компіляції. Починаючи з Perl 5.6, він замінив застаріле use vars, яке було лише зафіксованим файлами, а не лексично розмененим, як є our.

Наприклад, формальне, кваліфіковане ім'я для змінної $xвсередині package mainє $main::x. Декларування our $xдозволяє використовувати голу $xзмінну без штрафних санкцій (тобто без виникаючої помилки) в області декларації, коли сценарій використовує use strictабо use strict "vars". Об'єм може бути одним, або двома, або більше пакетами, або одним невеликим блоком.


2
Тож чим наші відрізняються від місцевих?
Натан Фелман

17
@Nathan Fellman, localне створює змінних. Це не стосується myі ourзовсім. localтимчасово створює резервну копію значення змінної та очищає її поточне значення.
ikegami

1
ourзмінні не є змінними пакетів. Вони не є загальносвітовими, а лексично-охопленими змінними, як і myзмінні. Ви можете бачити , що в наступній програмі: package Foo; our $x = 123; package Bar; say $x;. Якщо ви хочете "оголосити" змінну пакету, вам потрібно скористатися use vars qw( $x );. our $x;оголошує змінну з лексичним діапазоном, яка є псевдонімом однойменної змінної в пакеті, в який ourбуло складено.
ikegami

60

Посилання PerlMonks та PerlDoc від Cartman та Olafur є чудовою орієнтиром - нижче мою тріщину підсумовано:

myзмінні охоплюються лексично в межах одного блоку, визначеного {}або в одному файлі, якщо не в {}s. Вони недоступні через пакети / підпрограми, визначені поза тим же лексичним діапазоном / блоком.

ourзмінні розміщуються в межах пакету / файлу і доступні з будь-якого коду, який useабо requireтой пакет пакету / файлу конфлікту імен вирішуються між пакетами, попередньо додавши відповідну область імен.

Просто для її округлення localзмінні "динамічно" охоплюються, що відрізняється від myзмінних тим, що вони також доступні з підпрограм, викликаних в одному блоці.


+1 для " myзмінні охоплюються лексично [...] в межах одного файлу, якщо не в {}s". Це було корисно для мене, дякую.
Георг

48

Приклад:

use strict;

for (1 .. 2){
    # Both variables are lexically scoped to the block.
    our ($o);  # Belongs to 'main' package.
    my  ($m);  # Does not belong to a package.

    # The variables differ with respect to newness.
    $o ++;
    $m ++;
    print __PACKAGE__, " >> o=$o m=$m\n";  # $m is always 1.

    # The package has changed, but we still have direct,
    # unqualified access to both variables, because the
    # lexical scope has not changed.
    package Fubb;
    print __PACKAGE__, " >> o=$o m=$m\n";
}

# The our() and my() variables differ with respect to privacy.
# We can still access the variable declared with our(), provided
# that we fully qualify its name, but the variable declared
# with my() is unavailable.
print __PACKAGE__, " >> main::o=$main::o\n";  # 2
print __PACKAGE__, " >> main::m=$main::m\n";  # Undefined.

# Attempts to access the variables directly won't compile.
# print __PACKAGE__, " >> o=$o\n";
# print __PACKAGE__, " >> m=$m\n";

# Variables declared with use vars() are like those declared
# with our(): belong to a package; not private; and not new.
# However, their scoping is package-based rather than lexical.
for (1 .. 9){
    use vars qw($uv);
    $uv ++;
}

# Even though we are outside the lexical scope where the
# use vars() variable was declared, we have direct access
# because the package has not changed.
print __PACKAGE__, " >> uv=$uv\n";

# And we can access it from another package.
package Bubb;
print __PACKAGE__, " >> main::uv=$main::uv\n";

11

Впоратися із визначенням масштабу - це хороший огляд правил визначення області Perl. Він досить старий, що ourне обговорюється в тілі тексту. Він розглядається в розділі Примітки наприкінці.

У статті йдеться про змінні пакету та динамічний обсяг, а також про те, чим вони відрізняються від лексичних змінних та лексичних областей.


5

myвикористовується для локальних змінних, тоді ourяк використовується для глобальних змінних.

Більше читання на сайті Variable Scoping в Perl: основи .


16
Будьте обережні, обманюючи слова локальні та глобальні. Власні умови лексичні та пакетні. Ви не можете створити справжні глобальні змінні в Perl, але деякі вже існують, як $ _, і локальна відноситься до змінних пакетів з локалізованими значеннями (створеними локальними), а не до лексичних змінних (створених з моїм).
Час. Оуенс

${^Potato}є глобальним. Він відноситься до тієї ж змінної незалежно від того, де ви її використовуєте.
MJD

5

Я коли-небудь зустрічав деякі підводні камені щодо лексичних декларацій в Perl, які мене збентежили, які також пов'язані з цим питанням, тому я просто додаю тут свій підсумок:

1. Визначення або декларація?

local $var = 42;
print "var: $var\n";

Вихід є var: 42. Однак ми не могли сказати, чи local $var = 42;це визначення чи декларація. Але як щодо цього:

use strict;
use warnings;

local $var = 42;
print "var: $var\n";

Друга програма видасть помилку:

Global symbol "$var" requires explicit package name.

$varне визначено, що означає local $var;просто декларація! Перш ніж використовувати localдля оголошення змінної, переконайтесь, що вона визначена як глобальна змінна раніше.

Але чому це не вийде з ладу?

use strict;
use warnings;

local $a = 42;
print "var: $a\n";

Вихід: var: 42.

Це тому $a, що , як і $b, є глобальною змінною, попередньо визначеною в Perl. Пам'ятаєте функцію сортування ?

2. Лексичні чи глобальні?

Перед початком використання Perl я був програмістом на C, тому поняття лексичних та глобальних змінних мені здається простим: воно просто відповідає автоматичним та зовнішнім змінним у C. Але є невеликі відмінності:

У C зовнішня змінна - це змінна, визначена поза будь-яким функціональним блоком. З іншого боку, автоматична змінна - це змінна, визначена всередині функціонального блоку. Подобається це:

int global;

int main(void) {
    int local;
}

Перебуваючи в Perl, справи тонкі:

sub main {
    $var = 42;
}

&main;

print "var: $var\n";

Вихід є var: 42. $varє глобальною змінною, навіть якщо вона визначена у функціональному блоці! Насправді в Perl будь-яка змінна за замовчуванням оголошується як глобальна.

Урок полягає в тому, щоб завжди додавати use strict; use warnings;на початку програми Perl, що змусить програміста чітко оголосити лексичну змінну, щоб ми не заплутувались через деякі помилки, сприйняті як належне.


Детальніше про ["запам'ятовування [$ a та $ b в] сортування" тут] ( stackoverflow.com/a/26128328/1028230 ). Perl ніколи не перестає мене, гм, дивувати.
ruffin

4

Perldoc має гарне визначення наших.

На відміну від мого, який обидва виділяє сховище для змінної і пов'язує просте ім'я з цим сховищем для використання в поточному діапазоні, ми пов'язуємо просте ім'я зі змінною пакета в поточному пакеті для використання в поточному масштабі. Іншими словами, у нас є ті ж правила розміщення, що і у мене, але не обов'язково створювати змінну.


2

Це лише дещо пов'язане з питанням, але я щойно виявив (мені) незрозумілий біт синтаксису perl, який ви можете використовувати з "нашими" (пакетними) змінними, які ви не можете використовувати з "моїм" (локальним) змінні.

#!/usr/bin/perl

our $foo = "BAR";

print $foo . "\n";
${"foo"} = "BAZ";
print $foo . "\n";

Вихід:

BAR
BAZ

Це не спрацює, якщо ви зміните "наше" на "моє".


1
Не так. $ foo $ {foo} $ {'foo'} $ {"foo"} всі працюють однаково для присвоєння змінних або перенаправлення. Заміняючи наше на вищенаведеному прикладі для мого дійсно. Те, що ви, мабуть, відчували, намагалося перенаправити $ foo як змінну пакету, наприклад $ main :: foo або $ :: foo, яка працюватиме лише для глобальних пакетів, таких як ті, які визначені нашими .
Cosmicnet

Щойно перепробовано, використовуючи v5.20, і він точно не дає однакового результату з моїм (він друкує BAR двічі)
Misha Gale

1
Мій тест (на windows): perl -e "my $foo = 'bar'; print $foo; ${foo} = 'baz'; pr int $foo"output: barbaz perl -e "my $foo = 'bar'; print $foo; ${"foo"} = 'baz'; print $foo"output: barbaz perl -e "my $foo = 'bar'; print $foo; ${\"foo\"} = 'baz'; print $foo"output: barbar Отже, під час мого тестування я потрапив у ту саму пастку. $ {foo} те саме, що і $ foo, дужки корисні при інтерполяції. $ {"foo"} насправді виглядає до $ main :: {}, яка є основною таблицею символів, оскільки така містить лише змінні, розміщені в пакеті.
Cosmicnet

1
$ {"main :: foo"}, $ {":: foo"} і $ main :: foo такі ж, як $ {"foo"}. Стенограма - це чутливі до пакета perl -e "package test; our $foo = 'bar'; print $foo; ${\"foo\"} = 'baz'; print $foo"роботи, оскільки в цьому контексті $ {"foo"} тепер дорівнює $ {"test :: foo"}. Про Symbol Tables and Globs є деяка інформація про нього, як і книга програмування Advanced Perl. Вибачте за мою попередню помилку.
Cosmicnet

0
print "package is: " . __PACKAGE__ . "\n";
our $test = 1;
print "trying to print global var from main package: $test\n";

package Changed;

{
        my $test = 10;
        my $test1 = 11;
        print "trying to print local vars from a closed block: $test, $test1\n";
}

&Check_global;

sub Check_global {
        print "trying to print global var from a function: $test\n";
}
print "package is: " . __PACKAGE__ . "\n";
print "trying to print global var outside the func and from \"Changed\" package:     $test\n";
print "trying to print local var outside the block $test1\n";

Виведе це:

package is: main
trying to print global var from main package: 1
trying to print local vars from a closed block: 10, 11
trying to print global var from a function: 1
package is: Changed
trying to print global var outside the func and from "Changed" package: 1
trying to print local var outside the block 

У разі використання "строгого використання" ця помилка отримає при спробі запуску сценарію:

Global symbol "$test1" requires explicit package name at ./check_global.pl line 24.
Execution of ./check_global.pl aborted due to compilation errors.

Будь ласка, надайте якесь пояснення. Цей демпінговий код рідко вважається доцільним.
Скотт Солмер

простими словами: Наше (як ім'я Саїс) - це змінення декларації для використання цієї змінної з будь-якого місця в сценарії (функція, блок тощо ...), кожна змінна за замовчуванням (у випадку, якщо не оголошено) належать до "головного" пакунок, наша змінна все ще може бути використана навіть після декларування іншого пакета в сценарії. Змінна "my" у випадку, де оголошено в блоці або функції, може використовуватися лише в цьому блоці / функції. у випадку, якщо змінна "моя" була оголошена не закритою в блоці, її можна використовувати будь-де в scriot, в закритому блоці, або у функції як "наша" змінна, але не може бути використана у випадку зміни пакету
Lavi Buchnik

Мій сценарій вище показує, що за замовчуванням ми перебуваємо в "головному" пакеті, тоді сценарій друкує змінну "наш" з "основного" пакету (не закрита в блоці), потім ми оголошуємо дві "мої" змінні у функції та вивести їх із цієї функції. тоді ми друкуємо змінну "наш" з іншої функції, щоб показати, що вона може бути використана у функції. потім ми змінюємо пакунок на "змінено" (не більше "основного") і знову друкуємо змінну "наш". потім спробу надрукувати змінну "мій" поза функцією і не вдалося. сценарій просто показує різницю між "нашим" і "моїм" використанням.
Лаві Бучник

0

Просто спробуйте використати таку програму:

#!/usr/local/bin/perl
use feature ':5.10';
#use warnings;
package a;
{
my $b = 100;
our $a = 10;


print "$a \n";
print "$b \n";
}

package b;

#my $b = 200;
#our $a = 20 ;

print "in package b value of  my b $a::b \n";
print "in package b value of our a  $a::a \n";

Це пояснює різницю між моїм і нашим. Моя змінна не виходить за межі фігурних дужок і збирається сміття, але наша змінна все ще живе.
Югдев

-1
#!/usr/bin/perl -l

use strict;

# if string below commented out, prints 'lol' , if the string enabled, prints 'eeeeeeeee'
#my $lol = 'eeeeeeeeeee' ;
# no errors or warnings at any case, despite of 'strict'

our $lol = eval {$lol} || 'lol' ;

print $lol;

Чи можете ви пояснити, що цей код повинен демонструвати? Чому ourі myрізні? Як це показує цей приклад?
Натан Фелман

-1

Давайте подумаємо, що насправді є інтерпретатором: це фрагмент коду, який зберігає значення в пам'яті та дає вказівки в програмі, що інтерпретує доступ до цих значень за їхніми іменами, які вказані всередині цих інструкцій. Отже, велика робота перекладача - формувати правила того, як ми повинні використовувати імена в цих інструкціях для доступу до значень, які зберігає перекладач.

Зустрічаючи "мій", інтерпретатор створює лексичну змінну: назване значення, до якого інтерпретатор може отримати доступ лише під час виконання блоку, і лише з цього синтаксичного блоку. Зустрічаючи "наше", інтерпретатор робить лексичний псевдонім змінної пакета: він пов'язує ім'я, яке інтерпретатор повинен згодом обробляти як ім'я лексичної змінної, поки блок не буде закінчений, до значення пакета змінна з тим же ім'ям.

Ефект полягає в тому, що ви можете зробити вигляд, що ви використовуєте лексичну змінну і обійти правила "строгого використання" щодо повної кваліфікації змінних пакетів. Оскільки інтерпретатор автоматично створює змінні пакета при їх першому використанні, побічним ефектом використання "наших" може бути також те, що інтерпретатор також створює змінну пакету. У цьому випадку створюються дві речі: змінна пакунка, до якої інтерпретатор може отримати доступ звідусіль, за умови, що вона належним чином позначена так, як цього вимагає "строго використовувати" (попередньо вказуючи назву свого пакета та дві колонки), і його лексичний псевдонім.

Джерела:

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.