The resultant in a State ADT instance can be used as a means of communication between different stateful transactions. It can be used to read and transform a portion of our state into a form that another transaction is dependent on. This allows us to only keep what is needed state, without filling it with calculations that are only needed for one or few transactions.

We take can take advantage of this by pulling not only a random number using the seed from our state, but also pulling a list of card and filtering them, to randomly select one from our calculated state. Then we use the resultant to store the calculation before we save the result to state.

Code for Random generator is here, which is not so important to this blog.

const {prop,assoc, State, identity, omit, curry, filter, converge,map, composeK, liftA2, equals, constant,option, chain, mapProps, find, propEq, isNumber, compose, safe} = require('crocks');
const {get, modify, of} = State; const state = {
cards: [
{id: 'green-square', color: 'green', selected: true, shape: 'square'},
{id: 'orange-square', color: 'orange', shape: 'square'},
{id: 'blue-triangle', color: 'blue', shape: 'triangle'}
],
hint: null,
seed: Date.now()
} // #region helper const liftState = fn => compose(
of,
fn
) // nextSeed :: Integer -> Integer
const nextSeed = seed =>
(seed * 1103515245 + 12345) & 0x7fffffff // value :: Integer -> Number
const value = seed =>
(seed >>> 16) / 0x7fff // normalize :: (Integer, Integer) -> Number -> Integer
const normalize = (min, max) =>
x => Math.floor(x * (max - min)) + min // getNextSeed :: () -> State AppState Integer
const getNextSeed = () =>
get(({ seed }) => nextSeed(seed)) // updateSeed :: Integer -> State AppState ()
const updateSeed = seed =>
modify(assoc('seed', seed)) // nextValue :: Integer -> State AppState Number
const nextValue = converge(
liftA2(constant),
liftState(value),
updateSeed
) // random :: () -> State AppState Number
const random =
composeK(nextValue, getNextSeed)
// #endregion // between :: (Integer, Integer) -> State AppState Integer
const between = (min, max) =>
random()
.map(normalize(min, max));

The only important piece is 'bewteen' function, which can generate the Number between a min & max range.

The  idea for what we are going to achieve is that:

  • Select all the unselected cards
  • Randomly choose one unselected card by using random function
  • Set the hint according to the selected card.

First, select all the unselected cards:

const selectState = (key, fn) => get(
compose(
map(fn),
prop(key)
)
)
const unselected = ({selected}) => !selected;
const getUnselectedCards = () => selectState('cards', filter(unselected)).map(option([]))

Second: Rnadomly choose one unselected card by using random function:

// randomIndex :: [a] -> State AppState a
const randomIndex = arr => between(0, arr.length)
const getAt = index => arr => arr[index];
const pickCard = converge(
liftA2(getAt),
randomIndex,
liftState(identity)
)

Last, Set the hint according to the selected card:

const over = (key, fn) => modify(
mapProps({[key]: fn})
)
const toHint = pick(['color', 'shape'])
// setHint :: Card -> State AppState()
const setHint = card => over('hint', constant(toHint(card)))
const nextHint = composeK(
setHint,
pickCard,
getUnselectedCards
)

-----------------

const {prop,assoc, pick, State, identity, omit, curry, filter, converge,map, composeK, liftA2, equals, constant,option, chain, mapProps, find, propEq, isNumber, compose, safe} = require('crocks');
const {get, modify, of} = State; const state = {
cards: [
{id: 'green-square', color: 'green', selected: true, shape: 'square'},
{id: 'orange-square', color: 'orange', shape: 'square'},
{id: 'blue-triangle', color: 'blue', shape: 'triangle'}
],
hint: null,
seed: Date.now()
} // #region helper const liftState = fn => compose(
of,
fn
) // nextSeed :: Integer -> Integer
const nextSeed = seed =>
(seed * 1103515245 + 12345) & 0x7fffffff // value :: Integer -> Number
const value = seed =>
(seed >>> 16) / 0x7fff // normalize :: (Integer, Integer) -> Number -> Integer
const normalize = (min, max) =>
x => Math.floor(x * (max - min)) + min // getNextSeed :: () -> State AppState Integer
const getNextSeed = () =>
get(({ seed }) => nextSeed(seed)) // updateSeed :: Integer -> State AppState ()
const updateSeed = seed =>
modify(assoc('seed', seed)) // nextValue :: Integer -> State AppState Number
const nextValue = converge(
liftA2(constant),
liftState(value),
updateSeed
) // random :: () -> State AppState Number
const random =
composeK(nextValue, getNextSeed)
// #endregion // between :: (Integer, Integer) -> State AppState Integer
const between = (min, max) =>
random()
.map(normalize(min, max)); // Get all unselected card
// Randomly choose an unselected Card
// Set hint const selectState = (key, fn) => get(
compose(
map(fn),
prop(key)
)
)
const unselected = ({selected}) => !selected;
const getUnselectedCards = () => selectState('cards', filter(unselected)).map(option([])) // randomIndex :: [a] -> State AppState a
const randomIndex = arr => between(0, arr.length)
const getAt = index => arr => arr[index];
const pickCard = converge(
liftA2(getAt),
randomIndex,
liftState(identity)
)
const over = (key, fn) => modify(
mapProps({[key]: fn})
)
const toHint = pick(['color', 'shape'])
// setHint :: Card -> State AppState()
const setHint = card => over('hint', constant(toHint(card)))
const nextHint = composeK(
setHint,
pickCard,
getUnselectedCards
) console.log(
nextHint()
.execWith(state)
)

[Javascript] Use a Pure RNG with the State ADT to Select an Element from State的更多相关文章

  1. [Functional Programming ADT] Adapt Redux Actions/Reducers for Use with the State ADT

    By using the State ADT to define how our application state transitions over time, we clear up the ne ...

  2. [Functional Programming ADT] Create State ADT Based Reducers (applyTo, Maybe)

    The typical Redux Reducer is function that takes in the previous state and an action and uses a swit ...

  3. [React + Functional Programming ADT] Connect State ADT Based Redux Actions to a React Application

    With our Redux implementation lousy with State ADT based reducers, it is time to hook it all up to a ...

  4. [Functional Programming ADT] Create a Redux Store for Use with a State ADT Based Reducer

    With a well defined demarcation point between Redux and our State ADT based model, hooking up to a R ...

  5. [Functional Programming ADT] Initialize Redux Application State Using The State ADT

    Not only will we need to give our initial state to a Redux store, we will also need to be able to re ...

  6. [Functional Programming ADT] Combine Multiple State ADT Based Redux Reducers

    Redux provides a convenient helper for combining many reducers called combineReducer, but it focuses ...

  7. [Functional Programming] Combine Multiple State ADT Instances with the Same Input (converge(liftA2(constant)))

    When combining multiple State ADT instances that depend on the same input, using chain can become qu ...

  8. [Functional Programming] Read and Transform Values from a State ADT’s State (get)

    Many times we need to access and transform state, either in part or in full, to be used when calcula ...

  9. [Functional Programming] Combine State Dependent Transactions with the State ADT (composeK to replace multi chian call)

    When developing a Finite State Machine, it is often necessary to apply multiple transitions in tande ...

随机推荐

  1. 【bzoj4636】蒟蒻的数列

    由于数据范围过大,直接线段树会炸,离散化或者动态开点都行. 打个标记在树上,最后把树dfs一边算一下即可. #include<bits/stdc++.h> #define N 100000 ...

  2. 【Android开发日记】之入门篇(十四)——Button控件+自定义Button控件

        好久不见,又是一个新的学期开始了,为什么我感觉好惆怅啊!这一周也发生了不少事情,节假日放了三天的假(好久没有这么悠闲过了),实习公司那边被组长半强制性的要求去解决一个后台登陆的问题,结果就是把 ...

  3. kafka 分区数

    Kafka的分区,相当于把一个Topic再细分成了多个通道(对应 多个线程) 部署的时候尽量做到一个消费者(线程)对应一个分区. 如何确定Kafka的分区数,key和consumer线程数,以及不消费 ...

  4. Selenium2+python自动化33-文件上传(send_keys)【转载】

    前言 文件上传是web页面上很常见的一个功能,自动化成功中操作起来却不是那么简单. 一般分两个场景:一种是input标签,这种可以用selenium提供的send_keys()方法轻松解决: 另外一种 ...

  5. k8s的Dashboard

     前面章节 Kubernetes 所有的操作我们都是通过命令行工具 kubectl 完成的.为了提供更丰富的用户体验,Kubernetes 还开发了一个基于 Web 的 Dashboard,用户可以用 ...

  6. 记录一下安卓本地文件File处理的问题

    在项目中更新软件下载更新包的时候删除系统文件夹会报错导致下载失败 研究了下是因为目录不存在导致的由于系统自带下载目录,但是一些极限操作当目录不存在时就需要处理下 项目中的解决方案 File f = n ...

  7. 一句话木马与caidao

    实验吧有个试验环境:http://www.shiyanbar.com/experiment-course/experiment-course/vid/1812 菜刀的主要功能是用来连接一句话木马的,a ...

  8. (2)java安装配置

    java 分为三大类 javasSE,javaEE,javaME. javaSE:一般用于开发桌面软件,是java EE的基础类库 javaEE:用于开发网站 javaME:手机软件程序 javaSE ...

  9. 有向图强连通分量的Tarjan算法及模板

    [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强联通(strongly connected),如果有向图G的每两个顶点都强联通,称有向图G是一个强联通图.非强联通图有向 ...

  10. 训练指南 UVALive - 3126(DAG最小路径覆盖)

    layout: post title: 训练指南 UVALive - 3126(DAG最小路径覆盖) author: "luowentaoaa" catalog: true mat ...