使用通用的loop 和recur 接口,我们编写以函数式风格表示的堆栈安全循环 -
const recur = (...values) =>
({ recur, values })
const loop = f =>
{ let r = f ()
while (r && r.recur === recur)
r = f (...r.values)
return r
}
const main = (limit = 10) =>
loop // begin a loop with vars
( ( r = [ 'x', 'y' ] // initial result
, i = 0 // initial index
) =>
i >= limit // exit condition
? r .slice (0, i) // return result
: recur // otherwise recur
( [ ...r, r[i] + '-' ] // next result
, i + 1 // next index
)
)
console .log (main (10))
// ["x","y","x-","y-","x--","y--","x---","y---","x----","y----"]
我们也可以轻松地将[ 'x', 'y' ] 作为程序的输入 -
const main = (init = [], limit = 10) =>
loop
( ( r = [...init] // <-- initial result
, i = 0
) =>
i >= limit
? r .slice (0, i)
: recur
( [ ...r, r[i] + '-' ]
, i + 1
)
)
main ([ 'a', 'b', 'c' ], 10)
// ["a","b","c","a-","b-","c-","a--","b--","c--","a---"]
在上面,我们在循环运行时使用索引i = 0 来执行查找r[i],但这只是解决问题的一种方法。要查看 loop 和 recur 以不同方式构建数组,我们将看到它们在另一个场景中的使用。
让我们想象一个游戏,您掷出N 面的骰子,结果M 小于N。如果M 是0,则游戏结束,否则记录您的掷骰并用M 面骰子重复游戏-
const rand = max =>
Math .floor (Math .random () * max)
const play = (init = 0) =>
loop // begin a loop with vars
( ( r = [] // initial result
, max = init // initial max
, roll = rand (init) // initial roll
) =>
roll === 0 // gameover condition
? [ ...r, roll ] // return result
: recur // otherwise recur
( [ ...r, roll ] // next result
, roll // next max
, rand (roll) // next roll
)
)
console .log (JSON .stringify (play (1000)))
// [688,416,215,12,5,1,0]
在这个特定的程序中,我们的大脑不再考虑数组索引或递增它们。上面,我们在构建它时不会读取结果r。相反,我们使用另一个循环参数max 来编码当前滚动限制。 loop 和 recur 足够通用,可以表达各种功能程序。
在这两个程序中,传播参数,如 [ ...r, r[i] + '-' ] 或 [ ...r, roll ],在每个步骤中复制结果 r。由于r 在循环开始时使用一个新数组r = [...] 进行了初始化,因此我们可以使用变异操作Array.prototype.push,而不会有泄漏副作用的风险。这大大减少了运行时间和内存占用 -
const push = (xs, x) =>
( xs .push (x) // <-- perform side-effect
, xs // <-- return value
)
const play = (init = 0) =>
loop
( ( r = [] // <-- new array
, max = init
, roll = rand (init)
) =>
roll === 0
? push (r, roll) // <-- mutate
: recur
( push (r, roll) // <-- mutate
, roll
, rand (roll)
)
)
将下面的sn-p扩展为play游戏与N = 1000-
const recur = (...values) =>
({ recur, values })
const loop = f =>
{ let r = f ()
while (r && r.recur === recur)
r = f (...r.values)
return r
}
const rand = max =>
Math .floor (Math .random () * max)
const push = (xs, x) =>
( xs .push (x)
, xs
)
const play = (init = 0) =>
loop
( ( r = []
, max = init
, roll = rand (init)
) =>
roll === 0
? push (r, roll)
: recur
( push (r, roll)
, roll
, rand (roll)
)
)
console .log (JSON .stringify (play (1000)))
// [688,416,215,12,5,1,0]