Skip to content

Instantly share code, notes, and snippets.

@kkismd
Last active August 3, 2018 10:21
Show Gist options
  • Save kkismd/d4330e6d235f8e5671ed2b7f0382bd89 to your computer and use it in GitHub Desktop.
Save kkismd/d4330e6d235f8e5671ed2b7f0382bd89 to your computer and use it in GitHub Desktop.
import {Pair, State} from "./state"
// ----------------------
// 自動販売機の実装
enum Input {
Coin,
Turn
}
interface Machine {
locked: boolean;
candies: number;
coins: number;
}
function machine(locked: boolean, candies: number, coins: number): Machine {
return ({ locked, candies, coins });
}
const update = (i: Input) => (s: Machine): Machine => {
if (s.candies == 0) return s;
else if (i === Input.Coin && !s.locked) return s;
else if (i === Input.Turn && s.locked) return s;
else if (i === Input.Coin && s.locked) {
return machine(false, s.candies, s.coins + 1);
}
else if (i === Input.Turn && !s.locked) {
return machine(true, s.candies - 1, s.coins);
}
};
function simulateMachine(inputs: Input[]): State<Machine, Pair<number, number>> {
const fn = (i: Input) => State.modify(update(i))
const arr: State<Machine, void>[] = inputs.map(fn);
return State.sequence(arr).bind(_ =>
State.get<Machine>().map((s: Machine) => ({ _1: s.candies, _2: s.coins })
));
}
// ----------------------
// 自動販売機の利用
const initialMachine = machine(true, 5, 10);
const inputs = [Input.Coin, Input.Turn, Input.Coin, Input.Turn, Input.Coin, Input.Turn, Input.Coin, Input.Turn];
console.log('machine -> %j', simulateMachine(inputs).evalState(initialMachine));
import { Pair, State} from "./state";
// ----------------------
// 乱数ジェネレータの実装
interface RNG {
nextInt(): Pair<number, RNG>
}
class SimpleRNG implements RNG {
seed: number;
constructor(seed: number) {
this.seed = seed;
}
nextInt(): Pair<number, SimpleRNG> {
const nextSeed = (this.seed * 0x5DEECE66D + 0xB) & 0xFFFFFFFFFFFF;
const nextRNG = new SimpleRNG(nextSeed);
const n = (nextSeed >>> 16);
return { _1: n, _2: nextRNG };
}
}
type Rand<A> = State<RNG, A>;
const int: Rand<number> = new State(rng => rng.nextInt());
const nonNegativeInt: Rand<number> = int.map(i => (i < 0) ? -(i + 1) : i);
const nonNegativeLessThan = (n: number): Rand<number> => nonNegativeInt.bind(i => {
const mod = i % n;
if (i + (n - 2) - mod >= 0)
return State.returns(mod);
else
return nonNegativeLessThan(n);
});
function ints(count: number) {
return State.sequence(arrayFill(count, int))
}
function arrayFill<A>(count: number, elem: A) {
const result: A[] = [];
for (let i = 0; i < count; i++) {
result.push(elem);
}
return result;
}
// ----------------------
// 乱数ジェネレータの利用
const rng = new SimpleRNG(42);
const { _1: n1, _2: rng2 } = rng.nextInt();
const { _1: n2, _2: rng3 } = rng2.nextInt();
console.log("n1 = %d, seed = %d", n1, rng2.seed);
console.log("n2 = %d, seed = %d", n2, rng3.seed);
const int2 = State.sequence([int, int]);
const r = int2.evalState(rng);
console.log('int2() -> %j', r);
const arr = ints(12).evalState(rng);
console.log('ints(12) -> %j', arr);
const dice = nonNegativeLessThan(6).map(i => i + 1);
const { _1: i1, _2: d2 } = dice.runState(rng);
const { _1: i2, _2: d3 } = dice.runState(rng2);
console.log("dice -> %f", i1);
console.log("dice -> %f", i2);
const dice30 = State.sequence(arrayFill(30, dice));
console.log("dice30 -> %j", dice30.evalState(rng));
const ns: Rand<Array<number>> =
dice.bind(x =>
dice.bind(y =>
ints(x).map(xs =>
xs.map(n => n % y))));
console.log('ns -> %j', ns.runState(rng));
// Tupleの代わり
export interface Pair<T, U> {
_1: T;
_2: U;
}
// Stateをクラスとして実装
export class State<S, A> {
runState: (s: S) => Pair<A, S>;
evalState = (s: S): A => this.runState(s)._1;
execState = (s: S): S => this.runState(s)._2;
constructor(g: (s: S) => Pair<A, S>) {
this.runState = g;
}
static returns<S, A>(v: A): State<S, A> {
return new State<S, A>((s: S) => {
return { _1: v, _2: s };
})
}
bind<B>(f: (a: A) => State<S, B>): State<S, B> {
const g = (s: S) => {
const st = this.runState(s);
return f(st._1).runState(st._2);
};
return new State<S, B>(g);
}
map<A, B>(f: (A) => B): State<S, B> {
return this.bind(a => State.returns(f(a)));
}
map2<A, B, C>(m: State<S, B>, f: (A, B) => C): State<S, C> {
return this.bind(a => m.map(b => f(a, b)));
}
static sequence<S, A>(ss: State<S, A>[]): State<S, A[]> {
const fn = (acc, v) => acc.map2(v, (a, b) => a.concat(b));
const init = this.returns([]);
return ss.reduce(fn, init)
}
static modify<S>(f: (s: S) => S): State<S, void> {
return this.get<S>().bind(s => this.set(f(s)))
}
static get<S>(): State<S, S> {
const g = (s: S) => ({ _1: s, _2: s });
return new State<S, S>(g);
}
static set<S>(a: S): State<S, void> {
const g = (a2: S) => ({ _1: null, _2: a });
return new State<S, void>(g);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment