2011-01-12 1 views
5

実行時にPerlシンボルテーブルからメソッドを削除する必要があります。私はundef &Square::areaを使用してこれを実行しようとしましたが、これは機能を削除しますが、いくつかのトレースが残っています。具体的には、$square->area()が呼び出されたとき、Perlは「未定義サブルーチン& Square :: area」の代わりに「Not a CODE reference」であると文句を言います。なぜこのPerlは "Not a CODE reference?"を生成しますか?

「なぜ重要なのですか?あなたはその機能を削除しました。なぜそれを呼び出すのですか?答えは私がそれを呼んでいないということです、Perlはです。 SquareはRectangleを継承し、継承チェーンはから&Rectangle::areaに渡しますが、メソッドが存在しない場合はSquareをスキップし、Rectangleのarea()に落ちるのではなく、メソッド呼び出しが「Not a CODE reference "

奇妙なことに、& Square :: areaがタイプグロブの割り当て(例:*area = sub {...})で定義されている場合にのみ発生します。標準sub area {}アプローチを使用して関数が定義されている場合、コードは期待どおりに機能します。

また、興味深いことに、全体のグロブを定義解除すると、期待どおりに機能します。サブルーチン自体を定義しないでください。

ここで症状を示し、短い例だし、正しい行動とは対照的である:

#!/usr/bin/env perl 
use strict; 
use warnings; 

# This generates "Not a CODE reference". Why? 
sub howdy; *howdy = sub { "Howdy!\n" }; 
undef &howdy; 
eval { howdy }; 
print [email protected]; 

# Undefined subroutine &main::hi called (as expected) 
sub hi { "Hi!\n" } 
undef &hi; 
eval { hi }; 
print [email protected]; 

# Undefined subroutine &main::hello called (as expected) 
sub hello; *hello = sub { "Hello!\n" }; 
undef *hello; 
eval { hello }; 
print [email protected]; 

更新:私は以来、パッケージ::スタッシュ(感謝@Ether)を使用してこの問題を解決してきたが、私は」なぜそれが最初に起こっているのか、まだ混乱しています。 perldoc perlmodは言う:

package main;

sub Some_package::foo { ... } # &foo defined in Some_package

これは、コンパイル時に型グロブの代入の簡単な表現であり:

BEGIN { *Some_package::foo = sub { ... } }

しかし、それはにISNことが表示されます」 t ちょうどは、関数の定義を解除した後に2つの動作が異なるため、省略されています。私はこれが(1)間違ったドキュメント、(2)perlのバグ、(3)PEBCAKのいずれかのケースかどうかを誰かが教えていただければ幸いです。

+0

あなたは 'undef&Square :: area'がサブルーチンを削除するという考えをどこから得ましたか?攻撃ではなく、ちょうど興味深い。 – Ether

+3

'perldoc -f undef'から。彼らが与えた例は 'undef &mysub;'ですが、修飾名でも動作します。 – KingPong

+1

「あなたが質問するかもしれません。なぜあなたは&Square :: areaを最初に定義しましたか?なぜあなたはスーパークラスに委譲するために&Square :: areaを定義しませんか? –

答えて

7

シンボル表の参照を自分で操作すると、問題が発生する可能性があります。右に出くわすことのないような小さなものがたくさんあるためです。幸いにも、あなたのためにすべての重労働を行うモジュールがあります。Package::Stash - 必要に応じてメソッドadd_package_symbolremove_package_symbolを呼び出してください。

あなたがチェックアウトしたい別の良い方法のインストーラはSub::Installです。これは、たくさんの似た機能を生成したい場合に特に便利です。

sub foo { "foo!\n"} 
sub howdy; *howdy = sub { "Howdy!\n" }; 

undef &howdy; 
eval { howdy }; 
print [email protected]; 

use Data::Dumper; 
no strict 'refs'; 
print Dumper(\%{"main::"}); 

プリント(簡略):

 
    $VAR1 = { 
       'howdy' => *::howdy, 
       'foo' => *::foo, 
    }; 

あなたが見ることができるように

あなたのアプローチが正しくない理由として、のは、コードの参照を削除した後、シンボルテーブルを見てみましょう、 'howdy'スロットはまだ存在しています - 未定義の&howdyは実際には 何か を実行しません。グロブスロット*howdyを明示的に削除する必要があります。

+0

Package :: Stashへのポインタありがとう。私は最後に私が見たときに何とかremove_package_symbolを逃しました。これは、remove_package_globとは対照的に、@areaと$ areaなどを壊すことになりますが、これは役に立つ情報ですが、質問には答えませんが、代わりに「難しい質問です。尋ねる気になる。 – KingPong

+0

@ KingPong:あなたのページをリロードしてください。説明にちょっと時間がかかりました。 :) – Ether

+0

@Esther:あなたが 'undef&foo'をしている場合、あなたがundef'edしていても、それはhowdy()のようなシンボルテーブルに残っていることがわかります。違いは、 'perldoc perlmod'によれば' BOOIN {* foo = sub {..} 'に相当する "sub foo {...}"で宣言したため、おそらくPerlはfoo()がなくなったことを知っているということです。 。}} '。 – KingPong

2

タイプグロブが割り当てられていることが原因です。

CODEシンボルを削除すると、typeglobの残りの部分はまだ残っていますので、howdyを実行しようとすると、CODEのタイプグロブを指します。

関連する問題