HSPにもコルーチンがあるということに衝撃を受けたので、線形合同法で簡単に乱数ジェネレーターを作ってみました。
乱数と言ったらジェネレーター、ジェネレーターっと言ったら乱数みたいな。
ちなみにコルーチンを使わない乱数ジェネレーターは以前別に実装しています。XorShiftの実装
実行例
【乱数ジェネレーター】
34
10618
34
251
8804
251
1770
61641
1770
12403
38290
12403
21299
5903
21299
シード値が同一な上段と下段は同じ値が取得できます。
線形合同法
まず、線形合同法とは。
Xn = (A × Xn-1 + C) mod M
の式によって求まる最も簡単な乱数のひとつです。
X0・A・C・Mは初期値としてシード値として与え、
Xnの値は常に変化します。
実装
#runtime "hsp3cl"
#module __RandGen Xn,A,C,M,yieId,value
#define global RandGen(%1,%2=3,%3=7,%4=13,%5=0xFFFF) \
dimtype %1,5 :\
newmod %1,__RandGen,%2,%3,%4,%5
#modinit int X0,int _A,int _C,int _M
Xn=X0: A=_A: C=_C: M=_M
newlab yieId,1: return
*infLoop
Xn=(A*Xn+C)\M
value=Xn
newlab yieId,1: return
goto*infLoop
return
#modcfunc genNextValue
gosub yieId
return value
#global
#module Program
#deffunc main
sdim _input
mes "【乱数ジェネレーター】"
RandGen g1
RandGen g2,1515
RandGen g3
repeat
input _input,,1
mes genNextValue(g1)
mes genNextValue(g2)
mes genNextValue(g3)
loop
return
#global
main
複数のジェネレーターを保持するためにモジュール型変数を使用しています。
細かいコルーチン文法については元記事参照。
なお、コルーチン内のループはreturnを挟むため、cntが破綻してしまうrepeatの使用は避けています。
比較の実装(Node.js)
比較用にJavaScriptでジェネレーターを用いた実装を載せておきます。
function* RandGen(Xn=3,A=7,C=13,M=0xFFFF){
for(;;){
yield Xn=(A*Xn+C)%M
}
}
(async function(){
console.log("【乱数ジェネレーター】");
const g1=RandGen();
const g2=RandGen(1515);
const g3=RandGen();
for(;;){
await new Promise(res=>process.stdin.once("data",res));
console.log(g1.next().value);
console.log(g2.next().value);
console.log(g3.next().value);
}
})();
おまけ
見た目をjsとかをその他のyieldな言語に近づけてみた。
#runtime "hsp3cl"
#module __RandGen Xn,A,C,M,yieId,value
#define yieId(%1="__undefined__") value=%1: newlab rezume,1: return
#modfunc genNext
gosub rezume: return
#modcfunc genValue
return value
#modcfunc genNextValue
gosub rezume: return value
#define global RandGen(%1,%2=3,%3=7,%4=13,%5=0xFFFF) \
dimtype %1,5 :\
newmod %1,__RandGen,%2,%3,%4,%5
#modinit int seed,int _A,int _C,int _M
Xn=seed: A=_A: C=_C: M=_M
yieId
*infLoop
Xn=(A*Xn+C)\M
yieId Xn
goto*infLoop
return
#global
#module Program
#deffunc main
sdim _input
mes "【乱数ジェネレーター】"
RandGen g1
genNext g1
RandGen g2,1515
RandGen g3
repeat
input _input,,1
mes genNextValue(g1)
mes genNextValue(g2)
mes genNextValue(g3)
loop
return
#global
main
終わりに
結構普通のコルーチン(ジェネレーター)として使えて満足でした。
モジュールの代わりに特異な変数を埋め込んだdefineでも行けるかな?