循环直到...与 Ramda
Loop until... with Ramda
我正在尝试使用 Ramda 重构几段代码,我想知道,在 Ramda/Functional 编程中有什么好的方法可以解决以下代码:
let arrayOfSomething = initArray();
for(let i = 0; SOME_INDEX_CONDITION(i)|| SOME_CONDITION(arrayOfSomething); i++) {
const value = operation(arrayOfSomething);
const nextValue = anotherOperation(value);
arrayOfSomething = clone(nextValue)
}
所以基本上我想在 arrayOfSomething 上迭代并应用相同的 pipe/composition 操作,直到满足其中一个条件。重要的是我得到最后一个值 (nextValue) 作为对 forLoop 组合的反馈。
我不知道这是否符合您的要求,但 Ramda 的 until
可能是您需要的:
const operation = ({val, ctr}) => ({val: val % 2 ? (3 * val + 1) : (val / 2), ctr: ctr + 1})
const indexCondition = ({ctr}) => ctr > 100
const valCondition = ({val}) => val === 1
const condition = R.either(indexCondition, valCondition)
const check = R.until(condition, operation)
const collatz = n => check({ctr: 0, val: n})
console.log(collatz(12))
// 12 -> 6 -> 3 -> 10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1 //=> {"ctr": 9, "val": 1}
console.log(collatz(5))
// 5 -> 16 -> 8 -> 4 -> 2 -> 1 //=> {"ctr": 5, "val": 1}
console.log(collatz(27))
//27 -> 82 -> 41 -> 124 -> 62 -> .... //=> {"ctr": 101, "val": 160}
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>
您似乎在寻找 反向 弃牌,或 unfold
。
大多数人都熟悉 reduce
:它需要一组值,然后 将其减少 为单个值 – unfold
恰恰相反:它取一个值,展开它到一个值集合
如果库中已经存在类似的函数,其他更熟悉 Ramda 的人可以发表评论
const unfold = (f, init) =>
f ( (x, next) => [ x, ...unfold (f, next) ]
, () => []
, init
)
const nextLetter = c =>
String.fromCharCode (c.charCodeAt (0) + 1)
const alphabet =
unfold
( (next, done, c) =>
c > 'z'
? done ()
: next (c, nextLetter (c))
, 'a'
)
console.log (alphabet)
// [ a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z ]
unfold
很厉害
const fib = (n = 0) =>
unfold
( (next, done, [ n, a, b ]) =>
n < 0
? done ()
: next (a, [ n - 1, b, a + b ])
, [ n, 0, 1 ]
)
console.log (fib (20))
// [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765 ]
我们可以使用 unfold
实现您的 iterateUntil
const unfold = (f, init) =>
f ( (x, acc) => [ x, ...unfold (f, acc) ]
, () => []
, init
)
const iterateUntil = (f, init) =>
unfold
( (next, done, [ arr, i ]) =>
i >= arr.length || f (arr [i], i, arr)
? done ()
: next (arr [i], [ arr, i + 1 ])
, [ init, 0 ]
)
const data =
[ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' ]
console.log (iterateUntil ((x, i) => i > 3, data))
// [ 'a', 'b', 'c', 'd' ]
console.log (iterateUntil ((x, i) => x === 'd', data))
// [ 'a', 'b', 'c', 'd' ]
我们可以通过 async
和 await
使其轻松支持异步。下面我们使用 asyncUnfold
执行递归数据库查找,从单个节点 ID 开始,0
db.getChildren
接受一个节点 id
和 returns 只有节点的 直接 子节点
traverse
接受一个节点 id
并递归地获取所有后代子节点(按 depth-first 顺序)
const asyncUnfold = async (f, init) =>
f ( async (x, acc) => [ x, ...await asyncUnfold (f, acc) ]
, async () => []
, init
)
// demo async function
const Db =
{ getChildren : (id) =>
new Promise (r => setTimeout (r, 100, data [id] || []))
}
const Empty =
Symbol ()
const traverse = (id) =>
asyncUnfold
( async (next, done, [ id = Empty, ...rest ]) =>
id === Empty
? done ()
: next (id, [ ...await Db.getChildren (id), ...rest ])
, [ id ]
)
const data =
{ 0 : [ 1, 2, 3 ]
, 1 : [ 11, 12, 13 ]
, 2 : [ 21, 22, 23 ]
, 3 : [ 31, 32, 33 ]
, 11 : [ 111, 112, 113 ]
, 33 : [ 333 ]
, 333 : [ 3333 ]
}
traverse (0) .then (console.log, console.error)
// => Promise
// ~2 seconds later
// [ 0, 1, 11, 111, 112, 113, 12, 13, 2, 21, 22, 23, 3, 31, 32, 33, 333, 3333 ]
其他适合 unfold
的程序
- "starting with a page URL
/
, crawl all descendant pages"
- "starting with search
"foo"
and page 1
, collect results from all pages"
- "starting with user
Alice
, show me her friends, and all of her friends' friends"
我正在尝试使用 Ramda 重构几段代码,我想知道,在 Ramda/Functional 编程中有什么好的方法可以解决以下代码:
let arrayOfSomething = initArray();
for(let i = 0; SOME_INDEX_CONDITION(i)|| SOME_CONDITION(arrayOfSomething); i++) {
const value = operation(arrayOfSomething);
const nextValue = anotherOperation(value);
arrayOfSomething = clone(nextValue)
}
所以基本上我想在 arrayOfSomething 上迭代并应用相同的 pipe/composition 操作,直到满足其中一个条件。重要的是我得到最后一个值 (nextValue) 作为对 forLoop 组合的反馈。
我不知道这是否符合您的要求,但 Ramda 的 until
可能是您需要的:
const operation = ({val, ctr}) => ({val: val % 2 ? (3 * val + 1) : (val / 2), ctr: ctr + 1})
const indexCondition = ({ctr}) => ctr > 100
const valCondition = ({val}) => val === 1
const condition = R.either(indexCondition, valCondition)
const check = R.until(condition, operation)
const collatz = n => check({ctr: 0, val: n})
console.log(collatz(12))
// 12 -> 6 -> 3 -> 10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1 //=> {"ctr": 9, "val": 1}
console.log(collatz(5))
// 5 -> 16 -> 8 -> 4 -> 2 -> 1 //=> {"ctr": 5, "val": 1}
console.log(collatz(27))
//27 -> 82 -> 41 -> 124 -> 62 -> .... //=> {"ctr": 101, "val": 160}
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>
您似乎在寻找 反向 弃牌,或 unfold
。
大多数人都熟悉 reduce
:它需要一组值,然后 将其减少 为单个值 – unfold
恰恰相反:它取一个值,展开它到一个值集合
如果库中已经存在类似的函数,其他更熟悉 Ramda 的人可以发表评论
const unfold = (f, init) =>
f ( (x, next) => [ x, ...unfold (f, next) ]
, () => []
, init
)
const nextLetter = c =>
String.fromCharCode (c.charCodeAt (0) + 1)
const alphabet =
unfold
( (next, done, c) =>
c > 'z'
? done ()
: next (c, nextLetter (c))
, 'a'
)
console.log (alphabet)
// [ a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z ]
unfold
很厉害
const fib = (n = 0) =>
unfold
( (next, done, [ n, a, b ]) =>
n < 0
? done ()
: next (a, [ n - 1, b, a + b ])
, [ n, 0, 1 ]
)
console.log (fib (20))
// [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765 ]
我们可以使用 unfold
iterateUntil
const unfold = (f, init) =>
f ( (x, acc) => [ x, ...unfold (f, acc) ]
, () => []
, init
)
const iterateUntil = (f, init) =>
unfold
( (next, done, [ arr, i ]) =>
i >= arr.length || f (arr [i], i, arr)
? done ()
: next (arr [i], [ arr, i + 1 ])
, [ init, 0 ]
)
const data =
[ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' ]
console.log (iterateUntil ((x, i) => i > 3, data))
// [ 'a', 'b', 'c', 'd' ]
console.log (iterateUntil ((x, i) => x === 'd', data))
// [ 'a', 'b', 'c', 'd' ]
我们可以通过 async
和 await
使其轻松支持异步。下面我们使用 asyncUnfold
执行递归数据库查找,从单个节点 ID 开始,0
db.getChildren
接受一个节点id
和 returns 只有节点的 直接 子节点traverse
接受一个节点id
并递归地获取所有后代子节点(按 depth-first 顺序)
const asyncUnfold = async (f, init) =>
f ( async (x, acc) => [ x, ...await asyncUnfold (f, acc) ]
, async () => []
, init
)
// demo async function
const Db =
{ getChildren : (id) =>
new Promise (r => setTimeout (r, 100, data [id] || []))
}
const Empty =
Symbol ()
const traverse = (id) =>
asyncUnfold
( async (next, done, [ id = Empty, ...rest ]) =>
id === Empty
? done ()
: next (id, [ ...await Db.getChildren (id), ...rest ])
, [ id ]
)
const data =
{ 0 : [ 1, 2, 3 ]
, 1 : [ 11, 12, 13 ]
, 2 : [ 21, 22, 23 ]
, 3 : [ 31, 32, 33 ]
, 11 : [ 111, 112, 113 ]
, 33 : [ 333 ]
, 333 : [ 3333 ]
}
traverse (0) .then (console.log, console.error)
// => Promise
// ~2 seconds later
// [ 0, 1, 11, 111, 112, 113, 12, 13, 2, 21, 22, 23, 3, 31, 32, 33, 333, 3333 ]
其他适合 unfold
- "starting with a page URL
/
, crawl all descendant pages" - "starting with search
"foo"
and page1
, collect results from all pages" - "starting with user
Alice
, show me her friends, and all of her friends' friends"