読者です 読者をやめる 読者になる 読者になる

Perl復習メモ

ファイルハンドル

    while (<>) {
        # do something with $_
    } 
  • connect済みのソケットを標準入力、標準出力と接続する
# $socket is already connected
open(STDIN, '<&=', fileno($socket));
open(STDOUT, '>&=', fileno($socket));

ファイル書き込み時のバッファリング無効化

  • ファイルを書き込みオープンした状態でスレッドを回して共有して書き込むときには、ロックをかけるだけでなく、ファイル書き込み時のバッファリングを無効にしないといけない。
  • ここではIO::Handleのautoflushを使う。
  • 各スレッドが交互にaaaaaaaaaabbbbbbbbbbをファイルに書き込んでいくプログラムを、autoflush有り・無しで動かし、結果を比較する。
$ cat file_write_test.pl 

package Thread;
use strict;
use threads;
use threads::shared;

sub func {
    my $hdlr = shift;
    my $id   = shift;
    my $queue= shift;
    my $mutex : shared;

    print $id."\n";

    for(;;){
        my $i = $queue->dequeue();
        if( $i==0 ){ last; }
        {
            lock($mutex);
            print { $hdlr } ( $i%2==1 ? "a"x10 : "b"x10 )."\n";
        }
    }
}

1;

package main;
use strict;
use threads;
use Thread::Queue;
use IO::Handle;

print "start\n";

my $hdlr;
open $hdlr, '>', "test.log";

$hdlr->autoflush;

my @threads;
my $queue = new Thread::Queue;
for( my $id=0; $id<4; $id++ ){
    my $thread = threads->create(\&Thread::func, $hdlr, $id, $queue);
    push(@threads, $thread);
}

for( my $i=1; $i<10000; $i++ ){
    $queue->enqueue($i);
}

for( my $id=0; $id<4; $id++ ){
    $queue->enqueue(0);
}

foreach (@threads) {
    $_->join();
}
close $hdlr;

1;
$ perl file_write_test.pl 
start
0
1
2
3

$ grep -v "[ab]\{10\}" test.log
$
  • IO::Handle autoflushを使わないときの結果はこのようになる。
$ grep -v "[ab]\{10\}" test.log
bbbbbbb
bbbbbaa
aaaaaaa
bbaa
aaaaaaaa

bbbbbbbb
$

ヒアドキュメント

  • 同時に複数のヒアドキュメントを扱う場合はこうなる。これは仕様だろうか。(Perl v5.18 on OSX
  • なお、hoge(<< "EOF2", << 'EOF1'); こうするとエラーとなる(Can’t find string terminator “EOF1” anywhere before EOF)
$ cat here.pl 
use strict;
use v5.10;

sub hoge {
    my ($a, $b) = @_;

    say "a".$a;
    say "b".$b;
}

our $x = "xyz";

hoge(<< "EOF1", << 'EOF2');

aaa
$x
ccc
EOF1

***
$x
===
EOF2

$ perl here.pl 
a
aaa
xyz
ccc

b
***
$x
===

型グロブ

  • *を使って同じ変数名で異なるSigilの各種変数にアクセスすることができる。${*var}@{*var}、あるいは&{*var}とすれば型グロブからそれぞれスカラと配列、サブルーチンにアクセスすることができる。*var{SCALAR}*var{ARRAY}*var{CODE}でも同じようにアクセスできる。
  • c.f. 型グロブ - hakobe-blog ♨

UTF-8フラグ

  • 内部表現のバイト列であることを表すフラグであり、決してUTF-8エンコードされた文字列であることを示すものではない
  • Devel::Peekで文字列にUTF-8フラグがついているかどうか確認できる FLAGS = (PADMY,POK,pPOK,UTF8)
  • このフラグがついた状態の文字列をprintしようとすると、Wide character in print atというWarningが表示される
  • UTF-8エンコードされた文字列を内部表現にデコードする場合は、Encode::decode('utf-8',$string);とする
  • 内部表現のバイト列をUTF-8エンコードする場合は、Encode::encode('utf-8',$string_flagged);とする
  • use utf8プラグマは、UTF-8で書かれたコード中の文字列を自動的に内部表現に変換して扱うことを宣言するものであるため、コードがUTF-8で書かれていることを前提とする
  • use utf8プラグマが無い場合、コードがlaten1で書かれているものとして扱われる
  • c.f. Perl の文字列エンコーディングの話 | Hachioji.pm 日めくりテックトーク

Sort

  • ハッシュの値の組み合わせによるソートにも柔軟にソート条件を与えることで対応できる
  • デフォルトは{$a cmp $b}で、文字列順でのソートになる
  • 条件を||で続けて書くとサブ条件となる
  • ハッシュからスカラを取り出した結果が配列のとき、@{$hash{$key}}とする
  • 配列の要素がハッシュの場合、各ハッシュの値を取り出すときは、配列からスカラをリファレンスとして取り出し、->デリファレンスしたハッシュにキーを与えることで値を取り出す
  • ${$a}[0]と$a->[0]は同じ
use strict;
use warnings;
use Data::Dumper;

my %hash;
push @{$hash{1}}, {val1=>10, val2=>1, val3=>100};
push @{$hash{1}}, {val1=>10, val2=>2, val3=>200};
push @{$hash{2}}, {val1=>20, val2=>2, val3=>300};
push @{$hash{2}}, {val1=>20, val2=>2, val3=>400};
push @{$hash{3}}, {val1=>30, val2=>1, val3=>500};
push @{$hash{3}}, {val1=>30, val2=>2, val3=>600};

print Dumper \%hash;

print "\n*** Sorted keys ***\n";
my @sortedkeys  = sort { $a <=> $b } keys %hash;
print Dumper \@sortedkeys;

print "\n*** Sorted hashes by dec for val2+val2 ***\n";
my @sortedhashlist;
for my $elem (@sortedkeys){
    my @sortedhash = sort { $b->{val1}+$b->{val2} <=> $a->{val1}+$a->{val2} || $a->{val3} <=> $b->{val3} } @{$hash{$elem}};
    print Dumper \@sortedhash;
    push @sortedhashlist, @sortedhash;
}

print "\n*** Sorted hashe lists ***\n";
for my $elem (@sortedhashlist){
    print Dumper $elem->{val1}."-".$elem->{val2}.":".$elem->{val3};
}
$ perl a.pl 
$VAR1 = {
          '1' => [
                   {
                     'val3' => 100,
                     'val1' => 10,
                     'val2' => 1
                   },
                   {
                     'val3' => 200,
                     'val2' => 2,
                     'val1' => 10
                   }
                 ],
          '3' => [
                   {
                     'val3' => 500,
                     'val2' => 1,
                     'val1' => 30
                   },
                   {
                     'val3' => 600,
                     'val2' => 2,
                     'val1' => 30
                   }
                 ],
          '2' => [
                   {
                     'val2' => 2,
                     'val1' => 20,
                     'val3' => 300
                   },
                   {
                     'val1' => 20,
                     'val2' => 2,
                     'val3' => 400
                   }
                 ]
        };

*** Sorted keys ***
$VAR1 = [
          '1',
          '2',
          '3'
        ];

*** Sorted hashes by dec for val2+val2 ***
$VAR1 = [
          {
            'val3' => 200,
            'val2' => 2,
            'val1' => 10
          },
          {
            'val3' => 100,
            'val1' => 10,
            'val2' => 1
          }
        ];
$VAR1 = [
          {
            'val2' => 2,
            'val1' => 20,
            'val3' => 300
          },
          {
            'val1' => 20,
            'val2' => 2,
            'val3' => 400
          }
        ];
$VAR1 = [
          {
            'val3' => 600,
            'val2' => 2,
            'val1' => 30
          },
          {
            'val3' => 500,
            'val2' => 1,
            'val1' => 30
          }
        ];

*** Sorted hashe lists ***
$VAR1 = '10-2:200';
$VAR1 = '10-1:100';
$VAR1 = '20-2:300';
$VAR1 = '20-2:400';
$VAR1 = '30-2:600';
$VAR1 = '30-1:500';

サブルーチンプロトタイプ宣言

サブルーチンへのリファレンス

サブルーチンお作法

  • ユーザ定義サブルーチンを呼び出す際に&を付けるのはdeprecated(プロトタイプ宣言があるのでbanned)
  • 組み込みサブルーチンを呼び出す際には()を付けないことで、ユーザ定義サブルーチン呼び出しと区別する
  • use const VAR => 定数つかうよりsub VAR () {定数}の方が、constant.pmを呼び出すオーバーヘッドがかからない
    • プロトタイプとして()を付けておくことで引数を取らないことを明示できる

ファットカンマ(=>)

ハッシュ

  • ハッシュの宣言はmy %hash = ( a => 10 );とする
  • 無名ハッシュのリファレンスの宣言はmy $hash = { a => 10 };とする
  • 無名ハッシュをマージするには、my $merged = { %$hash1, %$hash2 };とする
  • なお、配列の要素のハッシュをマージするには、my $merged = { %{$hash1[0]}, %{$hash2[0]} };とする必要がある
  • ハッシュへのリファレンスをデリファレンスしてその要素(value)を取り出す際は$$hash{0}とする