2010-08-06 31 views
23

Tôi đã đặt câu hỏi này trước hoặc tìm kiếm và thấy những người khác hỏi - tại sao tôi nhận được cảnh báo "Subroutine mySub được xác định lại tại ../lib/Common.pm line x"? và bạn luôn nhận được câu trả lời bạn đã khai báo phụ hai lần trong cùng một mã. Tôi tạo ra gói thử nghiệm này:Perl - Chương trình con được định nghĩa lại

FILE TOÀN BỘ ---------------

package MyCommonPkg; 

use strict; 

sub thisSubroutineIsNotDefinedAnywhereElse{ 
} 

1; 

FILE TOÀN BỘ -------------- -

và tôi sử dụng gói này từ một kịch bản perl, trong đó sử dụng các gói khác, mà sử dụng gói này cũng có, và tôi nhận được cảnh báo:

chương trình con ThisSubroutineIsNotDefinedAnywhereElse định nghĩa lại tại dòng ../lib/MyCommonPkg.pm 19.

Tôi hứa tôi đã không khai báo phụ này ở bất kỳ nơi nào khác. Vậy điều này có phải do tham chiếu vòng tròn không? Làm thế nào tôi có thể đi theo dõi nguyên nhân của cảnh báo này xuống và sửa chữa?

+5

Bạn có thực sự khai báo 'gói Common.pm' không? Điều đó có vẻ như là một lỗi. – mob

+0

Bạn có xảy ra hai gói có cùng tên không? Điều đó có thể gây ra một xung đột không gian tên. Luôn đặt tên cho các gói của bạn cho tệp mà chúng đang ở (thay thế '/' bằng '::', và loại bỏ '.pm'). Điều này cũng có thể xảy ra nếu bạn có không gian tên * no *, điều này thực sự có nghĩa là bạn đang ở trong 'main'. – Ether

+0

không - tôi đã không khai báo Common.pm. Tôi chỉ đổi tên mọi thứ để có được một ví dụ mã giả lên và đánh máy. Tôi sẽ chỉnh sửa. – user210757

Trả lời

7

Nếu bạn đang sử dụng hệ thống tệp phân biệt chữ hoa chữ thường (Windows và khá thường xuyên OSX) và bạn làm use Common trong một tệp và use common trong một tệp khác, bạn có thể gây ra sự cố như thế này.

+0

tìm kiếm sự cố xảy ra và không thể tìm thấy bất kỳ sự cố nào. rất tốt để biết mặc dù - cảm ơn! – user210757

0

Tôi đã cố sử dụng "gói Common.pm" làm tên gói. Trình biên dịch đã cho tôi lỗi. Rất tốt nhỉ? Bạn đang sử dụng phiên bản Perl nào? Tôi đã thử nó trên 5.10.0 và 5.12.1.

Thậm chí nếu bạn có thể biên dịch thì thực hành tốt là xóa tệp .pm. Ví dụ;

Tệp: some_package.pm;

package some_package; 
use strict; 

sub yadayadayada { ... } 

1; 
+0

5.10.1 MS-win32-x86-mulithread – user210757

31

Bạn có vòng lặp phụ thuộc không? Nếu Perl bắt đầu biên dịch tập lệnh của bạn và gặp một dòng như sau:

use PackageA; 

Perl tạm dừng việc biên soạn tập lệnh của bạn; đặt PackageA.pm và bắt đầu biên dịch nó. Nếu nó gặp phải một dòng như sau:

use PackageB; 

Perl tạm dừng việc biên soạn PackageA; đặt PackageB.pm và bắt đầu biên dịch nó. Thông thường, điều đó sẽ hoàn thành thành công và Perl sẽ quay trở lại để hoàn thành việc biên dịch PackageA và khi hoàn tất thành công nó sẽ quay trở lại để biên dịch tập lệnh của bạn và khi hoàn thành thành công nó sẽ bắt đầu thực thi các mã được biên dịch.

Tuy nhiên, nếu PackageB.pm chứa dòng này:

use PackageA; 

Bạn có thể mong đợi nó sẽ không làm gì kể từ Perl đã xử lý PackageA.pm nhưng vấn đề là nó vẫn chưa kết thúc. Vì vậy, Perl sẽ tạm dừng việc biên dịch PackageB và bắt đầu biên dịch lại PackageA.pm ngay từ đầu. Điều đó có thể kích hoạt thông báo bạn đang thấy về các chương trình con trong PackageA được định nghĩa lại.

Theo nguyên tắc chung, hai gói không được phụ thuộc lẫn nhau. Tuy nhiên, đôi khi vòng lặp khó xác định hơn vì nó được gây ra bởi gói thứ ba.

+2

tôi nghĩ rằng đây là vấn đề của tôi - làm thế nào để sửa chữa sau đó? nó không phải là một tham chiếu vòng tròn - tôi sẽ thấy một tham chiếu vòng tròn như PackageA đã "sử dụng PackageB;" và PackageB đã "sử dụng PackageA;" Thay vào đó tôi có một kịch bản "main.pl" sử dụng PackageA và sử dụng PackageB, và PackageA cũng sử dụng PackageB. Không nên có gì sai với điều đó sao? – user210757

+0

Đúng, không có gì sai với điều đó. –

2

Bạn có cơ hội chạy điều này dưới dạng cgi-script trên máy chủ web không?

Tôi thấy tôi cần phải khởi động lại máy chủ web để tránh xung đột này.

+1

không, chỉ cần chạy từ dòng lệnh – user210757

0

Hãy chắc chắn rằng bạn không quên dòng này vào cuối của module của bạn:

1;

Tôi biết nó được đưa vào một vài trong số các ví dụ ở đây, nhưng tôi đề cập đến nó bởi vì nó rất dễ dàng để bỏ qua, và trong trường hợp của tôi nó trở thành nguyên nhân duy nhất của các lỗi!

19

Khi bạn có hai chương trình con có cùng tên trong các gói khác nhau, bạn nên xem cảnh báo này (khi cảnh báo được bật) dưới dạng "Chương trình con mới được định nghĩa lại ....". Lý do đơn giản (rất gần với những gì Grant McLean nói, nhưng vẫn không chính xác) là bạn phải nhận được gói của bạn bỏ qua giai đoạn biên dịch và sau đó yêu cầu. Bằng cách này, trình quản lý không gian tên Perl sẽ không tìm thấy bất kỳ biểu tượng xung đột nào có cùng tên tại thời gian biên dịch và nếu các mô-đun của bạn không có bất kỳ lỗi nào, chúng sẽ hoạt động tốt sau đó.

Chỉ cần chắc chắn rằng bạn thực hiện

đòi hỏi Mô-đun;

tuyên bố hơn

sử dụng mô-đun;

Bạn sẽ không thấy cảnh báo này nữa.

+0

cảm ơn, điều này đã giải quyết được sự cố của tôi – Thariama

+0

Cảm ơn bạn. Đây là thông tin khá hữu ích –

4

Điều này nghe có vẻ giống như sự cố do phụ thuộc vòng tròn gây ra. Đây là cách để theo dõi nó xuống. Nếu lớp vấn đề của bạn trông như thế này:

package AlientPlanet; 
use Dinosaurs; 
sub has_dinosaurs {...} 
1; 

Sau đó thay đổi ví dụ của bạn trông như thế này:

package AlienPlanet; 
sub has_dinosaurs {...}  # <-- swap 
use Dinosaurs;    # <-- swap 
1; 

Bây giờ biên dịch mã của bạn với Carp::Always như thế này:

⚡ perl -MCarp::Always -c lib/AlienPlanet.pm                            
Subroutine has_dinosaurs redefined at lib/AlienPlanet.pm line 4. 
    require AlienPlanet.pm called at lib/Dinosaurs.pm line 4 
    Dinosaurs::BEGIN() called at lib/AlienPlanet.pm line 4 
    eval {...} called at lib/AlienPlanet.pm line 4 
    require Dinosaurs.pm called at lib/AlienPlanet.pm line 5 
    AlienPlanet::BEGIN() called at lib/AlienPlanet.pm line 4 
    eval {...} called at lib/AlienPlanet.pm line 4 
lib/AlienPlanet.pm syntax OK 

Bây giờ bạn có một stacktrace bạn có thể thấy nơi vòng lặp được. Các giải pháp nhanh chóng và bẩn là sử dụng Class::Load trong Dinosaurs.pm.

Để có giải thích chi tiết hơn hãy thử blog post của tôi.

1

Hãy xem chương trình package MyCommonPkg.pm và xem nội dung nó nói. Liệu nó có cái gì như thế này?

package MyCommonPkg; 

use Exporter qw(import); # Might be "require" and not "use" 
our @EXPORT = qw(thisSubroutineIsNotDefinedAnywhereElse); 

Cú pháp có thể khác đôi chút. Những điều chính bạn sẽ thấy là tuyên bố package, rằng nó sử dụng Exporter và mảng @EXPORT có tên chương trình con của bạn trong đó.

Điều đang xảy ra là xung đột không gian tên. Gói của bạn đang xác định cùng một chương trình con mà bạn đang xác định.

Để ngăn chặn điều này xảy ra, Perl sử dụng các không gian tên. Theo mặc định, không gian tên của bạn là main. Tuy nhiên, các gói được giả định để xác định các tên riêng của chúng bằng cách sử dụng lệnh package.

Không gian tên đầy đủ của một chương trình con hoặc biến là không gian tên được theo sau bởi dấu hai chấm, theo sau là tên chương trình con hoặc tên biến. Ví dụ: bạn xem File::Find, bạn sẽ thấy tham chiếu đến các biến $File::Find::name$File::Find::dir. Đây là các biến số $name$dir bên trong gói File/Find.pm trong không gian tên File::Find.

Để giúp mọi thứ dễ dàng hơn cho bạn, gói có thể xuất biến và chương trình con của chúng vào không gian tên chính của bạn. Ví dụ, nếu tôi sử dụng File::Copy, O có thể làm điều này:

... 
use File::Copy 
... 
copy ($file, $to_dir); 

Thay vì:

... 
use File::Copy 
... 
File::Copy::copy ($file, $to_dir); 

Nếu bạn nhìn vào File/Copy.pm, bạn sẽ thấy như sau:

package File::Copy; 
... 
our(@ISA, @EXPORT, @EXPORT_OK, $VERSION, $Too_Big, $Syscopy_is_copy); 
... 
require Exporter; 
@ISA = qw(Exporter); 
@EXPORT = qw(copy move); 

Các package File::Copy; xác định không gian tên. require Exporter;@ISA = qw(Exporter) cho phép gói xuất các chương trình con và các biến vào không gian tên chính chính. @EXPORT tự động, không có thông báo cho bạn bất kỳ điều gì, hãy nhập các tiểu trình copymove vào các không gian tên chínhcho dù bạn có muốn hay không!

Điều cuối cùng là rất quan trọng. Hiện được coi là cách cư xử xấu để sử dụng @EXPORT. Thay vào đó, bạn nên sử dụng @EXPORT_OK yêu cầu bạn liệt kê các chương trình con mà bạn muốn sử dụng. Các gói hiện đại hơn như Scalar::Util làm điều này.

Rất nhiều thứ. Trước tiên, số MyCommonPkg của bạn có tuyên bố package MyCommonPkg;. Nếu không, nó nên. Điều này giúp các gói con và các biến không ảnh hưởng đến chương trình của bạn theo những cách khó chịu. Sau đó, bạn có thể sử dụng @EXPORT hoặc @EXPORT_OK.

Nếu MyCommonPkg có tuyên bố package, có sử dụng @EXPORT không? Nếu có, bạn có một số cách để tránh vấn đề này:

  • Bỏ qua cảnh báo. Nó chỉ là một cảnh báo. Vì bạn biết bạn đang xác định lại chương trình con, và bạn muốn sử dụng định nghĩa của chương trình con, hãy bỏ qua nó.

Bạn có thể làm điều này để tắt các cảnh báo khi bạn xác định lại chương trình con:

use MyCommonPkg; 

no warnings qw(redefine); 
sub thisSubroutineIsNotDefinedAnywhereElse { 
    ... 
} 
use warnings qw(redefine); 
  • Sử dụng require MyCommonPkg; thay vì use MyCommonPkg;.Điều này sẽ ngăn việc nhập mọi chương trình con hoặc biến số vào không gian tên của bạn bao gồm cả những thứ bạn muốn sử dụng. Giả sử MyCommonPkg xác định bốn chương trình con: thisSubroutineIsNotDefinedAnywhereElse, foo, barbarfoo. Để sử dụng bất kỳ chương trình con nào.

Bạn cần phải làm điều này:

my $answer = MyCommonPkg::foo($input); 

Không vui vẻ.

  • Sử dụng tên khác cho chương trình con của bạn. Cần phải có tài liệu rằng chương trình con này được định nghĩa trong MyCommonPkg và nếu bạn muốn sử dụng MyCommonPkg, bạn không nên sử dụng các tên chương trình con được xuất.

  • Cuối cùng, nếu MyCommonPkg là khá mới, và không được sử dụng trong hàng chục chương trình, sử dụng @EXPORT_OK thay vì @EXPORT, và chắc chắn rằng tất cả các chương trình sử dụng MyCommonPkg được sửa đổi để xuất khẩu các chương trình con họ muốn:

như thế này:

use MyCommonPkg qw(foo bar); 

Trong trường hợp này, chỉ có thủ tục con foobar được xuất khẩu. Các chương trình con thisSubroutineIsNotDefinedAnywhereElsebarfoo không được xuất vào môi trường của bạn.

0

Tôi đã gặp vấn đề tương tự; Đó là vì chương trình đã sử dụng một mô-đun và chương trình con đã có mặt cả trong chương trình và trong mô-đun perl;

Các vấn đề liên quan