Home > Perl Archive

Perl Archive

今日覚えた(思い出した?)Perl

  • 2009-01-09 (金)
  • Perl

無名配列と無名連想配列。
無名配列は[]で作成、無名連想配列は{}で作成。リファレンスを使ってアクセスする。


$a = ['hoge', 'fuga'];
$b = {'hoge' => 1, 'fuga' => 2};

foreach(@{$a}) {
	print $_ . "\n";
}
foreach(keys(%{$b})) {
	print $_ . "=>" . $b->{$_} . "\n";
}

出力


hoge
fuga
fuga=>2
hoge=>1

# あと、Perlには”echo”という命令はないらしい、ということも覚えた。

以上

インクリメントはインプリメント依存

o-taki氏からのタレコミです。
まずはC言語。
問題のコードは下記です。


#include <stdio.h>

int main(int argc, int *argv[]){
    int a[2][2] = {{6,8},{100,120}};    // ①
    int *t = *a;        // ②
    int **l = &t-1;    // ③

    printf("%d\n",++*++*++l*++*++*l++);   // ④
    
    return 0;
}

まあ、②、③のあたりでやや意味不明ですが、要はlはtのアドレスの一個手前だからaの一個手前、つまり何もないところ指してる。多分、直接**lにアクセスすると怒られると思われます。

④の所を解析すると、演算子の優先順位を考えると


++*++*++l*++*++*l++

= ++*++(*(++l)) * ++*++(*(l++))

= (++(*(++(*(++l))))) * (++(*(++(*(l++)))))

= (++(*(++(*(++l))))) * (++(*(++(*(l++)))))      //  ⑤

になるんじゃないかと思います。ややこしいことは間違いないですが、問題は「後置インクリメントがどのタイミングで評価されるのか」ということに集約されると思います。
まず、⑤の最初の大きな括弧


(++(*(++(*(++l)))))   // ⑤-i

が最終的にどこを指しているか考えます。a[][]のメモリ領域を便宜的に下記の表現すると、


Z.a:0   Z.b:0
A.a:6   A.b:8
B.a:100  B.b:120

初期状態でlはZ(=Z.a)を指しています。順を追うと、


(++l) => A (=A.a)
(*(++l)) => A.a
(++(*(++l))) => A.b   // ⑥
</code></pre>
したがって最後の
<pre><code>
(++(*(++(*(++l)))))

でA.bの値が8から9に変更されます。
注意すべきは、最後の時点でlが指している先はA.bです。(多分)
⑥のインクリメントは*lに対するインクリメントですが、*lもlも一緒な気がします。

で、⑤の二つめの大きな括弧(これが問題)


(++(*(++(*(l++)))))     // ⑤-ii

も同様に考えると


(l++) => A (=A.b)   // ⑦
(*(++l)) => A.b
(++(*(++l))) => B.a

というわけで、⑦は後置のため、参照される段階ではまだインクリメントされません。(多分)
したがって最後の


(++(*(++(*(l++))))) 

でB.aの値が100から101に変更されます。

ということは、④=⑤=


(++(*(++(*(++l))))) * (++(*(++(*(l++))))) 

は9*101=909になります。
といいたいところなんですけど、コンパイラの実装依存ぽい感があります。まあ、上の解析が間違っている可能性も大ですが、最初のmainをコンパイルして実行すると、
・cygwin gcc ⇒ 909
・cygwin g++ ⇒ 909
・VC6 ⇒ 909
・VC2005 ⇒ 10201
謎です。VC2005。
これを解釈するには、二項演算子*は最後に評価されるため、前半の括弧(⑤-i)と後半の括弧(⑤-ii)の前置インクリメントが評価された後のlを考えれば、説明が付くような、付かないような・・・。

次にPerl。
超単純な次のコード。


$i = 1;

print ++$i + $i++;
print "\n";

$i = 1;
print $i++ + ++$i;
print "\n";

$i = 1;
print $i++ + $i++;

最初の出力は後置インクリメントなわけですから、2+2=4と表示されると思われたんですが結果は5。謎。
上記結果は下記のようになります。


5
4
3

全く謎。

Cでやると


 i = 1;
 printf("%d\n", ++i + i++);

 i = 1;
 printf("%d\n", i++ + ++i);

 i = 1;
 printf("%d\n", i++ + i++);

もちろん結果は


4
4
2

よって、結論!
インクリメントは使ってはならない。ルビーのように。

追記:2009-04-27
匿名さんのコメントより
>少なくともC/C++言語仕様上、副作用完了点の前に同じ変数を複数回変更した結果は「未定義(undefined)」です。
ということらしいです。他の言語についてまだ調べてませんが、言語使用上は動作を規定する義務はないので、どうなろうが知ったこっちゃないという。そうだったのか・・・。
まあ結論は同じで、そんな変なインクリメントをするなってことだと思います。

以上

Perlトリビアルメモ 1(ファイルロック、正規表現、foreach)

今日、これ(中村屋)の紹介を受けたんですが、こういうのはもっと早く報告していただかないと困りますね。本家じゃないようですが、本家消えてるようで。早めに世界遺産に登録したほうがいいんじゃないでしょうか?

今回はPerlのメモ。最近Perl触ってないので、思い出すため。ノスタルジー。たいしたことは書き(け)ません。

1.ファイルのロック
ファイルのロックは、今時flock()。昔はWindowsサーバーとかで使えなかったりしてsymlinkやmkdirで代用する手法がメジャー(?)だった。今はWindowsでもflock()使えるし、大体においてflock()でOK、ってどっかで読んだ。
問題はロックするタイミング。
失敗例:


open(FILE, " > file.txt");
flock(FILE, 2);

ロックする前にファイルサイズが0になるので×。追加書き込みモードで開く。


open(FILE, "+< file.txt");
flock(FILE, 2);

もちろん、新規にファイルを作成する場合は最初のでOK。
もっと問題なのはデッドロック。CGIなどで複数のファイルを読み書きする必要がある場合で、複数の同時アクセスがあった場合、一個一個のファイル操作で無頓着にロックを行っているとデッドロックになる。
例えば、ユーザの1アクセスでファイルAとファイルBを読み書きする場合。次のような順番で処理が行われると危険。

①ユーザFooがファイルAをロックして読み書き開始。
②ユーザBarがファイルBをロックして読み書き開始。
③ユーザFooはファイルAをロックしたままファイルBを読み書き開始。
④ユーザBarはファイルBをロックしたままファイルAを読み書き開始。

この場合、③でファイルBはBarによってロックされているので、ロックが解除されるまでFooは待とうとします。同様に④でファイルAはFooによってロックされているので、ロックが解除されるまでBarは待とうとします。ここでどちらも処理が止まり、どのファイルもロックが解除されることはありません。デッドロック。
①の後、FooがとっととファイルAを開放すりゃいいじゃないかという案もありますが、ファイルAを更新するためにはファイルBの内容が必要ということもあります。いったんファイルAを開放し、ファイルBを読み込んだ後改めてファイルAを更新するという手順を踏むと、ファイルAを開放している間に他のプロセスによってファイルAが更新されてしまい、データの連続性が失われる、とかそういう話です。
こういう場合はどうしたらいいかというと、簡単には2通りだったと思います。

○どんなアクセスでもファイルAを読み込んでからファイルBを読み込む。
○処理全体でロックしちゃう。

最初の方法だと、②でユーザBarはファイルBを操作する前にファイルAを操作しなくてはなりません。しかし、ファイルAはロックされているため、ユーザFooの処理が終わるまではBarは手出しできないという寸法です。
しかし、実際にはファイルAやBがユーザ情報ファイルだったりする場合、Barが最初に読み込むべきはファイルBでしょう。

そういうわけで、後者の方法が手っ取り早いです。現在のネット航海時代のユーザ情報ファイルアクセスもこの方式(のはず)です。
この方法ではファイルロック用に別途ファイルを用意しておいて、プロセス開始後、目的のファイルを操作する前にこのファイルロック用ファイルをロックします。その後、どのファイルでも何個でも適当にアクセスします。プロセス終了時にファイルロック用のファイルのロックを解除します。こうしておけば、処理中はファイルロック用ファイルがずっとロックされた状態になり、他のプロセスは手出しできないようになります。ネット航海時代ではシステム上、複数のユーザ情報ファイルにアクセスする必要があるためこのようにしています。(パフォーマンスは下がると思いますが。というか「DBを使え」という話も・・・。一度DB化したんですが、めんどくさくてリリースしませんでした。)

2.正規表現
同じ正規表現を繰り返し利用する場合は、正規表現をコンパイルしたほうが早いらしいです。正規表現のコンパイルにはqrを使います。


$pattern = qr/abc(\\d+)efg/;

foreach (@text) {
    if ($_ =~ /$pattern/) {
        print $1;    # 後方参照もOK
    }
}

3.foreach()
PHPやってるから思う疑問かもしれません。foreachでの変数の変更は有効か。


@text = ("aaa", "bbb");

foreach (@text) {
	$_ = "ccc";
}

foreach (@text) {
	print "$_\n";
}

結果:


ccc
ccc

ちなみにPHPでやると


$text = array("aaa", "bbb");

foreach ($text as $one) {
	$one = "ccc";
}
print_r($text);

結果:


aaa
bbb

PHP5だと以下のように書けば、


$text = array("aaa", "bbb");

foreach ($text as &$one) {   // &が付いた
	$one = "ccc";
}
print_r($text);

結果:


ccc
ccc

以上

Perl の my と local :月並みな話

  • 2006-12-09 (土)
  • Perl

myとlocalってどう違うのよ?というのは常識ですが、
未だに誤解していたので下記。


my $a = 1; 
local $b = 2;  

check1(*a); 
check1(*b);  

check2($a); 
check2($b);  

exit;  

sub check1 { 
   local *test = $_[0];  

   print $test; 
   print "n"; 
}  

sub check2 { 
   local $test = $_[0];  

   print $$test; 
   print "n"; 
} 

#####################################

実行結果:

2
1
2
#####################################

my変数は型グロブに渡せない、と。

あとついでですが、
$/ = “\n”
です。
$/ = ‘\n’
じゃない。

キーワード:局所変数,リファレンス

ソートソーターソーテスト

ソートを考えてみる。

C:


#include <stdlib .h>
void qsort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *)); 


まあ、以上だな。最後の引数で比較関数へのポインタを渡してあげれば(人間)万事(塞翁が馬)OKだ。

C++:


#include <algorithm>
void std::sort(RanIt first, RanIt last, BinPred pr); 

まあ、STL使いましょうやってことでな。最後のprはなくてもいいが、比較関数を自分で用意する場合に渡してあげれば(人間)(略)。
たとえばvectorをソートするには下記のようにする。


#include <algorithm>
struct cmp { 
   bool operator()(int a, int b) const { 
      return b > a; // 昇順ソート 
      // return a > b; 降順ソート 
   } 
};

int main() { 
   std::vector< int > a; 
   a.push_back(10); 
   a.push_back(2); 
   a.push_back(5); 

   std::sort(a.begin(), a.end(), cmp()); 
   for(int i = 0; i < a.size(); i++) { 
      printf("%d\n",a[i]); 
   } 
} 

なんかしらんが、operator()をオーバーロードしたクラスを作ってインスタンスをstd::sortに渡せばいいらしい。ちなみにoperator()をオーバーロードしたクラスを関数オブジェクトって言うらしい。関数のオブジェクトだな。インスタンス名()みたいな使い方できるからな。
なんでboolなのか、なんであれで昇順なのかはしらん。
たぶん、strcmpみたいなんと同じような理屈で結果を解釈して、大きいほうの引数を後ろに配置するんだろう。関数ポインタでもいいらしい。

 
std::vector< char * >

みたいな場合は


struct cmp { 
   bool operator()(const char *a, const char *b) const { 
   for (int i = 0; a[i] != '\\0' && b[i] != '\\0'; i++) { 
      if (a[i] != b[i]) { 
         return b[i] > a[i]; 
      } 
   } 
   if (a[i] == '\\0' && b[i] != '\\0') { 
      return true; 
   } 
   return false; 
}; 

みたいな感じでいいんじゃないかと思った。

Perl:


@array = sort @array; 

まあ、こんな感じ。比較関数を定義したい場合は、


@array = sort { $a cmp $b } @array; 

見たいな感じ。
{ }の中が-1なら$aを前に、1なら$bを前にするということらしい。
→http://www.uopmu.ees.osakafu-u.ac.jp/~yabu/soft/perl.html
がとっても参考になる。今まで雰囲気で使ってた。
そういうわけで、[C++]のやつと同じような考え方でよさげ。
$aと$bは予約語みたいなもんなのか?

Java:
配列なら
Arrays.sort()
コレクションなら
Collections.sort()

まあ、これはJavadocあるし、こまるこたぁないだろう。
Comparatorを作れば好きなように比較できる

キーワード:
クイックソート,quick,sort,ファンクタ,アルゴリズム,演算子,ソート,sort,list,コンテナ,アダプタ

Home > Perl Archive

Search
Feeds
Meta
 合計:000240
 今日:0112 昨日:0128

Return to page top