TOP>カテゴリ

2011年08月10日

create_functionのアレ

PHP5.2以下のPHPを使ってアレしてる人なんか、なんだかんだでcreate_function でアレしたりしてアレするでしょう?

でも、これって調子こいてJavascriptやLISPのノリや、Perl のサブルーチンのリファレンスのつもりで使ってるとアレしちゃうんですよ。そもそもそこまで便利じゃないので、そのノリで使えないっていうのはアレしちゃってください。で、create_functionをよくアレしてる人は、以下のコード実行してみてください。

for ( $i = 0; $i < 1000000; $i++ ){
  hoge ('$x', 'return $x' );
}
function hoge ( $x, $y ) { return create_function ( $x, $y ); }

スコープぬけようがcreate_functionした関数は破棄しないし、同じコードだろうが問答無用で新しい関数定義するという男らしい仕様でアレしているので使用可能メモリを一瞬でオーバーして男らしく散り際をわきまえてアレしちゃうんですね。でも、そんなアホみたいな回数をcreate_functionするような場所で回さないもんへへーんとかアレしてる方、

// バッチコントローラの階層
for ( $i = 0; $i < 10; $i++ ) {
  // モデルの階層
  for ( $j = 0; $j < 100; $j++ ) {
    // そのモデルの下請けモデルの階層
    for ( $k = 0; $k < 1000; $k++ ){
      hoge ('$x', 'return $x' );
    }
  }
}
function hoge ( $x, $y ) { return create_function ( $x, $y ); }

これ位の計算量は普通に有り得るでしょう。うげーやべぇって思ってアレしました? 思ってないなら思った方が良いです。どう考えても思えないなら、ここでは先に進まなくてアレしちゃうので思ってください。

で、こうしてみることにしました。

function create_function_ex ( $args, $code )  {
  return LambdaFactory::create ( $args, $code );
}
class LambdaFactory {
   private static $_Cache = array ();
   public function create ( $args, $code )  {
     if ( !self::$_Cache[$args . "//\/i/ix0b" . $code]  ) self::$_Cache[$args."//\/i/ix0b". $code] = create_function ( $args, $code );
     return self::$_Cache[$args . "//\/i/ix0b" . $code];
   }
}

です。
じゃあ、これと同じファイルに

for ( $i = 0; $i < 10000000; $i++ ){
   $fnc = create_function_ex ( '$x', 'print "$x//\/i/in";' );
   $fnc ( $i );
}

を追記して実行してみてください。
PHPさん、ここでは男らしく踏ん張ってくださいましたね。
これで安心してcreate_function 多用した可読性の低いコードを
ハッピーに書けますね!

※勿論、5.3以降のPHPなら create_function なんか使わずに。 function ( $x ) { return $x; } というようにしましょう。

Share and Enjoy: Digg del.icio.us Google Bookmarks Tumblr email Facebook FriendFeed

2011年06月23日

[誰得]SVNクライアントのラッパが大きくなりました

SVNクライアントのラッパーが使っているうちに大きくなりましたので、晒します。使い方はソース読んでください。

正直、ここまで大きくなるなら、pm にしてhelpとコマンドをセットと略式コマンドに対応出来たほうが良かったんじゃないかって思ってます。

#!/usr/bin/perl
use File::Basename;
use strict;

## 環境設定
sub say { print @_ , "//\/i/in" }
my $SVN   = 'svn';
my %HOSTS = (
  host1  => {
    host       => 'http://svn-server.domain/path/to/repostiry',
    local_dir  => '/local/working/copy/dir/home',
    default    => 1,
  },
  host2 => {
    host       => 'http://svn-server.domain/path/to/repostiry',
    local_dir  => '/local/working/copy/dir/home',
    default    => 0,
  },
);

my $SVN_ENV    = {};
## ここからロジック

our %COMMANDS =  (
  ##
  ## 対象リポジトリのブランチのリストを表示
  ##
  ls        => sub {
    `$SVN list '$SVN_ENV->{host}/branches'`
  },

  ##
  ## 対象ブランチのベースリビジョンを取得
  ##
  base_rev  => sub {
    `$SVN log '$SVN_ENV->{host}/branches/$_[0]' --stop-on-copy --quiet`
  },

  ##
  ## update
  ##
  update       => sub {
    if ( $_[0] ) { `$SVN update $SVN_ENV->{local_dir}/branches/$_[0]'` }
    else         { `$SVN update` }
  },

  ##
  ##  ブランチの削除
  ##
  del       => sub {
    `$SVN del -m 'del $_[0]' '$SVN_ENV->{HOST}/branches/$_[0]'`
  },

  ##
  ##  ブランチの移動
  ##
  mv        => sub {
    `$SVN mv -m 'mv $_[0]' '$SVN_ENV->{host}/branches/$_[0]' '$SVN_ENV->{host}/branches/$_[1]'`
  },

  ##
  ##  ブランチを作成してチェックアウトしてくる
  ##
  create => sub {
    my $branch_name = shift;
    if ( `$SVN log $SVN_ENV->{host}/branches/$branch_name 2>&1` =~ /not +found/ ){
      print 'creating branch ' . $branch_name . '.....' . "//\/i/in";
      print `$SVN cp -m 'make $branch_name' $SVN_ENV->{host}/trunk $SVN_ENV->{host}/branches/$branch_name`;
    }

    if ( !-d "$SVN_ENV->{local_dir}/branches/$branch_name" ) {
      print 'checking out branch ' . $branch_name . '.....' . "//\/i/in";
      print `$SVN co $SVN_ENV->{host}/branches/$branch_name $SVN_ENV->{local_dir}/branches/$branch_name` ;
    } else {
      print 'updating branch ' . $branch_name . '.....' . "//\/i/in";
      print `$SVN update $SVN_ENV->{local_dir}/branches/$branch_name`;
    }

    if ( $SVN_ENV->{server_dir} && !-f "$SVN_ENV->{local_dir}/branches/$branch_name/.aurc" ) {
      print 'making dot_aurc_copy ' . $branch_name . '.....' . "//\/i/in";
      my $TMPL = "admin//\/i/i@sc-test01.aa-dev.com//\/i/in" .
      "/Volumes/sourcecodes/snjn_project/sc_src/key/sc-test01(113.38.98.203)/id_rsa//\/i/in".
      "$SVN_ENV->{server_dir}//\/i/in";
      my $path = "$SVN_ENV->{local_dir}/branches/$branch_name";
      $path =~ s#(?:/$)##;
      if ( $path ) { -d $path or die 'ディレクトリが無いような気がしますよ' . $path; }
      else       { $path = '.' }
      open F, '> ' . $path . '/.aurc'; print F $TMPL; close F;
      print $path . 'に.aurcを作りました'. "//\/i/in";
    }
  },

  ##
  ## mergeコマンド(対象の作業ディレクトリで実行してください)
  ##
  merge => sub {
    our %COMMANDS;
    my ( $path, $rev, $dry_run ) = @_;
    print $COMMANDS{update}->() . "//\/i/in";
    $path or die 'パスの指定がありません' ;
    if ( !$rev ) {
      my @log =
        map  { ($_) = $_ =~ /(//\/i/id+)/; }
          grep { $_=~///\/i/iS/ }
            split /^//\/i/is*-+$/m, $COMMANDS{base_rev}->( $path );
      $rev = pop @log or die 'ブランチ間違ってない?';
    }
    my $MERGE_COMMAND = "$SVN merge -x -w ";
    $dry_run and $MERGE_COMMAND .= ' --dry-run ';
    $MERGE_COMMAND .= " -r $rev:HEAD $SVN_ENV->{host}/branches/$path";
    print `$MERGE_COMMAND`;
  },

  ##
  ## ひとつ前のリビジョンに戻す
  ##
  rollback => sub {
    if ( $_[0] ) { `$SVN merge -x -w -r HEAD:PREV $SVN_ENV->{host}/branches/$_[0]` }
    else         { `$SVN merge -x -w -r HEAD:PREV` }
  },
);

## パラメータ処理(一個目はいつでもコマンド)
my $cmd    = shift @ARGV;
$COMMANDS{$cmd} or die '指定のコマンド無いっす';

## 対象ホストの解析とか
my %params = @ARGV;
if ( $params{-h} && $HOSTS{$params{-h}} ) {
  $SVN_ENV    = $HOSTS{$params{-h}};
}
else {
  my ( $key ) = grep { $HOSTS{$_}->{default} } keys %HOSTS;
  $SVN_ENV = $HOSTS{$key};
}
/^-/ and delete $params{$_} for keys %params;

## 実行
say $COMMANDS{$cmd}->( %params );
Share and Enjoy: Digg del.icio.us Google Bookmarks Tumblr email Facebook FriendFeed

2011年05月30日

[誰得]svnクライアントのラッパ

また、svnクライアントのラッパ。
ブランチ操作に特化したもの。
ブランチ作ってチェックアウトしてくる奴とか、
マージする奴とかと統合したい。

[perl]
#!/usr/bin/perl

## 環境設定
sub say { print @_ , “//\/i/in” }
my $SVN = ’svn’;
my %HOSTS = (
user => {
host => ‘http://hoge.fuga’,
default => 1,
},
admin => {
host => ‘http://hoge.fuga’,
default => 0,
},
);

## ここからロジック
my $HOST = ”;
my %COMMANDS = (
ls => sub {
`$SVN list ‘$HOST/branches’`
},
base_rev => sub {
`$SVN log ‘$HOST/branches/$_[0]‘ –stop-on-copy –quiet`
},
del => sub {
`$SVN del -m ‘del $_[0]‘ ‘$HOST/branches/$_[0]‘`
},
mv => sub {
`$SVN mv -m ‘mv $_[0]‘ ‘$HOST/branches/$_[0]‘ ‘$HOST/branches/$_[1]‘`
}
);

## パラメータ処理(一個目はいつでもコマンド)
my $cmd = shift @ARGV;
$COMMANDS{$cmd} or die ‘指定のコマンド無いっす’;

## 対象ホストの解析とか
my %params = @ARGV;
if ( $params{-h} && $HOSTS{$params{-h}} ) {
$HOST = $HOSTS{$params{-h}}->{host};
}
else {
( $HOST ) =
map { $HOSTS{$_}->{host} }
grep { $HOSTS{$_}->{default} }
keys %HOSTS;
}
/^-/ and delete $params{$_} for keys %params;

## 実行
say $COMMANDS{$cmd}->( %params );

1;
[/perl]

Share and Enjoy: Digg del.icio.us Google Bookmarks Tumblr email Facebook FriendFeed

2011年03月14日

[誰得]SVNのブランチを切ってチェックアウトするスクリプト

よくあるtrunkをブランチに切るパターンを自動化。
さらに前に紹介したdot_aurc_copy.plもついでにキック。
ディスク勿体無いけど、switchは経験上危険なので問答無用でチェックアウト。

[perl]
#!/usr/bin/perl -w
use File::Basename;

my $ROOT
= ‘[svnのリポジトリのパス]‘;
my $LOCAL_ROOT
= ‘[ローカルのパス]‘;
my $SVN
= ’svn’;
my $DOT_AURC_COPY
= dirname ( $0 ) . ‘/dot_aurc_copy.pl’;

my $branch_name = shift ( @ARGV )
or die ‘ブランチ名が無い’;

if ( `$SVN log $ROOT/branches/$branch_name 2>&1` =~ /not +found/ ){
print ‘creating branch ‘ . $branch_name . ‘…..’ . “//\/i/in”;
print `$SVN cp -m ‘make $branch_name’ $ROOT/trunk $ROOT/branches/$branch_name`;
}

if ( !-d “$LOCAL_ROOT/branches/$branch_name” ) {
print ‘checking out branch ‘ . $branch_name . ‘…..’ . “//\/i/in”;
print `$SVN co $ROOT/branches/$branch_name $LOCAL_ROOT/branches/$branch_name` ;
} else {
print ‘updating branch ‘ . $branch_name . ‘…..’ . “//\/i/in”;
print `$SVN update $LOCAL_ROOT/branches/$branch_name`;
}

if ( !-f “$LOCAL_ROOT/branches/$branch_name/.aurc” ) {
print ‘making dot_aurc_copy ‘ . $branch_name . ‘…..’ . “//\/i/in”;
print `perl $DOT_AURC_COPY branches/$branch_name`;
}
[/perl]

Share and Enjoy: Digg del.icio.us Google Bookmarks Tumblr email Facebook FriendFeed