Translate

2012年4月9日月曜日

リファレンスのメモ

変数の前に\をつけることでその変数に対する参照(リファレンス)でやり取りが可能になる。
そして、参照情報から実際の値を取り出すには、各変数のファニー文字(スカラー⇒$,
配列⇒@, ハッシュ⇒%)をつけてあげれば元の変数のように扱うことができる。

サンプルコード
#!/usr/bin/perl                                                                         
use feature 'say';
use strict;
use warnings;
use utf8;
use File::Find;

binmode STDIN,  ":utf8";
binmode STDOUT, ":utf8";

#配列、ハッシュ                                                                         
my @array = ("a", "b", "c");
my %hash  = (
             guu   => "rock",
             cyoki => "scissors",
             paa   => "paper",
            );

#リファレンス                                                                           
my $ref_array = \@array;
my $ref_hash  = \%hash;

                                                                                   
#表示                                                                                   
for(my $i = 0; $i < @{$ref_array}; $i++){
  say "${$ref_array}[$i]";
}


for my $key (keys %{$ref_hash}){ 
  say "key: $key / value: ${$ref_hash}{$key}";
}
実行結果:
a
b
c
key: paa / value: paper
key: cyoki / value: scissors
key: guu / value: rock
変数の頭に\をつけることをリファレンスと呼びその逆の行為をデリファレンスと呼ぶ。
デリファレンスした後
配列の場合 ${$ref_array}[$i]
ハッシュの場合 ${$ref_hash}{$key}
とアクセスすることもできるが、アロー演算子による表記が用意されているので
以下のように書き換えることができる。

配列の場合   $ref_array->[$i]
ハッシュの場合 $ref_hash->{$key}
サブルーチンの場合も同様にアロー演算子による表記が可能である。
まとめ。
配列   リファレンス->[要素]
ハッシュ リファレンス->{キー}
関数   リファレンス->(引数)


上記の例では、配列とハッシュの変数に格納してからリファレンスを
扱っているが直接リファレンスを扱うこともできる。

配列の場合
(要素, 要素, 要素)から
[要素, 要素, 要素]に変えるだけ。

ハッシュの場合
(キー=>値, キー=>値)から
{キー=>値, キー=>値}に変えるだけ。

上記の例を書き直すとこうなる。
#!/usr/bin/perl                                                                         
use feature 'say';
use strict;
use warnings;
use utf8;
use File::Find;

binmode STDIN,  ":utf8";
binmode STDOUT, ":utf8";


#リファレンス                                                                           
my $ref_array = ["a", "b", "c"];
my $ref_hash  = {
                 guu   => "rock",
                 cyoki => "scissors",
                 paa   => "paper",
                };

                                                                                   
#表示                                                                                   
for(my $i = 0; $i < @{$ref_array}; $i++){
  say "${$ref_array}[$i]";
}


for my $key (keys %{$ref_hash}){ 
  say "key: $key / value: ${$ref_hash}{$key}";
}
簡単ですね! さて、今度は多次元配列を表現してみます。
#!/usr/bin/perl                                                                         
use feature 'say';
use strict;
use warnings;
use utf8;
use File::Find;

binmode STDIN,  ":utf8";
binmode STDOUT, ":utf8";


#リファレンス
my $ref_array = [
                 [1, 2],
                 [3, 4, 5],
                 [6, 7, 8, 9, 0],
                ];


for (my $x = 0; $x < @{$ref_array}; $x++){
  for (my $y = 0; $y < @{$ref_array->[$x]}; $y++){
    say $ref_array->[$x][$y];
  }
}

@{$ref_array} で [1, 2]と[3, 4, 5]と[6, 7, 8, 9, 0]の3要素であることを取得し
@{$ref_array->[$x]}で各要素
[1, 2] => 2要素
[3, 4, 5] => 3要素
[6, 7, 8, 9, 0] => 4要素
であることを取得しています。

2012年4月8日日曜日

File::Find

■再帰的にディレクトリを移動して、ファイル/ディレクトリ名を取得する

.
├── 1
│   └── 2
│       └── 3.txt
├── a
│   └── b.txt
└── Sample.pl

上記構成を例に使用してみる。
■"1"ディレクトリの構成を調べる
#!/usr/bin/perl
use feature 'say';
use strict;
use warnings;
use utf8;
use File::Find;


find(\&wanted, '1');                                                                    

sub wanted{
  say "CurrentDirectory :", $File::Find::dir;
  say "Name             :", $_;
  say "FullPath         :", $File::Find::name;
  say "---";
}

実行結果:
CurrentDirectory :1
Name             :.
FullPath         :1
---
CurrentDirectory :1
Name             :2
FullPath         :1/2
---
CurrentDirectory :1/2
Name             :3.txt
FullPath         :1/2/3.txt
---



■複数のディレクトリを指定してみる
#!/usr/bin/perl
use feature 'say';
use strict;
use warnings;
use utf8;
use File::Find;


find(\&wanted, qw/1 a/); #配列で指定

sub wanted{
  say "CurrentDirectory :", $File::Find::dir;
  say "Name             :", $_;
  say "FullPath         :", $File::Find::name;
  say "---";
}

実行結果:
CurrentDirectory :1
Name             :.
FullPath         :1
---
CurrentDirectory :1
Name             :2
FullPath         :1/2
---
CurrentDirectory :1/2
Name             :3.txt
FullPath         :1/2/3.txt
---
CurrentDirectory :a
Name             :.
FullPath         :a
---
CurrentDirectory :a
Name             :b.txt
FullPath         :a/b.txt
---



■一番下の階層からスタートする
#!/usr/bin/perl
use feature 'say';
use strict;
use warnings;
use utf8;
use File::Find;


finddepth(\&wanted, '1');

sub wanted{
  say "CurrentDirectory :", $File::Find::dir;
  say "Name             :", $_;
  say "FullPath         :", $File::Find::name;
  say "---";
}

実行結果:
CurrentDirectory :1/2
Name             :3.txt
FullPath         :1/2/3.txt
---
CurrentDirectory :1
Name             :2
FullPath         :1/2
---
CurrentDirectory :1
Name             :.
FullPath         :1
---

FindBin

実行スクリプトのディレクトリを取得するモジュール

#!/usr/bin/perl
use feature 'say';
use strict;
use warnings;
use utf8;
use FindBin;
                                                        
say $FindBin::Bin;

libモジュールのパス指定をする際に利用すると便利。
use lib "$FindBin::Bin/lib";

File::Path

■ディレクトリの作成
カレントディレクトリにfoo/bar/bazなディレクトリ階層を作成したい時

下記構成
├── foo
│   └── bar
│   └── baz

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

mkdir foo
mkdir foo/bar
mkdir foo/bar/baz
のように上位ディレクトリから順に作成していくことになる。

File::Pathを使うと1行ですむ。
#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use File::Path qw/make_path/;

make_path('foo/bar/baz'); #ver 2.06以降


■ディレクトリの削除
下記ディレクトリ構造でfooディレクトリを削除したい時
├── foo
│   ├── bar
│   │   ├── baz
│   │   │   └── sample.txt
│   │   └── sample.txt
│   └── sample.txt
最下層のディレクトリ内のファイルを削除⇒ディレクトリ削除の順で削除する必要がある。
File::Findeモジュールのfinddepth関数で最下層のディレクトリからアクセスして削除していく
#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use File::Find;
use FindBin;

finddepth(\&deleteAll, 'foo');

sub deleteAll{
  #フルパスじゃないと削除できなかったのでスクリプトのパスを取得しておく
  my $BasePath = $FindBin::Bin . '/';
  if(-f $_){ unlink $BasePath . $File::Find::name || die $! };
  if(-d $_){ rmdir  $BasePath . $File::Find::name || die $! };                          
}


File::Pathを使うと1行ですむ。
#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use File::Path qw/remove_tree/;

remove_tree('foo'); #ver 2.06以降


v2.00より前とv2.06より前とそれ以降でインターフェースが異なります。
perl -MFile::Path -le 'print "$File::Path::VERSION"'
等で、確認した上で利用する必要があります。

v2.06以降の場合
use File::Path qw(make_path remove_tree);

  make_path('foo/bar/baz', '/zug/zwang'); #複数作成したい場合コンマで区切る
  make_path('foo/bar/baz', '/zug/zwang', {
      verbose => 1,  #1を指定すると実行時コマンドを出力する
      mode => 0711, #アクセス権限を指定  デフォ:0777
  });

  remove_tree('foo/bar/baz', '/zug/zwang');
  remove_tree('foo/bar/baz', '/zug/zwang', {
      verbose => 1,
      error  => \my $err_list, #エラー情報
  });

ワンライナー

随時追加していく予定。

・eスイッチ
文字列をスクリプトとして解釈する
---
perl -e 'print "hello world¥n"'
---


・lスイッチ
行末に改行をつけてくれる。下の2コードは同じ結果となる
---
perl -e 'print "hello world¥n"'
perl -le 'print "hello world"'
---
改行を自動的につけてくれるといえばsay関数があるのでそれを使っても同じ結果となる。
ただし、Eスイッチになる。
---
perl -e 'print "hello world¥n"'
perl -le 'print "hello world"'
perl -E 'say "hello world"'
---


・Mスイッチ
モジュールを呼び出す
---
#FilePathモジュールのバージョンを出力
perl -MFile::Path -E 'say "$File::Path::VERSION"'
#LWP::Simpleをつかってwww.google.co.jpを取得
perl -MLWP::Simple -E 'say get shift' http://google.co.jp
---


配列からハッシュを作る

■ハッシュ
・2つの配列(キー用の配列,値用の配列)からハッシュを作る
⇒ハッシュのスライスを使う

#!/usr/bin/perl
use feature 'say';
use strict;
use warnings;
use utf8;
use Encode;

my @k = qw/k1 k2 k3/;
my @v = qw/v1 v2 v3/;
my %h = ();
@h{@k} = @v; #ハッシュのスライス

for my $key (sort keys %h){
  say encode_utf8("key:". $key. " value:". $h{$key});
}
実行結果
key:k1 value:v1
key:k2 value:v2
key:k3 value:v3

上記では、空のハッシュに対し、ハッシュのスライスをおこなってみた。
今度は、値が入っているハッシュに対し、ハッシュのスライスを行い
配列データを配列に格納してみる。
#!/usr/bin/perl
use feature 'say';
use strict;
use warnings;
use utf8;
use Encode;

my @k = qw/k1 k2 k3/;
my @v = qw/v1 v2 v3/;
my %h = ("k0" => "v0", "k1" => "dummy");
@h{@k} = @v; #ハッシュのスライス

for my $key (sort keys %h){
  say encode_utf8("key:". $key. " value:". $h{$key});
}

実行結果
key:k0 value:v0
key:k1 value:v1
key:k2 value:v2
key:k3 value:v3


・配列(一つ)からハッシュを作る。
(存在するかどうかを知るためにハッシュ化するだけで値は意味のある値を必要としないとする)
#!/usr/bin/perl
use feature 'say';
use strict;
use warnings;
use utf8;
use Encode;

my @k = qw/k1 k2 k3/;
my %h = ();
@h{@k} = 1..@k; #ハッシュのスライス

for my $key (sort keys %h){
  say encode_utf8("key:". $key. " value:". $h{$key});
}

実行結果
key:k1 value:1
key:k2 value:2
key:k3 value:3


でも…map関数を使えばもっと綺麗に。
#!/usr/bin/perl
use feature 'say';
use strict;
use warnings;
use utf8;
use Encode;

my @k = qw/k1 k2 k3/;
my %h = map{($_, 1)} @k;

for my $key (sort keys %h){
  say encode_utf8("key:". $key. " value:". $h{$key});
}