iT邦幫忙

2021 iThome 鐵人賽

DAY 26
0
Software Development

Functional Programming For Everyone系列 第 26

Day 26 - State Monad I

還記得先前提到 Math.random 並非是純函式嗎,因為每次給定相同的輸入都會是不同的輸出回傳回來,那有什麼辦法可以讓它是回傳亂數,又可以是純函數呢? 沒錯,PRNG亂數生成器!在用 State Monad 實作前,先來用一般的方式實作看看!

簡單來說 PRNG 的概念就是先給定一個數值,稱為 seed, 將其放入 generator 中,取得一個 nextSeed 之後,又可以將 nextSeed 放入 generator 產生新的亂數。

const generator = (seed) => (seed * 1103515245 + 12345) & 0x7fffffff; 

const seed = 1;
const randomNumber1 = generator(seed); // 1103527590
const randomNumber2 = generator(randomNumber1) // 377401600

而我們寫的 generator 能確保每次給定相同輸入就會有相同輸出!

有這個能幹嘛呢? 我們可以用它來實作一個給定特定範圍,就會隨機回傳特定範圍數值的函式

const value = (seed) => (seed >>> 16) / 0x7fff;

const normalize = (min, max) => (x) => Math.floor(x * (max - min)) + min;

const randomInRange = (min, max) => (seed) => {
  const nextSeed = generator(seed);
  const random = R.compose(normalize(min, max), value)(nextSeed);
  return [nextSeed, random];
};

這樣就可以隨機產生特定範圍的亂數,而由於我們還需要用下一個 seed 的值去造下一組數,所以必須將下一個 seed 也一併傳出,

const seed  = 1;
const [n1, r1] = randomInRange(0, 10)(seed) // 5
const [n2, r2] = randomInRange(0, 10)(n1) // 1

既然都可以亂數產生特定範圍的數值了,也可以用此一算法,去隨機取出陣列中某一個項目

const name = ["jing", "jing*5", "jing-tech"];

const randomName = (from) => (seed) => {
  const [n1, r1] = randomInRange(seed, 0, from.length)
  return [n1, name[r1]];
}

const [n1, name] = randomName(name)(1) // '1103527590, jing*5'

大致介紹完 PRNG 的概念了,可以反思一下如果現在要從某種陣列(資料)內抽取很多組不同的值呢? 又或是想要一次用同一組亂數不同陣列(資料)取值呢? 這樣是不是會讓程式複雜度上升呢?

沒錯,本章就先介紹到這裡,下一章帶大家來看 State Monad 是如何處理這種問題的!

小結

感謝大家閱讀!

Reference

  1. State Monad

上一篇
Day 25 - Reader Monad
下一篇
Day 27 - State Monad II
系列文
Functional Programming For Everyone30

尚未有邦友留言

立即登入留言