rand(3)を使ってSML# にSML/NJのRandを実装

Basisに乱数無いのは何故なのか?(これ書くの多分3回目くらい)
というわけで(?)、SML#でC言語のライブラリのインポートを試しつつSML/NJが提供している乱数のストラクチャ]を定義してみました。

(*
 * Rand : RAND (SML/NJ library clone)
 *
 * http://www.smlnj.org/doc/smlnj-lib/Manual/rand.html
 * **)
structure Rand : RAND =
struct
local
  val srand_ = _import "srand" : word -> unit
  val  rand_ = _import  "rand" : unit -> word
  structure W = Word
  structure R = Real
in
  type rand = Word.word
  fun random r = (srand_ r; rand_ ())
  val randMin = Word.fromInt 1
  (* mingw/include/stdlib.h:#define   RAND_MAX        0x7FFF *)
  val randMax = Word.fromInt 0x7FFF (* SML/NJ : 2147483647 *)

  fun mkRandom r =
    let val ctx = ref r
    in fn ()=> (
        let val r_ = random (!ctx)
        in (ctx := r_; r_)
        end)
    end

  fun norm r =
    R.fromInt (W.toInt r) / (R.fromInt (W.toInt randMax))

  local fun errmsg i j =
                concat["excepect (i <= j) "
                      ,"actually (i,j)=(",Int.toString i,",",Int.toString j,")"]
  in
  fun range (i,j) r =
    if j < i then raise Fail (errmsg i j)
    else R.toInt IEEEReal.TO_NEAREST (R.fromInt i + R.fromInt (j - i) * norm r)
  end
end
end

インポート命令書くだけなのでホントに楽ですね!インタプリタ上でも同じように書くだけで動作します。これはちょっと感動するレベルですね。
アノテーションは自分で書く必要がありますが、§ 6.2. C関数の型を見て書きましょう。

ちょっと注意

SML/NJではシグネチャがrand型の実装(Word31.word)*1を公開してしまっているのですが、SML#-mingwではWord31が提供されていないようなので、Word32.wordを使っています。よって厳密には型が異なります。*2でもめんどいのでここまで:p

*1:31bitワードを表す

*2:その上mingwのRAND_MAXは0x7FFFなので…