Last active
August 3, 2018 10:21
-
-
Save kkismd/d4330e6d235f8e5671ed2b7f0382bd89 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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