Posts Tagged ‘Programming’

Windows/Linux両環境で動作するC言語ソースの一元管理をGitで行う

5月 2nd, 2010

gitでリポジトリからのチェックアウト時に文字コードを変換する » tune webの続きです。
【未解決】VisualStudioとgccでコンパイルできるソースのエンコーディング » tune webでも書きましたが、問題はVisualStudioはUTF-8 BOM有のみ、gccはUTF-8 BOM無しのみ扱え、両者で問題なく扱える文字コードが存在しない事でした。gitにあるsmudge/cleanを使うとチェックアウト/ステージ時に任意のフィルタを通すことができ、BOMの除去をここでやれば出来そうなのですが、文字コード変換ソフトのnkfではBOMの有り無し以外も書き変わってしまうことがあるため実用に使えなかったのがこれまででした。

git smudge/cleanはPro Git – Pro Git 7.2 Git のカスタマイズ Git の属性に詳しく解説されています。
git smudge/cleanの設定は前の日記に書きましたが、ここでも引用しておきます。

まず.gitconfigファイルに以下を追加

[filter "fixbom"]
clean = “/usr/bin/bom_util -a”
smudge = “/usr/bin/bom_util -d”

これでsmudgeでUTF-8 BOM無し、cleanでUTF-8 BOM有りになります。

これだけではダメで、フィルタ処理をかけるファイルを指定する必要が有ります。
gitの管理フォルダである.gitがあるトップディレクトリに.gitattributesファイルを以下の内容で作成し、git checkout -fします。

*.c filter=fixbom
*.h filter=fixbom

/usr/share/git-core/templates/info/attributes を作って上記内容を書いておくとclone時に.git/info以下にコピーされてgit cloneしただけで文字コード変換が動くようになります。

上記で指定している、BOMをつけ外しするプログラムは結局自作しました。単にファイル先頭のBOMを検知して追加・削除をするだけです。ハマったこととしてgit smudge/cleanのデータは標準入力から渡され、標準出力へ書いた内容で差し替えられます。外部コマンドとして起動されるのかと思っていましたが異なるようです。
BOMをつけ外しするプログラムを下に貼っておきます。(gistはこちら→gist: 386410 – GitHub)

#!/usr/bin/ruby

require "optparse"

mode = :help

opt = OptionParser.new
opt.on("-a", "Add BOM"){|v| mode = :add}
opt.on("-d", "Delete BOM"){|v| mode = :delete}
opt.parse!(ARGV)

case mode
when :add
 STDOUT.binmode
       lines = readlines
       unless lines[0] =~ /^\M-o\M-;\M-?/ then
               print "\xEF\xBB\xBF"
       end
       print lines

when :delete
 STDOUT.binmode
       lines = readlines
 lines[0].sub!(/^\xEF\xBB\xBF/, '')
 print lines

when :help
       STDERR.puts opt.help
end

VisualStudioでの開発をメインにしているので、リポジトリ内のソースをUTF-8 BOM有、改行コードをLFCRにして運用しています。
これでLinux環境での開発がひとつやりやすくなりました。

入門Git 入門git 実用Git

麻雀問題解いてみた

4月 4th, 2010

Mahjong set [04/04]
Creative Commons License photo credit: drdaeman

makeplex salon:あなたのスキルで飯は食えるか? 史上最大のコーディングスキル判定 (1/2) – ITmedia エンタープライズを解いてみました。ソースはtune’s itmedia_makeplex at master – GitHubに上げてありますのでリビジョンも見れるようになってます。最初のコミットを上げるまでに、面子の表示順の違いを抑えるところで詰まってチンイツの待ちを出力するプログラム – 107143955443560を参考にしました。参考にしましたというよりもC++のソースをRubyに移植しましたと言う方が近いほど同じ処理です。基本的なアルゴリズムは最後まで踏襲しています。

最初のコミットまでが3時間ぐらい、リファクタリングに1時間ぐらいです。ちょっと時間がかかりすぎですが、日常的に書いてないRubyで、日常的にプログラムしてない家のMacパソコン、プラグインが古いのか5分に1回落ちてしまうMacVimで時間がとられたと言い訳しておきます。ただの実力不足です、ごめんなさい。

成立する可能性がある面子を減らしながら、総当たりで探索しています。面子の順序違いによる重複表示を避けるのが結構面倒ですね。刻子を優先して探さなければならず、色々試行錯誤してみたのですが、参考にさせていただいたid:staebchenさんよりも良さそうな方法に達しませんでした。もっといいやり方があると思いますが、参考まで。

#!/usr/bin/ruby

class Array
  def sum
    s = 0
    self.each do |v|
      s += v
    end
    return s
  end
end

class Fixnum
  # 刻子
  def anko
    num = self+1
    "(#{num},#{num},#{num})"
  end

  # 順子
  def shuntsu
    num = self+1
    "(#{num},#{num+1},#{num+2})"
  end

  # アタマ
  def atama
    num = self+1
    "(#{num},#{num})"
  end

  # 単騎
  def tanki
    num = self+1
    "[#{num}]"
  end

  # リャンメン(ペンチャンも兼ねる)
  def ryanmen
    num = self+1
    "[#{num},#{num+1}]"
  end

  # カンチャン
  def kanchan
    num = self+1
    "[#{num},#{num+2}]"
  end

  # シャボ
  def shabo
    num = self+1
    "[#{num},#{num}]"
  end
end

#
# 雀牌を表す数字列を解析し、0〜8の配列に与えられた個数を入れて返す
#
def parsePai(str)
  pai = Array.new(9, 0)
  str.split(//).each do |p|
    pai[p.to_i-1] += 1
  end

  # 入力の妥当性チェック
  if pai.sum != 13 then
    # 与えられる牌は13枚
    raise RuntimeError
  else
    pai.each do |val|
      if val > 4 then
        # 1種類の牌が4枚より多いことはない
        raise RuntimeError
      end
    end
  end

  return pai
end

#
# 手牌を再帰的に解析し待ちを表示する
#
# 順子を刻子に優先して探索する。
# 順子はfromが9〜18の時に探索される
#
def analyzeTehai(pai, from=0, mentsu=[])
  shuntsu_phase = 9
  skip_anko_check = false

  if from >= 9 then
    skip_anko_check = true
    from -= shuntsu_phase
  end

  if pai.sum > 4 then
    # 面子を揃える

    # 刻子
    unless skip_anko_check then
      from.upto(8).each do |i|
        if pai[i] >= 3 then
          tmp = pai.dup
          tmp[i] -= 3
          analyzeTehai(tmp, i+1, mentsu+[i.anko])
        end
      end
    end

    # 順子
    from.upto(6).each do |i|
      if pai[i]>=1 && pai[i+1]>=1 && pai[i+2]>=1 then
        tmp = pai.dup
        tmp[i] -= 1
        tmp[i+1] -= 1
        tmp[i+2] -= 1
        analyzeTehai(tmp, i+shuntsu_phase, mentsu+[i.shuntsu])
      end
    end

  else
    # 残った4枚から待ちを絞る

    # アタマを探す
    0.upto(8).each do |i|
      if pai[i] >= 2 then
        tmp = pai.dup
        tmp[i] -= 2
        analyzeMachi(tmp, mentsu+[i.atama])
      end
    end

    # 面子(刻子 or 順子)があれば単騎待ち
    # 刻子
    unless skip_anko_check then
      from.upto(8).each do |i|
        if pai[i] >= 3 then
          tmp = pai.dup
          tmp[i] -= 3
          analyzeTanki(tmp, mentsu+[i.anko])
        end
      end
    end

    # 順子
    from.upto(6).each do |i|
      if pai[i]>=1 && pai[i+1]>=1 && pai[i+2]>=1 then
        tmp = pai.dup
        tmp[i] -= 1
        tmp[i+1] -= 1
        tmp[i+2] -= 1
        analyzeTanki(tmp, mentsu + [i.shuntsu])
      end
    end
  end
end

#
# 4枚から待ちを絞る
#
def analyzeMachi(pai, mentsu)
  0.upto(8).each do |i|
    # リャンメン(ペンチャン)
    if pai[i]==1 && pai[i+1]==1 then
      puts mentsu.sort.to_s + i.ryanmen
    end

    # カンチャン
    if pai[i]==1 && pai[i+2]==1 then
      puts mentsu.sort.to_s + i.kanchan
    end

    # シャボ
    if pai[i]==2 then
      puts mentsu.sort.to_s + i.shabo
    end
  end
end

#
# 単騎待ちの数字を調べて待ちを表示する
#
def analyzeTanki(pai, mentsu)
  # 単騎待ち
  0.upto(8).each do |i|
    if pai[i] == 1 then
      puts mentsu.sort.to_s + i.tanki
    end
  end
end

# 以下実際の処理
pai = parsePai ARGV[0] unless ARGV[0] == nil
analyzeTehai(pai)

WindowsのVisualStudioでpriority_queueに大量データを突っ込むと遅くなった

2月 11th, 2010

という問題が職場であったのでそのメモ。Releaseモードだとすぐ処理出来るのにDebugモードだと無限ループに陥ったかのような挙動を示す質問をもらったので調べてみました。priority_queueというのはある値に戻づいて順序が保たれるqueueです。便利ですね。

再現コードはこれでOK、C++編(標準ライブラリ) 第7章 priority_queueを参考にさせて頂きました。

#include
#include
using namespace std;

int main()
{
    priority_queue qu;

    // 要素を追加
    for(int i=0; i<100000; i++){
        qu.push( i );
    }

    // 先頭要素を取り出して出力
    cout << qu.top() << endl;

    return 0;
}

VisualStudio2005で試したところDebugモードだと動いてはいるのですが、とにかく時間がかかります。途中で処理を止めたところpushに時間がかかっているようです。そこでpushの先を追って行くと下記のようなコードが見つかります。

_Vector_iterator()
       {       // construct with null vector pointer
       }

#if _HAS_ITERATOR_DEBUGGING
_Vector_iterator(pointer _Ptr, const _Container_base *_Pvector)
       : _Mybase(_Ptr, _Pvector)
       {       // construct with pointer _Ptr
       }

#elif _SECURE_SCL
_Vector_iterator(pointer _Ptr, const _Container_base *_Pvector)
       : _Mybase(_Ptr, _Pvector)
       {       // construct with pointer _Ptr
       }

#else
_Vector_iterator(pointer _Ptr)
       : _Mybase(_Ptr)
       {       // construct with pointer _Ptr
       }
#endif /* _HAS_ITERATOR_DEBUGGING */

これを見るとdefineの定義状況によって、毎回値の妥当性チェックが呼ばれているようです。
これが過度にパフォーマンスが落ちていた原因でしょう。

_HAS_ITERATOR_DEBUGGINGはデバッグビルドでのみ有効になるようですが、_SECURE_SCLはリリースビルドでも残るようです。何用の定義なのかは軽くググッた限りではわかりませんでした。2007-07-04 – 新言語 Xtalを作る日記が見つかったぐらいです。

回避策としては両defineを0に設定すれば動かなくなり、通常のRelease/Debugの時間差で収まるようになります。

TDD Boot Camp行ってきた

12月 20th, 2009

TDD Boot Campに行ってきました。

◯t-wadaさんのTDD話
いつものように資料があとでアップされるのではないかと思います。
個人的にとってたメモを以下に載せておきます。

会場ではt-wadaさんのテスト駆動開発本が見れるようになっていました。かなり読み込まれた形跡があって、TDD愛が伝わります。訳がいまいちというAmazonレビューで躊躇していましたが購入して読んでみようと思います。

Test Drivenの作者 Lasseさんの講演
レシーバーが足りなそうだったので英語を聞きとるのに必死になってしまい、話の所々がフォローできず。
twitterの#tddbcタグを見る方が参考になるかも。
内容はレガシーコード改善ガイドの紹介と、Coberturaのライブハッキング。Eclipseのコードさばきが見事すぎて見とれてしまいます。

◯TDD実践編
ペアを組んでサイズ制限付きのハッシュを作成しました。
いつもだとつい実装を先に書いてしまい、テストが後回しになったり、テストに抜けや不足ができてしまうのですが、ペアプロだったこともあって、TDDの基本的な流れが改めて抑えられたと思います。

ソースはgithubに上げてあります。

http://github.com/tune/lrucache

TDDを知るには完成形のソースではなくて、その過程を学ぶことが大事ですね。gitを途中から使っておきながらあまりコミットできてません。次はもうちょっと気を配らないと。

周りにすごい人もたくさんいました。

  • ペアプロでのソースの受け渡しをgithub/gistでやっちゃう
  • gistにソースあげたし、CIもやるか→ローカルHudsonでCIまで
  • 一人で時間あったしやってみたよとLasseさん、しかもJavaとRubyの2言語。Rubyのテストをみんなで見たけどテストが完結で読みやすい、ここまでできるのかと目から鱗でした。

会社ではC言語なのでCUnitを使ってテストは書いていますが、我流になっているところがあって、今日は行けてよかったです。

  • テストも製品ソースと同じく綺麗に書く → 主張はよく耳にするけど実際にLasseさんやその他の人のソースをみると上には上がいて、きちんと実践できてます。
  • 言語でテストの読みやすさ、書きやすさに差ができるわけではない。Javaでも簡潔にかけるし、Rubyでもどうしようもないコードはかけてしまう。

楽しいイベントを開催していただきありがとうございました。

gitでリポジトリからのチェックアウト時に文字コードを変換する

9月 30th, 2009

ようやく実現できたのでやり方をメモ。
設定ファイルで拡張子に基づくフィルタリングをすればOK。

ProGitの情報によるとリポジトリから取ってくるときをsmudge、リポジトリに突っ込むときをcleanと呼ぶらしい。

以下はリポジトリ内のソースファイルがUTF-8 BOM有、改行コードがLFCRの場合の設定例。文字コード変換はnkfを使っています。
WindowsのVisualStudioに合わせると上記設定が望ましいが、Linux環境でgccを使うにはBOM無しにして、改行コードをLFにする必要がある。
まず.gitconfigファイルに以下を追加する

[filter "fixencoding"]
clean = “/usr/local/bin/nkf -w8 -Lw”
smudge = “/usr/local/bin/nkf -w -Lu”

これでsmudgeでUTF-8 BOM無し/LF、cleanでUTF-8 BOM有り/LFCRとなる。

これだけではダメで、フィルタ処理をかけるファイルを指定する必要が有る。
gitの管理フォルダである.gitがあるトップディレクトリに.gitattributesファイルを以下の内容で作成し、git checkout -fする必要が有る。

*.c filter=fixencoding
*.cpp filter=fixencoding
*.cxx filter=fixencoding
*.h filter=fixencoding
*.hxx filter=fixencoding
*.txt filter=fixencoding
Makefile filter=fixencoding

/usr/share/git-core/templates/info/attributes
を作って上記内容を書いておくとclone時に.git/info以下にコピーされてgit cloneしただけで文字コード変換が動くようになる。

動かすにあたって問題となったのはgitでチェックアウトしただけで編集されたことになってしまうファイルが多々発生したことです。原因はいろいろあったのですが
リポジトリインデックス内のファイル文字コードがバラバラだった(BOM無しファイルが紛れ込んでいた とか)
ファイル内の文字に半角カナがあるとダメらしい。
ファイル内の文字に機種依存文字(実際にあったのは丸数字)があるとダメらしい。

git statusなどで編集が有ったかどうかはインデックス内の状態と比較するからcleanして元々の状態と変わってしまうと当然チェックアウトしただけで編集されたと勘違いされてしまうファイルができてしまう ということですね。
WEB DB Press Vol.50で解説されていたgitの内部データ構造を知ってようやく理解できました。

Subverisonだと文字コードをうまく変換する機構も無いのでgitをかましてやるのが便利ですね。

Buildbotはディレクトリの移動ができない

7月 30th, 2008

会社で Buildbotを設定していた困ってしまったのですが、この手のCIツールはディレクトリの移動ができないのでしょうか?

CatalystアプリをbuildbotでContinuous Integration – dann@catalyst – Catalystグループを参考にBuildbotの設定ファイルを書いていたのですが、Subversionリポジトリからtrunkを引っ張ってきてトップディレクトリにあるmakeファイルを使ってプロジェクトをビルドする設定は

from buildbot.process import factory
from buildbot.steps import source,shell
f1 = factory.BuildFactory()
f1.addStep(source.SVN,
svnurl=source_code_svn_url,
mode=”clobber”)
f1.addStep(shell.ShellCommand, command=['make'])
f1.addStep(shell.ShellCommand, command=['make', 'test'])

と書けますが、自分が設定していたプログラムのMakefileは”project/Makefile”にあったのでcdコマンドを加えれば行けるのかと

f1.addStep(shell.ShellCommand, command=['cd', 'project'])
f1.addStep(shell.ShellCommand, command=['make'])
f1.addStep(shell.ShellCommand, command=['make', 'test'])

と書いたところBuildbotがエラーを吐いて止まってしまいました。作業ディレクトリ変更用の機能があるのかとマニュアルを一通り見てみたのですがどこにも見当たりません。

確かにConfigureスクリプトやMakefile、インストールプログラムはプロジェクトのトップに置くのが一般的なので問題になった自分のプロジェクトが変なだけかもしれません。

ビルドテスト用スクリプトを自作するか、プロジェクトの構成を変更してしまうか悩んでいるところです。
自作はさけたいんだけどなぁ。

Review Board

3月 21st, 2008

http://www.review-board.org/#kwout_scb6t94x" height="282" width="388" />

Review Board via kwout

Pythonで作られたソースコードのレビュー支援を行ってくれるソフト。VMWareの開発にも使われているとか。

自分が書いたソースを見てもらったり、人のソースを見てコメントしたりってのは割りとよくある要求だと思うんだけどオープンソースでこういうツールがあるのはすごく助かるかも。会社のサーバに今度設定してみよう。

Signal Analysis/Processing Software

9月 3rd, 2007



デジタル信号処理のライブラリ一覧、目的別にかなりの数がリストアップされています。

今は使う予定はないけど車輪の再発明をしないように”いつか使う”。