Sự khác biệt giữa 'của tôi' và 'của chúng tôi' trong Perl là gì?


188

Tôi biết những gì mytrong Perl. Nó định nghĩa một biến chỉ tồn tại trong phạm vi của khối mà nó được xác định. Không gì ourlàm gì?

Làm thế nào ourkhác nhau my?

Câu trả lời:


215

Câu hỏi tuyệt vời: Làm thế nào ourkhác với myvà làm gìour làm gì?

Tóm tắt:

Có sẵn kể từ Perl 5, mylà một cách để khai báo các biến không phải là gói, đó là:

  • riêng tư
  • Mới
  • phi toàn cầu
  • tách biệt với bất kỳ gói nào, để biến không thể được truy cập dưới dạng $package_name::variable.


Mặt khác, ourcác biến là các biến gói và do đó tự động:

  • biến toàn cầu
  • chắc chắn không riêng tư
  • không nhất thiết phải mới
  • có thể được truy cập bên ngoài gói (hoặc phạm vi từ vựng) với không gian tên đủ điều kiện, như $package_name::variable.


Khai báo một biến với ourcho phép bạn khai báo trước các biến để sử dụng chúng theo use strictmà không nhận được cảnh báo lỗi chính tả hoặc lỗi thời gian biên dịch. Kể từ Perl 5.6, nó đã thay thế lỗi thời use vars, chỉ có phạm vi tệp và không có phạm vi từ vựng như hiện tại our.

Ví dụ, tên chính thức, đủ điều kiện cho biến $xbên trong package main$main::x. Khai báo our $xcho phép bạn sử dụng $xbiến trần mà không bị phạt (nghĩa là không có lỗi dẫn đến), trong phạm vi khai báo, khi tập lệnh sử dụng use stricthoặc use strict "vars". Phạm vi có thể là một, hoặc hai hoặc nhiều gói hoặc một khối nhỏ.


2
Vậy chúng ta khác với địa phương như thế nào?
Nathan Fellman

17
@Nathan Fellman, localkhông tạo biến. Nó không liên quan đến myourtất cả. localtạm thời sao lưu giá trị của biến và xóa giá trị hiện tại của nó.
ikegami

1
ourbiến không phải là biến gói. Chúng không có phạm vi toàn cầu, nhưng các biến có phạm vi từ vựng giống như mycác biến. Bạn có thể thấy rằng trong chương trình sau : package Foo; our $x = 123; package Bar; say $x;. Nếu bạn muốn "khai báo" một biến gói, bạn cần sử dụng use vars qw( $x );. our $x;khai báo một biến có phạm vi từ vựng được đặt bí danh cho biến cùng tên trong gói trong đó gói ourđược biên dịch.
ikegami

60

Các liên kết PerlMonks và PerlDoc từ cartman và Olafur là một tài liệu tham khảo tuyệt vời - dưới đây là bản tóm tắt của tôi:

mycác biến được phạm vi từ vựng trong một khối duy nhất được xác định bởi {}hoặc trong cùng một tệp nếu không tính bằng {}s. Chúng không thể truy cập được từ các gói / chương trình con được xác định bên ngoài cùng phạm vi / khối từ vựng.

ourcác biến được đặt trong phạm vi một gói / tệp và có thể truy cập được từ bất kỳ mã nào usehoặc requirexung đột tên / tệp đó được giải quyết giữa các gói bằng cách thêm vào không gian tên thích hợp.

Chỉ cần làm tròn nó ra, localcác biến có phạm vi "động", khác với mycác biến ở chỗ chúng cũng có thể truy cập được từ các chương trình con được gọi trong cùng một khối.


+1 cho " mycác biến được đặt trong phạm vi từ vựng [...] trong cùng một tệp nếu không tính bằng {}s". Điều đó rất hữu ích cho tôi, cảm ơn.
Georg

48

Một ví dụ:

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

Đối phó với Scoping là một tổng quan tốt về các quy tắc phạm vi Perl. Nó đủ cũ mà ourkhông được thảo luận trong phần thân của văn bản. Nó được giải quyết trong phần Ghi chú ở cuối.

Bài báo nói về các biến gói và phạm vi động và cách khác với các biến từ vựng và phạm vi từ vựng.


5

myđược sử dụng cho các biến cục bộ, trong khi ourđược sử dụng cho các biến toàn cục.

Đọc thêm tại Biến phạm vi trong Perl: những điều cơ bản .


16
Hãy cẩn thận để ném xung quanh các từ địa phương và toàn cầu. Các điều khoản thích hợp là từ vựng và gói. Bạn không thể tạo các biến toàn cục thực sự trong Perl, nhưng một số đã tồn tại như $ _ và cục bộ đề cập đến các biến gói với các giá trị cục bộ (được tạo bởi cục bộ), không phải là các biến từ vựng (được tạo bằng của tôi).
Chas. Owens

${^Potato}là toàn cầu. Nó đề cập đến cùng một biến bất kể bạn sử dụng nó ở đâu.
MJD

5

Tôi đã từng gặp một số cạm bẫy về các tuyên bố từ vựng trong Perl đã làm tôi bối rối, điều này cũng liên quan đến câu hỏi này, vì vậy tôi chỉ cần thêm tóm tắt của tôi ở đây:

1. Định nghĩa hay khai báo?

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

Đầu ra là var: 42. Tuy nhiên, chúng tôi không thể biết nếu local $var = 42;là một định nghĩa hoặc tuyên bố. Nhưng làm thế nào về điều này:

use strict;
use warnings;

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

Chương trình thứ hai sẽ đưa ra một lỗi:

Global symbol "$var" requires explicit package name.

$varkhông được xác định, có nghĩa local $var;chỉ là một tuyên bố! Trước khi sử dụng localđể khai báo một biến, hãy đảm bảo rằng nó được định nghĩa là biến toàn cục trước đó.

Nhưng tại sao điều này sẽ không thất bại?

use strict;
use warnings;

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

Đầu ra là : var: 42.

Đó là bởi vì $a, cũng như $b, là một biến toàn cục được xác định trước trong Perl. Ghi nhớ chức năng sắp xếp ?

2. Từ điển hay toàn cầu?

Tôi là một lập trình viên C trước khi bắt đầu sử dụng Perl, vì vậy khái niệm về các biến từ vựng và toàn cầu có vẻ đơn giản với tôi: nó chỉ tương ứng với các biến tự động và biến ngoài trong C. Nhưng có một số khác biệt nhỏ:

Trong C, một biến ngoài là một biến được định nghĩa bên ngoài bất kỳ khối chức năng nào. Mặt khác, một biến tự động là một biến được xác định bên trong một khối chức năng. Như thế này:

int global;

int main(void) {
    int local;
}

Trong khi ở Perl, mọi thứ thật tinh tế:

sub main {
    $var = 42;
}

&main;

print "var: $var\n";

Đầu ra là var: 42. $varlà một biến toàn cục ngay cả khi nó được xác định trong một khối chức năng! Trên thực tế trong Perl, bất kỳ biến nào được khai báo là toàn cục theo mặc định.

Bài học là luôn luôn bổ sung use strict; use warnings;vào đầu chương trình Perl, điều này sẽ buộc lập trình viên phải khai báo biến từ vựng một cách rõ ràng, để chúng ta không bị nhầm lẫn bởi một số sai lầm đã xảy ra.


Thông tin thêm về ["ghi nhớ [$ a và $ b in] sort" tại đây] ( stackoverflow.com/a/26128328/1028230 ). Perl không bao giờ hết, ừm, làm tôi kinh ngạc.
ruffin

4

Các perldoc có một định nghĩa tốt của chúng tôi.

Không giống như tôi, cả hai phân bổ lưu trữ cho một biến và liên kết một tên đơn giản với lưu trữ đó để sử dụng trong phạm vi hiện tại, chúng tôi liên kết một tên đơn giản với một biến gói trong gói hiện tại, để sử dụng trong phạm vi hiện tại. Nói cách khác, chúng tôi có các quy tắc phạm vi giống như của tôi, nhưng không nhất thiết phải tạo một biến.


2

Điều này chỉ liên quan đến câu hỏi, nhưng tôi vừa phát hiện ra một cú pháp tối nghĩa (với tôi) tối nghĩa mà bạn có thể sử dụng với các biến "của chúng tôi" (gói) mà bạn không thể sử dụng với "của tôi" (cục bộ) biến.

#!/usr/bin/perl

our $foo = "BAR";

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

Đầu ra:

BAR
BAZ

Điều này sẽ không hoạt động nếu bạn thay đổi 'của chúng tôi' thành 'của tôi'.


1
Không phải vậy. $ foo $ {foo} $ {'foo'} $ {"foo"} tất cả đều hoạt động giống nhau cho phép gán biến hoặc hội thảo. Trao đổi của chúng tôi trong ví dụ trên cho tôi làm việc. Những gì bạn có thể đã trải qua là cố gắng lấy lại $ foo như một biến gói, chẳng hạn như $ main :: foo hoặc $ :: foo, sẽ chỉ hoạt động cho các gói toàn cầu, chẳng hạn như các biến được định nghĩa với chúng ta .
Vũ trụ

Chỉ cần kiểm tra lại bằng v5.20 và chắc chắn nó không cho cùng một đầu ra với (tôi in BAR hai lần.)
Misha Gale

1
Thử nghiệm của tôi (trên 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 Vì vậy, trong thử nghiệm của tôi, tôi đã rơi vào cùng một cái bẫy. $ {foo} giống như $ foo, dấu ngoặc hữu ích khi nội suy. $ {"foo"} thực sự là một tra cứu tới $ main :: {} là bảng ký hiệu chính, vì như vậy chỉ chứa các biến trong phạm vi gói.
Cosmicnet

1
$ {"main :: foo"}, $ {":: foo"} và $ main :: foo giống như $ {"foo"}. Tốc ký là gói perl -e "package test; our $foo = 'bar'; print $foo; ${\"foo\"} = 'baz'; print $foo"công việc nhạy cảm , vì trong bối cảnh này $ {"foo"} hiện bằng $ {"test :: foo"}. Of Symbol Bảng và Globs có một số thông tin về nó, cũng như cuốn sách lập trình Advanced Perl. Xin lỗi vì sai lầm trước đây của tôi.
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";

Sẽ xuất ra điều này:

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 

Trong trường hợp sử dụng "sử dụng nghiêm ngặt" sẽ gặp lỗi này trong khi cố gắng chạy tập lệnh:

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

Vui lòng cung cấp một số loại giải thích. Mã bán phá giá như thế này hiếm khi được coi là thích hợp.
Scott Solmer

nói một cách đơn giản: Của chúng tôi (như tên sais) là một từ chối biến để sử dụng biến đó từ bất kỳ vị trí nào trong tập lệnh (hàm, khối, v.v.), mọi biến theo mặc định (trong trường hợp không được khai báo) thuộc về "chính" gói, biến của chúng tôi vẫn có thể được sử dụng ngay cả sau khi từ chối gói khác trong tập lệnh. Biến "của tôi" trong trường hợp được khai báo trong một khối hoặc hàm, chỉ có thể được sử dụng trong khối / hàm đó. trong trường hợp biến "của tôi" được khai báo không được đóng trong một khối, nó có thể được sử dụng ở bất kỳ vị trí nào trong scriot, trong một khối kín hoặc trong một hàm là biến "của chúng tôi", nhưng không thể sử dụng trong trường hợp gói thay đổi
Lavi Buchnik

Tập lệnh của tôi ở trên cho thấy theo mặc định, chúng tôi đang ở trong gói "chính", sau đó tập lệnh in một biến "của chúng tôi" từ gói "chính" (không được đóng trong một khối), sau đó chúng tôi khai báo hai biến "của tôi" trong một hàm và in chúng từ chức năng đó. sau đó chúng tôi in một biến "của chúng tôi" từ một chức năng khác để cho thấy nó có thể được sử dụng trong một chức năng. sau đó chúng tôi thay đổi gói thành "đã thay đổi" (không phải "chính" nữa) và chúng tôi in lại biến "của chúng tôi" thành công. sau đó cố gắng in một biến "của tôi" bên ngoài hàm và không thành công. kịch bản chỉ cho thấy sự khác biệt giữa cách sử dụng "của chúng tôi" và "của tôi".
Lavi Buchnik

0

Chỉ cần cố gắng sử dụng chương trình sau đây:

#!/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";

Điều này giải thích sự khác biệt giữa tôi và chúng tôi. Biến của tôi đi ra khỏi phạm vi bên ngoài các dấu ngoặc nhọn và là rác được thu thập nhưng biến của chúng tôi vẫn sống.
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;

Bạn có thể giải thích những gì mã này có nghĩa là để chứng minh? Tại sao ourmykhác nhau? Làm thế nào để ví dụ này cho thấy nó?
Nathan Fellman

-1

Chúng ta hãy nghĩ trình thông dịch thực sự là gì: đó là một đoạn mã lưu trữ các giá trị trong bộ nhớ và cho phép các hướng dẫn trong một chương trình mà nó diễn giải truy cập các giá trị đó bằng tên của chúng, được chỉ định trong các hướng dẫn này. Vì vậy, công việc lớn của một thông dịch viên là định hình các quy tắc về cách chúng ta nên sử dụng tên trong các hướng dẫn đó để truy cập các giá trị mà trình thông dịch lưu trữ.

Khi gặp "của tôi", trình thông dịch tạo ra một biến từ vựng: một giá trị được đặt tên mà trình thông dịch chỉ có thể truy cập trong khi nó thực thi một khối và chỉ từ trong khối cú pháp đó. Khi gặp "của chúng tôi", trình thông dịch tạo một bí danh từ vựng của biến gói: nó liên kết một tên, mà trình thông dịch được cho là từ đó trở thành xử lý như một tên biến từ vựng, cho đến khi khối kết thúc, với giá trị của gói biến có cùng tên.

Hiệu quả là sau đó bạn có thể giả vờ rằng bạn đang sử dụng một biến từ vựng và bỏ qua các quy tắc 'sử dụng nghiêm ngặt' về trình độ đầy đủ của các biến gói. Do trình thông dịch tự động tạo các biến gói khi chúng được sử dụng lần đầu tiên, nên tác dụng phụ của việc sử dụng "của chúng tôi" cũng có thể là trình thông dịch cũng tạo ra một biến gói. Trong trường hợp này, hai thứ được tạo ra: một biến gói, mà trình thông dịch có thể truy cập từ mọi nơi, miễn là nó được chỉ định đúng theo yêu cầu của 'userict' (được đặt trước tên của gói và hai dấu hai chấm) bí danh từ vựng của nó.

Nguồn:

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.