ความแตกต่างระหว่าง 'ของฉัน' และ 'ของเรา' ใน Perl คืออะไร?


188

ฉันรู้ว่าสิ่งที่myอยู่ใน Perl มันกำหนดตัวแปรที่มีอยู่เฉพาะในขอบเขตของบล็อกที่มีการกำหนดไว้ อะไรourทำอย่างไร

วิธีการที่ไม่ourแตกต่างจากmy?

คำตอบ:


215

คำถามยอดเยี่ยม: ourแตกต่างจากmyและourทำอย่างไร

สรุป:

มีให้ตั้งแต่ Perl 5 myเป็นวิธีการประกาศตัวแปรที่ไม่ใช่แพ็คเกจซึ่ง ได้แก่ :

  • เอกชน
  • ใหม่
  • ไม่ใช่โลก
  • แยกออกจากแพคเกจใด ๆ เพื่อให้ตัวแปรไม่สามารถ$package_name::variableเข้าถึงได้ในรูปแบบของ


ในทางตรงกันข้ามourตัวแปรเป็นตัวแปรแพ็คเกจและโดยอัตโนมัติ:

  • ตัวแปรทั่วโลก
  • ไม่เป็นส่วนตัวอย่างแน่นอน
  • ไม่จำเป็นต้องใหม่
  • สามารถเข้าถึงได้นอกแพคเกจ (หรือขอบเขตคำศัพท์) กับ namespace $package_name::variableที่ผ่านการรับรองเป็น


การประกาศตัวแปรด้วยourอนุญาตให้คุณประกาศตัวแปรuse strictล่วงหน้าเพื่อใช้งานภายใต้โดยไม่ได้รับคำเตือนที่พิมพ์ผิดหรือข้อผิดพลาดในการคอมไพล์ ตั้งแต่ Perl 5.6 มันได้เข้ามาแทนที่ล้าสมัยuse varsซึ่งเป็นเพียงไฟล์ขอบเขตและไม่กำหนดขอบเขต lexically ourตามที่เป็นอยู่

ยกตัวอย่างเช่นอย่างเป็นทางการชื่อที่มีคุณภาพสำหรับตัวแปร$xภายในคือpackage main $main::xประกาศour $xช่วยให้คุณใช้เปลือย$xตัวแปรโดยไม่มีการลงโทษ (เช่นโดยไม่มีข้อผิดพลาดที่เกิด) อยู่ในขอบเขตของการประกาศเมื่อใช้สคริปต์หรือuse strict use strict "vars"ขอบเขตอาจเป็นแพ็คเกจหนึ่งหรือสองแพคเกจขึ้นไปหรือหนึ่งบล็อคเล็ก


2
แล้วเราแตกต่างจากท้องถิ่นอย่างไร
นาธาน Fellman

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;ประกาศตัวแปรที่กำหนดขอบเขตแบบ lexically ซึ่งเป็นนามแฝงกับตัวแปรที่มีชื่อเดียวกันในแพ็กเกจที่ourคอมไพล์แล้ว
ikegami

60

ลิงก์ PerlMonks และ PerlDoc จาก cartman และ Olafur เป็นข้อมูลอ้างอิงที่ดีมาก - ด้านล่างเป็นข้อสรุปของฉัน:

myตัวแปรถูกกำหนดขอบเขตโดยละเอียดภายในบล็อกเดียวที่กำหนดโดย{}หรือภายในไฟล์เดียวกันหากไม่อยู่ใน{}s ไม่สามารถเข้าถึงได้จากแพ็คเกจ / รูทีนย่อยที่กำหนดไว้ภายนอกขอบเขต / บล็อกของคำศัพท์เดียวกัน

ourตัวแปรถูกกำหนดขอบเขตภายในแพ็กเกจ / ไฟล์และสามารถเข้าถึงได้จากโค้ดใด ๆ ที่useหรือrequireความขัดแย้งของแพ็กเกจ / ไฟล์ - ชื่อถูกแก้ไขระหว่างแพ็กเกจโดยการเพิ่มเนมสเปซที่เหมาะสม

เพียงเพื่อปัดเศษlocalตัวแปรจะถูกกำหนดขอบเขตแบบไดนามิกแตกต่างจากmyตัวแปรที่สามารถเข้าถึงได้จากรูทีนย่อยที่เรียกว่าภายในบล็อกเดียวกัน


+1 สำหรับ " myตัวแปรถูกกำหนดขอบเขตโดยเล็ก [... ] ภายในไฟล์เดียวกันหากไม่อยู่ใน{}s" นั่นเป็นประโยชน์สำหรับฉันขอบคุณ
Georg

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จะไม่กล่าวถึงในเนื้อความของเนื้อหา มันถูกแก้ไขในNotesส่วนท้าย

บทความนี้กล่าวถึงตัวแปรแพ็กเกจและขอบเขตไดนามิกและความแตกต่างจากตัวแปรคำศัพท์และขอบเขตคำศัพท์


5

myใช้สำหรับตัวแปรโลคัลขณะที่ourใช้สำหรับตัวแปรโกลบอล

เพิ่มเติมอ่านมากกว่าที่ตัวแปรกำหนดขอบเขตใน Perl: พื้นฐาน


16
ระมัดระวังในการอ่านคำในท้องถิ่นและทั่วโลก เงื่อนไขที่เหมาะสมเป็นศัพท์และแพคเกจ คุณไม่สามารถสร้างตัวแปรโกลบอลที่แท้จริงใน Perl แต่มีบางอย่างอยู่แล้วเช่น $ _ และ local อ้างถึงตัวแปรแพ็กเกจที่มีค่าการแปล (สร้างโดย local) ไม่ใช่ตัวแปร lexical (สร้างด้วย my)
Chas Owens

${^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. คำศัพท์หรือทั่วโลก?

ฉันเป็นโปรแกรมเมอร์ C ก่อนที่จะเริ่มใช้ Perl ดังนั้นแนวคิดเกี่ยวกับตัวแปรคำศัพท์และทั่วโลกดูเหมือนจะตรงไปตรงมากับฉัน: มันสอดคล้องกับตัวแปร auto และตัวแปรภายนอกใน 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 ไม่เคยหยุดที่จะ, um, ประหลาดใจฉัน
ruffin

4

perldocมีความหมายที่ดีของเรา

ต่างจากฉันซึ่งทั้งสองจัดสรรที่เก็บข้อมูลสำหรับตัวแปรและเชื่อมโยงชื่อแบบง่ายกับที่เก็บข้อมูลนั้นเพื่อใช้ภายในขอบเขตปัจจุบันเราเชื่อมโยงชื่อแบบง่ายกับตัวแปรแพคเกจในแพ็คเกจปัจจุบันเพื่อใช้ภายในขอบเขตปัจจุบัน กล่าวอีกนัยหนึ่งเรามีกฎการกำหนดขอบเขตเดียวกับของฉัน แต่ไม่จำเป็นต้องสร้างตัวแปร


2

สิ่งนี้เกี่ยวข้องกับคำถามเพียงเล็กน้อย แต่ฉันเพิ่งค้นพบไวยากรณ์ perl ที่ไม่ชัดเจนซึ่งคุณสามารถใช้กับตัวแปร "ของเรา" (แพ็คเกจ) ที่คุณไม่สามารถใช้กับ "my" (local) ตัวแปร

#!/usr/bin/perl

our $foo = "BAR";

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

เอาท์พุท:

BAR
BAZ

สิ่งนี้จะไม่ทำงานหากคุณเปลี่ยน 'ของเรา' เป็น 'ของฉัน'


1
ไม่เช่นนั้น $ foo $ {foo} $ {'foo'} $ {"foo"} ทั้งหมดทำงานเหมือนกันสำหรับการกำหนดตัวแปรหรือการยกเลิกการลงทะเบียน การสลับของเราในตัวอย่างด้านบนสำหรับการทำงานของฉัน สิ่งที่คุณอาจมีประสบการณ์ได้พยายามที่จะ dereference $ foo เป็นตัวแปรแพคเกจเช่น $ หลัก :: foo หรือ $ :: foo ซึ่งจะทำงานเฉพาะสำหรับ Globals แพคเกจเช่นที่กำหนดไว้กับเรา
Cosmicnet

เพิ่งทดสอบใหม่โดยใช้ v5.20 และมันก็ไม่ได้ให้ผลลัพธ์เดียวกันกับฉัน (มันพิมพ์ BAR สองครั้ง)
Misha Gale

1
การทดสอบของฉัน (บน windows): perl -e "my $foo = 'bar'; print $foo; ${foo} = 'baz'; pr int $foo"เอาท์พุท: barbaz perl -e "my $foo = 'bar'; print $foo; ${"foo"} = 'baz'; print $foo"เอาท์พุท: barbaz perl -e "my $foo = 'bar'; print $foo; ${\"foo\"} = 'baz'; print $foo"เอาท์พุท: 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มีข้อมูลบางอย่างเกี่ยวกับมันเช่นเดียวกับหนังสือการเขียนโปรแกรม 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.

โปรดให้คำอธิบายบางอย่าง การทุ่มตลาดรหัสเช่นนี้ไม่ค่อยถือว่าเหมาะสม
Scott Solmer

ในคำง่าย ๆ : ของเรา (เป็นชื่อ sais) คือการประกาศตัวแปรที่จะใช้ตัวแปรนั้นจากที่ใด ๆ ในสคริปต์ (ฟังก์ชั่นบล็อก ฯลฯ ... ) ทุกตัวแปรโดยค่าเริ่มต้น (ในกรณีที่ไม่ได้ประกาศ) เป็น "หลัก" แพคเกจตัวแปรของเรายังคงสามารถใช้งานได้แม้หลังจากแพ็กเกจอื่นถูกปฏิเสธในสคริปต์ ตัวแปร "ของฉัน" ในกรณีที่ประกาศในบล็อกหรือฟังก์ชั่นสามารถใช้ในบล็อก / ฟังก์ชั่นนั้นเท่านั้น ในกรณีที่ตัวแปร "ของฉัน" ถูกประกาศว่าไม่ปิดในบล็อกก็สามารถใช้ที่ใดก็ได้ใน scriot ในบล็อกที่ปิดเช่นกันหรือในฟังก์ชั่นเป็นตัวแปร "ของเรา" แต่ไม่สามารถใช้ในกรณีที่มีการเปลี่ยนแปลงแพคเกจ
Lavi Buchnik

สคริปต์ของฉันแสดงให้เห็นว่าโดยค่าเริ่มต้นเราอยู่ในแพ็คเกจ "main" จากนั้นสคริปต์จะพิมพ์ตัวแปร "ของเรา" จากแพ็คเกจ "main" (ไม่ปิดในบล็อก) จากนั้นเราประกาศตัวแปร "my" สองตัวในฟังก์ชันและ พิมพ์จากฟังก์ชันนั้น จากนั้นเราพิมพ์ตัวแปร "ของเรา" จากฟังก์ชั่นอื่นเพื่อแสดงว่าสามารถใช้งานได้ในฟังก์ชั่น จากนั้นเราเปลี่ยนแพคเกจเป็น "เปลี่ยน" (ไม่ใช่ "หลัก" ไม่มาก) และเราพิมพ์ตัวแปร "ของเรา" อีกครั้งสำเร็จ จากนั้นพยายามพิมพ์ตัวแปร "ของฉัน" นอกฟังก์ชั่นและล้มเหลว สคริปต์แสดงความแตกต่างระหว่างการใช้ "ของเรา" และ "ของฉัน"
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";

สิ่งนี้อธิบายความแตกต่างระหว่างฉันกับเรา ตัวแปรของฉันออกนอกขอบเขตที่อยู่ในเครื่องหมายปีกกาและรวบรวมขยะ แต่ตัวแปรของเรายังมีชีวิตอยู่
Yugdev

-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แตกต่างกันอย่างไร ตัวอย่างนี้แสดงให้เห็นอย่างไร
Nathan Fellman

-1

ให้เราคิดว่าผู้แปลคืออะไร: เป็นชิ้นส่วนของรหัสที่เก็บค่าในหน่วยความจำและให้คำแนะนำในโปรแกรมที่ตีความการเข้าถึงค่าเหล่านั้นด้วยชื่อซึ่งระบุไว้ในคำแนะนำเหล่านี้ ดังนั้นงานใหญ่ของล่ามคือจัดทำกฎของวิธีที่เราควรใช้ชื่อในคำแนะนำเหล่านั้นเพื่อเข้าถึงค่าที่ล่ามเก็บ

เมื่อเผชิญหน้ากับ "my" ล่ามจะสร้างตัวแปรศัพท์: ค่าที่มีชื่อที่ล่ามสามารถเข้าถึงได้เฉพาะในขณะที่มันเรียกใช้บล็อกและจากภายในวากยสัมพันธ์บล็อกนั้น ในการเผชิญหน้ากับ "ของเรา" ล่ามจะสร้างนามแฝงศัพท์ของตัวแปรแพ็กเกจ: มันผูกชื่อซึ่งล่ามควรจากนั้นไปประมวลผลเป็นชื่อของตัวแปรศัพท์จนกระทั่งบล็อกเสร็จแล้วถึงค่าของแพคเกจ ตัวแปรที่มีชื่อเดียวกัน

ผลกระทบคือคุณสามารถแสร้งทำเป็นว่าคุณกำลังใช้ตัวแปรศัพท์และข้ามกฎของ 'การใช้ที่เข้มงวด' กับคุณสมบัติทั้งหมดของตัวแปรแพคเกจ เนื่องจากล่ามจะสร้างตัวแปรแพ็กเกจโดยอัตโนมัติเมื่อมีการใช้งานครั้งแรกผลข้างเคียงของการใช้ "ของเรา" อาจเป็นไปได้ว่าล่ามจะสร้างตัวแปรแพ็กเกจด้วยเช่นกัน ในกรณีนี้จะมีการสร้างสองสิ่ง: ตัวแปรแพ็กเกจซึ่งล่ามสามารถเข้าถึงได้จากทุกที่หากมีการกำหนดอย่างถูกต้องตามที่ร้องขอโดย 'ใช้เข้มงวด' (ต่อท้ายด้วยชื่อแพ็คเกจและโคลอนสองตัว) และนามแฝงศัพท์

แหล่งที่มา:

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.