Tags : Perl

このTagsの登録数:4件 表示 : 1 - 4 / 4

2008-05-11

リストと配列 その2 push/popとshift/unshift

昨日の続きで、リストと配列の実装を追います。今日はpush/popとshift/unshiftです。

まずは push/pop。配列の末尾に追加(push)、末尾から削除(pop)するものです。

store に処理を任せてますね。要素数を一個増やして末尾に追加。 次はpop。

array.c:103

要素数を一個減らして末尾を Nullstr で埋めて、それまで入ってた奴を戻すと。

さて次は配列の先頭を操作するshift/unshift。まずは先頭から一つ取り出す shift。

配列1つめから末尾までのデータを先頭にコピーして、 要素数を一つへらしつつ最後の要素に Nullstrを入れると。

次は先頭に追加するunshift。

array.c:116

ダミー要素を末尾に store を利用して追加した上で、 既存の要素ポインタを一個づつずらして言ってます。 引数が num であることからどうも aunshift 自体は要素を追加するわけではなくて、 先頭に追加する要素分だけスペースを空けるだけのようです。

というわけで aunshift が呼ばれている部分を見てみます。 呼び出し元はやはり arg.c の eval でした。

arg.c:2059

if AF_SPECIAL else のほうで aunshift で一つスペースを空けた上で、 ゼロ番目にastore しています。AF_SPECIALのほうでやってる do_unshift は配列を指定されたときかな?

do_unshift は同じく arg.c の中にありました。

arg.c:827

どうもそんな雰囲気です。最初に eval して配列を取得した上でその要素だけ指定された配列の先頭に aunshift、astore してます。

AF_SPECIAL というのが何かよくわかりません。arg の内容が問題です。

そろそろ eval の引数を詳しく見た方がいいかもしれません。また今度やります。

2008-05-10

リストと配列 その1

Perl学習の続き。今日は「はじめてのPerl」第3章、「リストと配列」の前半をやりました。

  • 3.1 配列の要素にアクセスする
  • 3.2 配列の特別なインデクス
  • 3.3 リストリテラル
    • 3.3.1 qw ショートカット
  • 3.4 リスト代入
    • 3.4.1 pop演算子とpush演算子
    • 3.4.2 shift演算子とunshift演算子
  • 3.5 配列を文字列の中に展開する
  • 3.6 foreach 制御構造
  • 3.7 Perl お気に入りのデフォルト: $_
    • 3.7.1 reverse演算子
    • 3.7.2 sort演算子

ひっかかったこと

  • push/pop と unshift/shift の違い
  • foreach で受ける変数は要素そのもので(参照?)、代入すると配列の要素自体が変更される
  • デフォルト変数 $_ はどこでいじられてるのか。内部表現

というわけで、とりあえず配列の内部表現を見てみようとまた Perl 1.0 のソースを見ます。

今回は簡単で、 array.h と array.c にまとまっているようです。

array.h (コメントは溝口)

atbl は perl.h で typedef struct atbl ARRAY; と定義されています。

では配列の作成をしてるらしい anew を見ます。

array.c:69

要素のバッファを多少余分にとって確保してることがわかります。ary_max を見ると4個分?1個は末尾マーカーかな?

safemalloc は util.c で定義されてて、引数チェックと、メモリが確保できなかったときの異常終了をしています。bzero は perl.h で #define bzero(s,l) memset(s,0,l); されてます。

配列からの取得は afetch

array.c:16

key が取得したい要素のインデックス番号。ここでみると数値を期待してますね。範囲外の要素は undef (Nullstr) を返すようです。

次、要素をいれるっぽい astore

array.c:26

オーソドックス。

配列の要素数以上のkeyにstoreされると、要素の枠を追加で確保して、要素数(ary_fill)、最大インデックス(ary_max)を更新した上で、上書きされる要素をstr_freeし、最後に上書きする。上書きがあった場合は1を返すようです。

要素枠の追加時は、既存の最大要素数の5分の1だけバッファを用意して、連続追加でもパフォーマンスが落ちないようにしてるみたいですね。

さて、ここまではわかりましたが、eval のなかからここまでどうやって呼ばれるんでしょう。あと配列の作成、解放のタイミングも気になります。

それらは奥が深そうなので、いったん気になるまま置いておいて、明日は pop / shift の違いと foreach, デフォルト変数周り、あとざっと reverse, sort も実装を見ようと思います。

2008-05-09

Perl の == と eq の実装の違い

Perl学習はじめました」でハマった以下の文字列比較に eq ではなく == を使ったコードがなぜ期待したとおりに動かなかったのか、調べてみました。

まず == は数値を期待していて、ここに文字列をわたすと Perl の文字列から数値への変換ルールで入力文字列が(だいたい) 0 になり、同様に 'quit' も数値変換されて 0 になるため 0 == 0 として必ず while を抜けるのは理解できました。

で、いい機会なので実装がどうなってるのかを見ようと思って Perl 5.8.8 のソースコードを見てみました。でも、O_SEQ というのが eq の opname だということ、 O_EQ というのが == の opname だということまではわかったのですが、そのバイトコードを処理する実装を見つけられませんでした。

そこで Perl 1.0 のソースコードなら単純だろうと当たりをつけて、このサイトから Perl 1.0 のソースコードをダウンロードして、いろいろ調べてみます。

でいろいろ追っかけると以下のような流れでバイトコードを実行してるみたいです。

  • main (perly.c)
  • cmd_execute (cmd.c)
  • eval (eval.c)

というわけで見つけた実装

まず文字列比較の eq

arg.c:1782 handy.h:20

というわけで単純に strcmp してました。

次、数値比較の ==

arg.c:1508

perl.h:56

str.c:83

ふむ。文字列を atof で double に変換しちゃうということで。なるほどねー

Perl 5.8 での実装が見つけられなかったのがちょっと悔しいけど、これからまた「はじめてのPerl」の続きをします。

ちなみに STR 型は str.h で定義されてました。でも最近のは utf-8 フラグとかいろいろ拡張されてるらしいので、あまり意味ないかもしれないなあ。

2008-05-08

Perl学習はじめました

YAPC::Asia 2008 に参加することにしたのですが、Perlを使ったことがほぼないので会社にあった「はじめてのPerl」を見ながらイチからやることにしました。あと1週間しかないけど。

というわけで最初にハマったこと。

  • while ループを抜けるのは last らしい。
  • use strict はみんなつけてるみたいだからとりあえずつけてみた
  • use strict にすると変数への代入で怒られるのでよく知らないまま my つけてみた
  • <STDIN>から読み込んで "quit" なら終了というのを書いてみたけど何を入れても終わっちゃうなあ
  • chomp を <STDIN> に対してやったら怒られた。ファイルハンドルよく分からない

2008-05-08 12:02 追記

$input == 'quit' が悪いとの指摘をいただきました。 == じゃなくて eq を使えとのこと。 ということで以下にしたら思い通りに quit のときだけ終了するようになりました。 指摘していただいたみなさん、ありがとうございます > @kimuchi @fuktommy @tokuhirom @kuzuha

eq は文字列比較用の演算子で、== は数値比較用の演算子らしいです。 最初はC言語的に strcmp しないといけないのかなとか思ってました。(strcmpあるのか知らない)

「初めてのPerl」本の目次をざっと見たけどこの辺の記述がなさそうだったので、本家サイトのドキュメンテーションから文字列の比較と演算子のあたりを調べようとおもいます。