2012/04/28

perl リファレンスは簡単

新年度ですね!!

サンプルコードは間違ってるかもしれないから、適当にワンライナーにでもして試してね!!!

ただのハッシュのコピー

まず、リファレンスでないハッシュだと "値" がコピーされるのを実行します!

# ただのハッシュですね!
my %foo = ( key => 1 );

# コピーしてみますよ!
my %bar = %foo;

# コピーした先で値を代入してみますよ!
$bar{key} = 2;

# おおもとの方は書き換わってませんね!あたりまえですね!
print $foo{key}; # 1

リファレンス

こういうのがリファレンス

HASH

my $foo = { key => 1 };
print ref($foo);

ARRAY

my $bar = [1, 2, 3, 4];
print ref($bar);

SCALAR

my $baz = 123;
my $hoge = \$baz;
print ref($hoge);

ref 関数でなく、直接変数を参照すると、どれも中身はアドレスが入ってる。値がメモリのどこにあるかの情報を持ってるだけってことですね!

my $foo = { key => 1 };
print $foo; # HASH(0x8f1cc28)

リファレンスは "参照" それ自体がコピーされるのです!

# ハッシュリファレンスですね!
my $foo = { key => 1 };

# コピーしてみますよ!
my $bar = $foo;

# コピーした方で値を代入してみますよ!
$bar->{key} = 2;

# おおもとの方も書き換わってますね!
print $foo->{key}; # 2

リストのリファレンス

もちろん、リストのリファレンスでも同じ!!

my $foo = ['foo', 'bar', 'baz'];

# コピーしてみますよ!
my $bar = $foo;

# コピーした先($bar)で値を代入してみますよ!
$bar->[0] = 'hoge';

# おおもと($foo)の方も書き換わってますね!
print "$_\t" for @{$foo}; # hoge bar baz

デリファレンスしてコピー

デリファレンスしてハッシュにコピーすれば "値" がコピーされますよ!

# ハッシュリファレンスですね!
my $foo = { key => 1 };

# デリファレンスしてコピー
my %bar = %{$foo};

# コピーした先で値を代入してみますよ!
$bar{key} = 2;

# 書き換わるわけありませんね!
print $foo->{key}; # 1

ちなみに、デリファレンスしてリファレンスにしても "参照" のままっぽ!!

my $foo = { key => 1 };

# デリファレンスしてリファレンスにしてコピー
my $bar = \%{$foo};

# コピーした先で値を代入してみますよ!
$bar->{key} = 2;

# おおもと書き換わるわってますね!
print $foo->{key}; # 1

サブルーチンにリファレンス渡し

リファレンスわたして書き換えると呼びもとの方ももちろん変わる。(破壊的!!)

sub foo {
    my $ref = shift;

    $ref->{key} = 2;
    return $ref->{key};
}

my $bar = { key => 1 };

foo($bar);

print $bar->{key}; # 2

書き換えるなら値を取り出してやるとかする!!

sub foo {
    my $ref = shift;

    my $key = $ref->{key}; # 値を取り出す
    $key = 2;
    return $key;
}

my $bar = { key => 1 };

foo($bar);

print $bar->{key}; # 1

コピーと参照の benchmark

取り急ぎ、リファレンス渡しは、値をコピーしないから超絶かわいい。

Benchmark: timing 5000 iterations of HASH, REF...
      HASH:  6 wallclock secs ( 6.24 usr +  0.00 sys =  6.24 CPU) @ 801.28/s (n=5000)
       REF:  0 wallclock secs ( 0.00 usr +  0.00 sys =  0.00 CPU)
            (warning: too few iterations for a reliable count)
                      Rate                HASH                 REF
HASH                 801/s                  --               -100%
REF  5000000000000000000/s 624000000000000000%                  --

そーす。

#!/usr/bin/perl
use strict;
use warnings;
use Benchmark qw/timethese cmpthese/;

my %HASH;
for (1..5000) { $HASH{$_} = $_; }

my $result = timethese (5000, {
    'HASH' => sub { _hoge(  %HASH ); },
    'REF'  => sub { _hoge( \%HASH ); },
});

cmpthese $result;

sub _hoge {}

perl 変数をみる

リファレンスに限らず、perl 変数の深遠を探りたいなら、Devel::Peek で覗くとOK。

$ perl -MDevel::Peek -le 'my $foo = { key => 1 }; print Dump($foo);'

SV = RV(0x8180918) at 0x8158cdc
  REFCNT = 1
  FLAGS = (PADBUSY,PADMY,ROK)
  RV = 0x8158c28
  SV = PVHV(0x815d190) at 0x8158c28
    REFCNT = 1
    FLAGS = (SHAREKEYS)
    IV = 1
    NV = 0
    ARRAY = 0x81752b8  (0:7, 1:1)
    hash quality = 100.0%
    KEYS = 1
    FILL = 1
    MAX = 7
    RITER = -1
    EITER = 0x0
    Elt "key" HASH = 0xe9806307
    SV = IV(0x8174174) at 0x8158d54
      REFCNT = 1
      FLAGS = (IOK,pIOK)
      IV = 1

注目はこのへん。

ARRAY = 0x81752b8  (0:7, 1:1)
hash quality = 100.0%

ハッシュは内部では ARRAY で quality 管理されてんのか!! とか興味深いですけど、うかつに each 使ったりしない限りここらを見ることは二度とないでしょう!! でも、unicode 的なあれで FLAGS を見ることもできて便利です!!

Let's enjoy Perl World together!!

サイト内検索