免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 4746 | 回复: 12
打印 上一主题 下一主题

Perl引用相关FAQ(共10条) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-01-03 18:28 |只看该作者 |倒序浏览
Question: How to force one variable to be an alias to another?
For example,

my $x; my $y;
make_alias( $x, $y );
$x = 5;
print $y; # prints 5


Answer:

One way is to use the Lexical::Alias module:

use Lexical::Alias;
my( $this, $that );
alias $that, $this; # $this is now an alias for $that.


Here, $this is made to be an alias of $that. Whatever $this contained prior to the alias call (e.g. the 2 in the above example) is released, much the same as if $this had simply gone out of scope. (That is, its ref count is decremented.) Similarly, the reference count of whatever $that contains is incremented by this operation.

You can also create aliases for array and hash variables this way:

alias @x, @y;
alias %x, %y;


Note that this technique specifically only works for lexical variables; it does not work for package variables or other globals, such as array elements and hash values.

Another, similar approach is to use Tie::Alias. It has the advantage that it's pure Perl, but it's both slower and (currently) only works for scalars.

use Tie::Alias;
my( $this, $that );
tie $this, 'Tie::Alias', \$that; # $this is now an alias for $that.

论坛徽章:
0
2 [报告]
发表于 2010-01-03 18:30 |只看该作者
Question: How to compare content of two references for equality?

Answer1:

Data::Compare:

use Data::Compare;
print "same" if Compare( $ref1, $ref2 );

But be aware that Data::Compare may not necessarily do the right thing in the rare case that you try to compare an object which overloads the stringify or numify operations.

Answer2:

Struct::Compare:

use Struct::Compare;
print "same" if compare( $ref1, $ref2 );

论坛徽章:
0
3 [报告]
发表于 2010-01-03 18:35 |只看该作者
Question: How do I determine the type of a reference?

Is there a way to determine whether a variable ($x) is a reference, and if so, what type of thing it refers to?

For example, I want to do something like:

if ( is_not_a_reference($x) ) {
   print $x;
} elsif ( is_a_hash_ref($x) ) {
   process_hash( %$x );
} else
   { . . . }

Answer1:

Use the ref function. It returns false if its argument is not a reference, and otherwise returns a string describing the referenced object. For example, in the case of an unblessed hash, it returns the string "HASH".

Answer2:

The Scalar::Util module provides a function reftype for this. It's somewhat more robust than the built-in ref function.

use Scalar::Util 'reftype';
my $reftype = reftype $x;
if ( !defined $reftype ) { print "\$x is not a reference.\n"; }
elsif ( $reftype eq 'HASH' ) { # do something with %$x }
elsif ( $reftype eq 'ARRAY' ) { # do something with @$x }
elsif ( $reftype eq 'SCALAR' ) { # do something with $$x }
else { # do something else } # ...


Answer3:

if ( ! ref($x) )
{ # Not a reference }
elsif ( UNIVERSAL::isa($x,'HASH') ) { # Reference to a hash }
elsif ( UNIVERSAL::isa($x,'ARRAY') ) { # Reference to an array }
elsif ( UNIVERSAL::isa($x,'SCALAR') || UNIVERSAL::isa($x,'REF') ) { # Reference to a scalar }
elsif ( UNIVERSAL::isa($x,'CODE') ) { # Reference to a subroutine }

[ 本帖最后由 兰花仙子 于 2010-1-3 18:36 编辑 ]

论坛徽章:
0
4 [报告]
发表于 2010-01-03 18:38 |只看该作者
Question: How to recover a reference from a stringified reference?

I have a string which is the stringification of an object reference, e.g.

"MYOBJECT(0x3a89fc)"

How can I recover from this an actual reference, so that (e.g.) I'll be able to call the object's methods?

Answer1:

The problem with doing this is that it bypasses perl's reference-counting memory management scheme. If you could ref-ify a stringified ref, you'd be getting a new reference to something without incrementing that thing's reference count. That means you could still have "live" references to an object which perl has already deallocated due to its reference count reaching zero.

Now that you know all that, if you really still want to reconstitute a reference from a string (and have no fear of the potentially nasty consequences) try the Devel::Pointer module.

See this post by Ovid for more information on references.

Answer2:

One additional point: If your refs are getting stringified because you're using them as keys to a hash, you might want to look into Tie::RefHash, which will avoid that problem.

论坛徽章:
0
5 [报告]
发表于 2010-01-03 18:40 |只看该作者
Question: How do I take a slice of a hash reference?

I can take a slice of an array ref, but not of a hash ref:

my $arr =[1,'a',2,'b'];
my $hash={1,'a',2,'b'};
print join ':',(@$arr )[1,2];
print join ':',(@$hash){1,2};


The first print statement works, the second is a syntax error. I also have this problem when taking a slice of an anonymous hash:

print join ':',(1,'a',2,'b')[1,2];
print join ':',(1,'a',2,'b'){1,2};

Again, the first statement works, but the second is a syntax error.

Answer:

Here's how:

@slice = @{$array_ref}[ @idx ];
@slice = @{$hash_ref}{ @keys };

And there's no easy way to "pretend" a list is a hash. You need to use a hash reference explicitly:

$scalar = {1, 'a', 2, 'b'}->{$key};
@slice = @{ { 1, 'a', 2, 'b' } }{ @keys };

[ 本帖最后由 兰花仙子 于 2010-1-3 20:23 编辑 ]

论坛徽章:
0
6 [报告]
发表于 2010-01-03 18:41 |只看该作者
仙子的贴··要顶

论坛徽章:
0
7 [报告]
发表于 2010-01-03 20:26 |只看该作者
Question: How do I get a reference to a builtin function?

I would like to call a built-in function (in this case, uc) via a reference.
However, the following does not work.
(I suspect I'm not using the correct namespace for built-in functions.)

my %op = ( 'toupper' => \&uc, ... );
print $op{'toupper'}->($text);

I've tried UNIVERSAL::uc, CORE::uc, and CORE::GLOBAL::uc, but they all complain with something like (depending on the namespace I try to use):

Undefined subroutine &CORE::uc called at ...

Answer:

I don't know of a way to do that. But perhaps you'd find the following solution acceptable:
writing your own wrapper functions to which you can refer.

my %op = ( 'toupper' => sub { uc shift }, ... );

论坛徽章:
0
8 [报告]
发表于 2010-01-03 20:31 |只看该作者
Question: How do I refer to an element of a hash of arrays as an array

I create a hash where each element refers to an array:

$h{'one'} = [1,2,3];

I can access the individual array elements o.k.:

print "$h{'one'}[1]\n";

prints "2" as I expect.

Why can't I access the entire array as if it were a regular array?
What don't I understand here:

my @a = $h{'one'};
print "$a[1]\n";

doesn't do what I expect.

Answer:

Since hash values can only be scalars, the value is a reference to an array.
That is, it's like a pointer. (Try print "$h{'one'}\n" and see what you get.)

To get at the thing the reference points to, you need to explicitly dereference it. To get at the array, do

my @a = @{ $h{one} };

By enclosing something in the @{ } construction, you're saying that you want the array to which that reference points.

论坛徽章:
0
9 [报告]
发表于 2010-01-03 20:37 |只看该作者
Question: How do I access an array for which I only have a reference?


Answer1:

You dereference the array reference.

For example, given

my @array = ( 'one', 'two', 'buckle my shoe', );
my $array_ref = \@array;

then you can access the array to which $array_ref refers by prefixing a @ to it, e.g.:

for ( @$array_ref ) { ... }

The @ symbol is essentially an array dereference operator. It can dereference any value which is an array reference, whether that value is in a variable (e.g. $array_ref) or is the result of some more complex expression. The general syntax is @{ ... }; but you can omit the curlies when the expression is a simple scalar variable holding an array ref.

To access elements of an array to which you have a reference, insert an arrow between the array ref and the square brackets used for indexing, e.g.

print "The third element is $array_ref->[2] \n";

Reference types are strictly checked by the interpreter; you can't dereference an array ref as a hash, for example.

print keys %{ $array_ref }; # fatal!

The error message I get is "Can't coerce array into hash at - line ..."

Answer2:

A really good book to get is O'Reilly's Advanced Perl Programming if you are going to get into this sort of thing. But here is my brief explanation.

Let's say you have an array reference...

my $r_array = [ 'inky', 'blinky', 'blu' ];

Now the question is, what exactly is in $r_array? It's sort of similar to the concept of pointers in C: a reference contains the address of where the data is actually located in memory. If you print $r_array directly, you get something funky looking like ARRAY(0xc70858), which is clearly not the data stored in the array. So think about your reference this way: it's just a sign saying where the data really is.

To get at the data where it really is, you have to dereference the reference, using the following syntax:

@{ $r_array }

You could use it in code, like this:

for ( @{ $r_array } ) { print "$_\n"; }

Now, as a shorthand, Perl lets you omit the curly braces (for reference variables only):

for ( @$r_array )

Answer3:

In places where you want the index of the last element (e.g. in foreach loops), you'd use the syntax

$#{ $arrayref }

It's just like the usual $#array, but with an array reference value in place of the array variable name. And you need the curly braces.

So, to iterate over the elements of an array to which you have a reference, using an index counter:

for ( my $i = 0; $i <= $#{ $arrayref }; $i++ )
  { print "$arrayref->[$i] \n"; }

I think this is useful when you have arrays of arrays, or even more complex data structures.

论坛徽章:
0
10 [报告]
发表于 2010-01-03 20:48 |只看该作者
Question: defining a HASH table by reference and using it in a sub function

How do I do this in one line?

sub build_list {
        ($RECORD,$RI,$RECORDxRI,$RECORDxRI_count_REF,$EACH_RECORD_REF) = @_;

        %RECORDxRI_count = %$RECORDxRI_count_REF;
        %EACH_RECORD     = %$EACH_RECORD_REF;

        $RECORDxRI_count{$RECORDxRI}++;
        $EACH_RECORD{$RECORD} = '';
        return(\%RECORDxRI_count,\%EACH_RECORD);
}

while <DATA> {

        @record = split('\t',$_);
        $RI = @record[38];

        ($RECORD,$junk) = split('{',@record[54]);
        $RECORDxRI = $RECORD . ',' . $RI;

        ($RECORDxRI_count_REF,$EACH_RECORD_REF) = &build_list($RECORD,$RI,$RECORDxRI,\%$RECORDxRI_count,\%EACH_RECORD);
        %RECORDxRI_count = %RECORDxRI_count_REF;
        %EACH_RECORD = %$EACH_RECORD_REF;

        # NOTE: I have to dereference both hashes
        # on every iteration of the while loop
} # end while data

OK ... I understand that ONE line is a little overzealous, but I'm actually interested in making the function call to &build_list in one line...

Is there a way to get a full hash without dereferencing the hashes I pass to the routine on every iteration of this loop?

Could I get

&build_list(\%hash1,\%hash2);

to create that hash on its first pass and on subsequent iterations of "while" use the same hash?

Answer1:

Why don't you just always treat them as references? Then you won't have to reference/dereference them each time.
# Define them as hash references.
my $RECORDxRI_count_REF = {};
my $EACH_RECORD_REF = {};

while (<DATA>) {
    ....
    # Call build list--now you don't need to
    # take references to the hashes, because they're
    # already references
    build_list( $RECORD, $RI, $RECORDxRI, $RECORDxRI_count_REF, $EACH_RECORD_REF );
    ....
}

sub build_list {
    my( $RECORD, $RI, $RECORDxRI, $RECORDxRI_count_REF, $EACH_RECORD_REF ) = @_;

    # These are hash references. To access them,
    # you *don't* need to assign them to hashes.
    # Just use the -> operator, which automatically
    # dereferences them.
    $RECORDxRI_count->{$RECORDxRI}++;
    $EACH_RECORD->{$RECORD} = '';
}

Read perlref if this doesn't make sense.

Answer2:

I may be missing the point here...

Since you seem to be using global variables anyway, you don't need to even return anything from build_list.

You are passing in references to hashes. Those hashes are being modified by build_list. Your assignment to the hashes from the return value of build_list is redundant. They are already modified when build_list returns. Just use %RECORDxRI_count and %EACH_RECORD.

I assume you are doing more in build_list and the <DATA> loop than you actually posted...

Here is some condensed code (since you expressed interest in minimizing lines of code):

my %RECORDxRI_count;
my %EACH_RECORD;

sub build_list {
  my( $RECORD, $RI, $RECORDxRI, $RECORDxRI_count_REF, $EACH_RECORD_REF ) = @_;

  $RECORDxRI_count_REF->{$RECORDxRI}++;
  $EACH_RECORD_REF->{$RECORD} = '';
}

while <DATA> {
  my( $Record54, $Record38 ) = (split /\t/)[54, 38];

  my $RECORD = (split /{/, $Record54)[0];
  $RECORDxRI = $RECORD . ',' . $Record38;

  build_list( $RECORD, $RI, $RECORDxRI, \%$RECORDxRI_count, \%EACH_RECORD );
}

Notes:

    * I explicitly named the "global" variables, just for ease-of-maintenance.
    * $RI was always unused in build_list — I left it here because I suppose you are doing more than the posted code shows.
    * You had @ instead of $ when getting indices from @record. If you only want one value, don't use a slice.
    * I almost just defined the "global" hashes as references in the first place (to avoid the backslashes altogether).
      Update: See btrott's answer for more on this.
      Like:
      my $RECORDxRI_count = {};
      my $EACH_RECORD     = {};
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP