代码之家  ›  专栏  ›  技术社区  ›  SirPeople

循环直到。。。和Ramda一起

  •  3
  • SirPeople  · 技术社区  · 7 年前

    我试图使用Ramda重构几段代码,我想知道,在Ramda/函数式编程中,有什么好方法可以解决以下代码:

    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上迭代并应用相同的管道/操作组合,直到满足其中一个条件。重要的是,我得到了最后一个值(nextValue),作为对forLoop合成的反馈。

    2 回复  |  直到 7 年前
        1
  •  6
  •   Mulan    7 年前

    看起来你在找一个 颠倒 折叠,或 unfold

    大多数人都熟悉 reduce :它接受一组值和 减少 将其转换为单个值– 展开 相反:它只取一个值 展开 它是一个值集合

    如果库中已经存在类似的函数,则其他更熟悉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 ]

    展开 非常强大

    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 ]
    

    我们可以实施您的 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开始执行递归db查找, 0

    • db.getChildren 接受节点 id 并仅返回节点的 立即的 儿童

    • traverse 接受节点 id号 它递归地获取所有子代(深度优先顺序)

    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 ]

    其他非常适合 展开

    • “从页面URL开始 / ,爬网所有子代页“
    • “从搜索开始 "foo" 和页码 1 ,从所有页面收集结果“
    • “从用户开始 Alice ,让我看看她的朋友,还有她所有朋友的朋友“
        2
  •  5
  •   Scott Sauyet    7 年前

    我不知道这是否符合你的要求,但拉姆达 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>