Powered By Blogger

2013年5月3日金曜日

REPLのラッパー

備忘録

Guile(schemeの処理系)のインタープリタも↑↓のコマンド履歴とか対応してなくて使いにくい。
履歴遡ろうと↑とか押しても

> guile

scheme@(guile-user)> ^[[A^[[A^[[1;2D

みたいな事になる。

rlwrapを使えばOK。
インストールはubuntuではaptitudeで出来た。
windowsのcygwinでもsetupでrlwrapで検索しインストール出来る。
インストールの詳細は省く。簡単に分かるはず。

使い方は簡単
起動したいインタープリタのコマンドの前にrlwrapを付けるだけ。

> rlwrap guile

2012年6月15日金曜日

Mac: Clojureコンソール(clj)を使いやすくする

Clojureはコンソールからcljで起動出来る。

$ clj
Clojure 1.4.0
user=>

これが↑↓キーで入力履歴が出なかったりしてイマイチ使いづらい。
Clojureのビルドツールleiningenを使って起動すると↑↓キーで入力履歴が出る様になる。
ちょっとClosureを試してみるだけなのでビルドツールは要らないと思っていたが、
入力履歴が使えないのは不便なのでleiningenを使ってcljを起動してみると
入力履歴が使える様になった。
因みにClojureもleiningenもhomebrewからインストール出来る。

$ lein repl
REPL started; server listening on localhost port
user=> 

2012年6月4日月曜日

ペアノ算術とProlog

ペアノ算術をPrologで表現してみる。

元ネタ
第3巻『数学ガール/ゲーデルの不完全性定理』
Prolog by Example: Peano Arithmetics

Swi-Prologを使います。

と…その前にペアノの公理で自然数を定義しよう。

ペアノの公理1
0は自然数である



Prologコード
natural_n(0).

ペアノの公理2
どんな自然数nに対しても後続数s(n)は自然数である



Prologコード
natural_n(s(N)) :- natural_n(N).

ペアノの公理3
どんな自然数nに対してもs(n)は0ではない



Prologコードは無し

ペアノの公理4
どんな自然数m,nに対しても、s(m)=s(n)ならばm=nである



Prologコードは無し
ペアノの公理5
自然数nに対する述語P(n)で、次の2つが成り立つとする。
・P(0)である。
・どんな自然数kに対しても、P(k)ならばP(s(k))である。
このときどんな自然数nに対してもP(n)が成り立つ。



Prologコードは無し
ここまでのPrologコードまとめ
natural_n(0).
natural_n(s(N)) :- natural_n(N).

実行してみる。
?- natural_n(X).
X = 0 ;
X = s(0) ;
X = s(s(0)) ;
X = s(s(s(0))) ;
X = s(s(s(s(0)))) ;
X = s(s(s(s(s(0))))) .

自然数が出来た!
「これが自然数?」と言いたい人もいるでしょうが、これで良いんです!!
s(0)を1
s(s(0))を2
s(s(s(0)))を3
というふうに読み替えて下さい。


さて、ここからがペアノ算術

足し算の公理1
どんな自然数nに対しても、n + 0 = nが成り立つ


Prologコード
sum(N,0,N).

余談ですが自然数に0を含める考え方(0,1,2 ...)と含めない考え方(1,2,3 ...)があり、上の足し算の公理は前者です。
後者の場合の公理はこうなります。
足し算の公理1(0は自然数に含めない)
どんな自然数nに対しても、n + 1 = s(n)が成り立つ


"0"という自然数は存在しないので、こうするしかないのでしょう。
この場合は、
s(1)を2
s(s(1))を3
s(s(s(1)))を4
というふうに読み替えて下さい。

Prologコード
sum(N,1,s(N)).

足し算の公理2



Prologコード
sum(N,s(M),s(K)) :- sum(N,M,K).

Prologコードまとめ
sum(N,0,N).     %In case of natural numbers contain 0.
sum(N,1,s(N)).  %In case of natural numbers contain NO 0.
sum(N,s(M),s(K)) :- sum(N,M,K).  %K=N+M

X=3+2を実行してみます。

?- sum(s(s(s(0))),s(s(0)),X).
X = s(s(s(s(s(0))))) ;
false.

X=5になりました。
おっと、ここで作った自然数は"5"とは言わないんでしたね。
s(s(s(s(s(0)))))ですね、でも長ったらしいので5と言う事にします。

因みに、0は自然数に含めない場合で
X=4+3を実行してみます。

?- sum(s(s(s(1))),s(s(1)),X).
X = s(s(s(s(s(s(1)))))) ;
false.

X=7になりました。

0を自然数に含める場合は、例えば2をs(s(0))と表現すると
sum(N,s(M),s(K)) :- sum(N,M,K).
を再帰的に評価し、最終的に
sum(N,0,N).
にマッチして、再帰の階段を戻って結果が出る訳です。

0を自然数に含めない場合は、例えば2をs(1)と表現して、最終的に
sum(N,1,s(N)).
の方にマッチするのです。

ではここからは掛け算です。
掛け算の公理1
0を自然数に含める場合

Prologコード
prod(M,0,0).

0を自然数に含めない場合

Prologコード
prod(M,1,M).

掛け算の公理2



Prologコード
prod(M,s(N),P) :-
     prod(M,N,K),
     sum(K,M,P).   % K=M+N

Prologコードまとめ
prod(M,0,0).    %In case of natural numbers contain 0.
prod(M,1,M).    %In case of natural numbers contain NO 0.
prod(M,s(N),P) :-
    prod(M,N,K),
    sum(K,M,P).   % K=M+N

実行

0を自然数に含める場合、X=2*3

?- prod(s(s(0)),s(s(s(0))),X).
X = s(s(s(s(s(s(0)))))) ;
false.

X=6となりました。


0を自然数に含めない場合、X=2*4

?- prod(s(1),s(s(s(1))),X).
X = s(s(s(s(s(s(s(1))))))) ;
false.

X=8となりました。

2012年4月21日土曜日

Blocks関連のバグがデバッグコンパイルで再現しないケース

TOMOHISAさんのブログ記事
BLOCKSを使った記述で、リリースビルドのみにクラッシュする事例
これは元のコードの問題はスタック領域上のブロックオブジェクトをスコープ外で使ってしまったという割と有りがちなバグだったのですが、 念のため検証してみました。

ところがブロックオブジェクトのデフォルトの生成場所(スタック領域かヒープ領域か)がデバッグコンパイル時にはどうも違うらしくて、 そういう挙動を知らずにデバッグコンパイルで検証してしまった為にハマってしまいました。

今回のはちゃんとリリースコンパイルでもテストしないといけないという教訓です。

ブロックのデフォルトの生成場所はスタック領域なのですが、デバッグだろうがリリースだろうがそれは同じだと思い込んでいました。

特に__block属性付き変数をキャプチャするとブロックの生成場所は最初からヒープになるみたいです。何でかな?…

検証コード
コードはTOMOHISAさんのものですが、
NSLog(〜〜,block) の箇所とコメントは私が検証の為に追加しました。
因みに_tweetはこのコードが属するオブジェクトのメンバ関数です。
ARCです。
//
    NSMutableArray *arrRows = [NSMutableArray arrayWithCapacity:0];
    { //検証コード1
#warning this code only crash on Release Build.... Don't use this
        NSMutableDictionary * dicRow = [NSMutableDictionary dictionaryWithCapacity:0];
        [dicRow setValue:NSLocalizedString(@"Pattern 1 Crash",nil) forKey:kDicKeyLinkPopCellText];
        [arrRows addObject:dicRow];
        dispatch_block_t block = ^{
            
            NSString *str = [NSString stringWithFormat:@"%@",[_tweet valueForKey:@"text"]];
            
            [[UIPasteboard generalPasteboard] setString:str];
            [[JTCAppNotificationManager sharedManger] startTimerNotificationWithMessage:NSLocalizedString(@"Copy succeeded", @"Copy succeeded") dulation:2.5 iconName:@"w17-check.png"];

        };
        NSLog(@"Pattern 1 %@",block);
        [dicRow setValue:block forKey:kDicKeyLinkPopBlock];
    }
    { //検証コード2
        NSMutableDictionary * dicRow = [NSMutableDictionary dictionaryWithCapacity:0];
        [dicRow setValue:NSLocalizedString(@"Pattern 2 __block",nil) forKey:kDicKeyLinkPopCellText];
        [arrRows addObject:dicRow];
#warning this code is not the way recommended how to use __block ... not recommended
        __block id bt = _tweet;
        dispatch_block_t block = ^{
            
            NSString *str = [NSString stringWithFormat:@"%@",[bt valueForKey:@"text"]];
            
            [[UIPasteboard generalPasteboard] setString:str];

            [[JTCAppNotificationManager sharedManger] startTimerNotificationWithMessage:NSLocalizedString(@"Copy succeeded", @"Copy succeeded") dulation:2.5 iconName:@"w17-check.png"];
            
        };
        NSLog(@"Pattern 2 %@",block);
        [dicRow setValue:block forKey:kDicKeyLinkPopBlock];
    }
    { //検証コード3
        NSMutableDictionary * dicRow = [NSMutableDictionary dictionaryWithCapacity:0];
        [dicRow setValue:NSLocalizedString(@"Pattern 3 only declare bt crash",nil) forKey:kDicKeyLinkPopCellText];
        [arrRows addObject:dicRow];
#warning this code only crash on Release Build.... Don't use this
        id bt = _tweet;
        dispatch_block_t block = ^{
            
            NSString *str = [NSString stringWithFormat:@"%@",[bt valueForKey:@"text"]];
            
            [[UIPasteboard generalPasteboard] setString:str];
            
            [[JTCAppNotificationManager sharedManger] startTimerNotificationWithMessage:NSLocalizedString(@"Copy succeeded", @"Copy succeeded") dulation:2.5 iconName:@"w17-check.png"];
        };
        NSLog(@"Pattern 3 %@",block);
        [dicRow setValue:block forKey:kDicKeyLinkPopBlock];
    }
    { //検証コード4
        NSMutableDictionary * dicRow = [NSMutableDictionary dictionaryWithCapacity:0];
        [dicRow setValue:NSLocalizedString(@"USE THIS:Pattern 4 declare bt and copy block",nil) forKey:kDicKeyLinkPopCellText];
        [arrRows addObject:dicRow];
        id bt = _tweet;
        dispatch_block_t block = ^{
            
            NSString *str = [NSString stringWithFormat:@"%@",[bt valueForKey:@"text"]];
            
            [[UIPasteboard generalPasteboard] setString:str];
            
            [[JTCAppNotificationManager sharedManger] startTimerNotificationWithMessage:NSLocalizedString(@"Copy succeeded", @"Copy succeeded") dulation:2.5 iconName:@"w17-check.png"];
        };
        NSLog(@"Pattern 4 %@",block);
        [dicRow setValue:[block copy] forKey:kDicKeyLinkPopBlock];
    }
    
    NSMutableArray *sections = [NSMutableArray arrayWithObject:arrRows];
    TOVLinkPopoverViewController *controller= [[TOVLinkPopoverViewController alloc] init];
    controller.arrayLink = sections;




デバッグビルドでのログ出力

2012-04-21 13:49:22.109 TOVPopoverSample[10592:707] Pattern 1 <__NSMallocBlock__: 0xfe44ee0>
2012-04-21 13:49:34.734 TOVPopoverSample[10592:707] Pattern 2 <__NSMallocBlock__: 0xfe45b20>
2012-04-21 13:49:39.624 TOVPopoverSample[10592:707] Pattern 3 <__NSMallocBlock__: 0x13dc50>
2012-04-21 13:49:43.224 TOVPopoverSample[10592:707] Pattern 4 <__NSMallocBlock__: 0xfe45110>

見ての通り4パターンともmallocされたブロック、つまりヒープ上に生成されてます。 これではバグの検証は出来ませんね。
別に全部問題ないじゃん、みたいに見えてしまいます。
「ほらスタック領域にブロック生成されるからヒープにコピーしないと」と言う結論を期待したのに「なんで…」と数時間悩んでしまいました。


リリースビルドでのログ出力

2012-04-21 13:10:39.912 TOVPopoverSample[10504:707] Pattern 1 <__NSStackBlock__: 0x2fe54510>
2012-04-21 13:10:54.860 TOVPopoverSample[10504:707] Pattern 2 <__NSMallocBlock__: 0x149250>
2012-04-21 13:11:18.754 TOVPopoverSample[10504:707] Pattern 3 <__NSStackBlock__: 0x2fe544c4>
2012-04-21 13:12:11.226 TOVPopoverSample[10504:707] Pattern 4 <__NSStackBlock__: 0x2fe544ac>

ほぼ予想通りStackBlockとなってます。
このスタックブロックを辞書に登録しておいてスコープ外で使おうとしているのでバグだね、と分かる訳です。
ただパターン2、__block属性の変数をキャプチャしたケースだけはMallocBlockとなってます。
なぜこのケースではヒープに生成されるのか理由は分かりませんが、この為に問題無いかの様に動いたのですね。


結論
Blocksはデバッグビルドでのテストだけで満足してはならない。
リリースビルドでも入念にテストすべきである。


因みに。。。
このケースでは正解は4なのですが、いつもコピーする方が良い訳ではないので注意が必要です。
ヒープにコピーしたらしたで、リークだとか別の心配をしないといけなくなりますから。
4ではブロックをスコープ外で使いたい為にヒープにコピーしています。
ただし!!
ヒープにコピーしたブロックを使い終わったら忘れない様にリリースしないといけません。
今回のはARCなので問題なさそうではありますが。

__blockをキャプチャするとヒープブロックになるのを知ってたのなら2も正解じゃん、
という意見もあるかも知れませんが、そういう裏ワザ的なのはここでは不正解とさせて頂きます(笑)

2012年3月24日土曜日

Objective-C++でboostを使う時のリンクエラー

XCodeでObjective-C++でboostを使う時に以下のリンクエラーが出た場合。

Undefined symbols for architecture x86_64:
"boost::system::generic_category()"

boostの.dylibが足りないらしい。
libboost_system-mt.dylib をTARGETSのBuild PhaseのLink Binary With Librariesに追加する。

2012年3月23日金曜日

Objective-cでコルーチン?!

「C10K問題を回避するためにコルーチンを使う方法」というブログ記事があって、なるほどと思いObj-cに移植しようとしたのですが、どうもコルーチンクラスをサンプルに特化させていじっている様で汎用性が無さそうであまり使い勝手よくありません。

仕方ないので元記事のソースはあまり参考にせずに移植と言うより作ってみました。
→ここgithub

C10K問題を回避するためには、コネクション数とスレッド数が比例しないような設計をしなければならない事からコルーチンが有効らしいです。
実際に通信するコードを書ければ素晴らしいですが、面倒くさいので省いてコルーチンの動作を確認するだけです。

まぁObj-cのコルーチンはCSCoroutineとかあるみたいですが、setjmp, longjmp使ってるのが気持ち悪かったりします。(食わず嫌い)

yieldではsetjmp, longjmp使わず普通にreturnで抜けているのでローカル変数等は保存されません。
なのでローカル変数の代わりにメンバ関数を使っています。
そういう作りなのでコルーチンからサブコルーチンを呼ぶ時には新たなコルーチンオブジェクトを作成します。
色々と制約あって万能ではありませんが、ちょこっとコルーチンを使いたいぐらいなら丁度よいかも。

使用例
二組のコルーチンを使って擬似マルチスレッドをシングルスレッドで実現しています。
performメソッドがコルーチンの中身ですが、COR_YIELDでmainへ値を戻し、mainで次にnextメソッド呼ぶとCOR_YIELDの直後から続き実行します。

//
#import "TestCoroutine.h"
#import "CoroutineX.h"


@interface TestCoroutine (/*PRIVATE*/)
- (NSString*)makeChildName;
@end

@implementation TestCoroutine {
  int _concurrency_level;
  NSString *_name;

  int _cl, _i, _j;
  NSString *_retStr;
}

- (id)initWithConcurrencyLevel:(int)lv name:(NSString*)name {
  self = [super init];
  if (self) {
    _concurrency_level = lv;
    _name = name;
  }
  return self;
}


- (NSString*)makeChildName {
  static int c =0;
  return [NSString stringWithFormat:@"%@c%d", _name, c++];
}

- (id)perform {
  /*
   You can NOT use any local variables in this method.
   */
  static int forkDepth = 2;
  COR_BEGIN;

  for (_cl = 0; _cl < _concurrency_level; ++_cl) {
    //recursive call
    COR_FORK([[TestCoroutine alloc] initWithConcurrencyLevel:forkDepth-- name:[self makeChildName]]);        
  }
  
  for (_i=0; _i < 3; ++_i) {
    _retStr = [NSString stringWithFormat:@"%@'s loop1 i=%d",self->_name, _i];
    COR_YIELD(_retStr);
  }
  
  for (_i=0; _i < 2; ++_i) {
    for (_j=0; _j < 3; ++_j) {
      _retStr = [NSString stringWithFormat:@"%@'s loop2 i=%d, j=%d",self->_name, _i, _j];
      COR_YIELD(_retStr);
    }
  }

  //sub coroutine call
  COR_FORK([[SubCoroutine alloc] init]);

  COR_END;
}

@end


@interface SubCoroutine (/*PRIVATE*/)
@end

@implementation SubCoroutine

- (id)perform {
  /*
   You can NOT use any local variables in this method.
   */
  COR_BEGIN;

  COR_YIELD([NSNumber numberWithFloat:1.2]);
  COR_YIELD([NSNumber numberWithFloat:2.3]);

  COR_END;
}

@end


//
//  main.m
//

#import 
#import "TestCoroutine.h"


int main(int argc, const char * argv[])
{

  @autoreleasepool {

    CoroutineState *corState1 = [[CoroutineState alloc] init];
    TestCoroutine *coro1 = [[TestCoroutine alloc] initWithConcurrencyLevel:3 name:@"p0"];
    [corState1 push:coro1];

    CoroutineState *corState2 = [[CoroutineState alloc] init];
    TestCoroutine *coro2 = [[TestCoroutine alloc] initWithConcurrencyLevel:2 name:@"p1"];
    [corState2 push:coro2];

    id retVal1, retVal2;
    do {
      retVal1 = [corState1 next];
      retVal2 = [corState2 next];
      NSLog(@"1:%@, 2:%@", retVal1, retVal2);
      NSLog(@"1's stack-size=%ld, 2's stack-size=%ld", [corState1 stackSize], [corState2 stackSize]);
    } while (retVal1 || retVal2);

  }
    return 0;
}