yaakaito / NLTQuickCheck

like haskell quickcheck
MIT License
45 stars 2 forks source link

Generator をユーザーが手軽にコントロールする為のユーティリティなどの実装 #3

Open VoQn opened 12 years ago

VoQn commented 12 years ago

Haskell でのインターフェース

Test.QuickCheck.Gen で用意されている、ジェネレーターのコントローラ

sized :: (Int -> Gen a) -> Gen a

resize :: Int -> Gen a -> Gen a

-- ランダムに値を生成できる2つの値の組から、その範囲内のジェネレーターを作る
choose :: Random a => (a, a) -> Gen a

-- 外部入力やリソースから読み込んだジェネレーターを、テストに利用できるようにする
promote :: Monad m => m (Gen a) -> Gen (m a)

-- ジェネレーターを「生成された値が特定の条件に合うように」加工する
suchThat :: Gen a -> (a -> Bool) -> Gen a

-- ジェネレーターを「生成された値が特定の条件に合うなら Just, 合わなければ Nothing に」加工する
suchThatMaybe :: Gen a -> (a -> Bool) -> Gen (Maybe a)

-- ジェネレーターのリストから、一つ選ぶ
oneof :: [Gen a] -> Gen a

-- [(比率, ジェネレーター)] のリストから、偏りがあるようにジェネレーターを選ぶ
frequency :: [(Int, Gen a)] -> Gen a

-- リストの中から一個
elements :: [a] -> Gen a

-- 無限リストからジェネレーターを作る
growingElements :: [a] -> Gen a

-- ジェネレーターから、空リストを含む、リストのジェネレーターを作る
listOf :: Gen a -> Gen [a]

-- 空じゃない(最少でも一個は要素のある)リストのジェネレーターを作る
listOf1 :: Gen a -> Gen [a]

-- 固定長リスト
vectorOf :: Int -> Gen a -> Gen [a]

JavaScript で書くとこういう感じ

var env = (function(){
  var seed = 0; // 乱数生成の為のシード。説明を簡易に済ます為に、ここでは 2 の指数だとする
  return { // テストの為の環境変数のようなモノを模擬的に用意している。
    getSeed: function(){ // シードは readonly
      return seed;
    },
    growSeed: function(){ // シードをインクリメントして乱数が大きくなるようにする
      seed++;
    },
    random: function( type ){ // seed から、引数のタイプに合わせてプリミティブのランダムを用意する
      var genNum = function(){ // 非負の数値のランダム
         return Math.min( Number.MAX_VALUE, Math.random() * Math.pow( 2, seed ));
      };
      var genSign = function(){ // 正負の係数をランダムで
         return Math.random() > 0.5 ? (-1) : 1;
      };
      var genChar = function(){
         return String.fromCharCode( 0x40 + genNum().ceil() );
      };
      if ( !!type ) return genSign() * genNum(); // 引数指定無い場合はデフォルト
      switch ( type ) {
        case 'boolean': return genSign() == -1;
        case 'int': return genSign() * genNum().ceil();
        case 'char': genChar();
        default: return genSign() * randomNum();
      }
    },
    clear: function(){ // テスト環境初期化
      seed = 0;
    }
  };
})();

var choose = function( minSeed, maxSeed ) {
  return function(){
   return minSeed + Math.random() * maxSeed + 1;
  };
}

var elements = function( list ) {
  return function(){
    return list[ choose( 0, list.length - 1 )() ]
  }();
}

var oneof = function( generators_list ) {
  return elements( generators_list );
};

var _genArray = function( generator, length, shouldNotEmpty ){
  var len = length || Math.abs( env.random( 'int' ) ); // 配列長が引数で決まらない場合は テスト環境オブジェクトに決めてもらう
  if ( shouldNotEmpty ) {
    len = Math.max( 1, len );
  }
  return function(){
     var i = 0, result = [];
     for( ; i < len; i++){
       result[ i ] = generator();
     }
     return result;
  };
};

var listOf = function( generator ){
  return _genArray( generator );
};

var listOf1 = function( generator ){
  return _genArray( generator, undefined, true );
};

var vectorOf = function( length, generator ){
   return _genArray( generator, length );
};
VoQn commented 12 years ago

なんか quickcheck.js 先に実装すませた方が開発進行良いのではって気がしてきた

yaakaito commented 12 years ago

まあなんか読みつつ

VoQn commented 12 years ago

乱数生成の seed に影響が無いやつ、こんな感じで書けたわ

var choose = function( low, high ){
  return function(){
    var l = Math.random() * low
       , h = Math.random() * high
       , i = l + h
       , r = Math.min( high, Math.max( low, i ));
    return r;
  };
};
var elements = function( list ){
  return function(){
    var index = Math.round( choose( 0, list.length - 1 )() )
       , item = list[ index ];
    return item;
  };
};
var oneOf = function( generators ){
  var generator = elements( generators );
  return generator;
};
yaakaito commented 12 years ago

resize elements choose が実装済み