【问题标题】:Functional way of re-using variables in a pipe在管道中重用变量的功能方式
【发布时间】:2023-03-29 19:43:01
【问题描述】:

在 javascript 和 typescript 中使用函数式编程以及 Ramda,我经常发现自己编写如下代码:

const myFun = c => {
  const myId = c.id

  const value = pipe(
    getAnotherOtherPropOfC,
    transformToArray,
    mapToSomething,
    filterSomething,
    // ... N other transformations
    // ok now I need myId and also the result of the previous function
    chainMyIdWithResultOfPreviousFunction(myId)
  )(c)

  return value
}

注意创建const myId 如何打破无点样式。我想写myFun 这样就不需要明确的c。所以像: const myFun = pipe(....)

我想知道是否有更实用和更易读的方式来做这样的事情。

【问题讨论】:

  • 多次使用变量非常在没有无点样式的情况下更容易和可读。
  • 第二个输入有什么问题吗? IE。 const myFun = (c, id) => {...}

标签: javascript functional-programming ramda.js


【解决方案1】:

可以做吗?当然。 应该完成吗?不是很清楚。

这是上面的一个无点版本,使用lift

const getAnotherOtherPropOfC = prop ('x')
const transformToArray = split (/,\s*/)
const mapToSomething = map (Number)
const filterSomething = filter (n => n % 2 == 1)
const square = (n) => n * n
const chainMyIdWithResultOfPreviousFunction = (id) => (arr) => `${id}: [${arr}]`

const myFun = lift (chainMyIdWithResultOfPreviousFunction) (
  prop ('id'),
  pipe(
    getAnotherOtherPropOfC,
    transformToArray,
    mapToSomething,
    filterSomething,
    map (square)
  )
)

console .log (
  myFun ({id: 'foo', x: '1, 2, 3, 4, 5'}) // => 'foo: [1,9,25]'
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script> const {prop, split, map, filter, lift, pipe} = R            </script>

lift 是一个比 Ramda 的 converge 更符合 FP 标准的函数(与 useWith 一起提供了制作无点解决方案的方法,通常以牺牲可读性为代价。)liftconverge 重叠应用于函数时,但专为一元函数而设计,其中converge 处理多元函数。

这并不可怕。但它与原版相比的唯一优势是它是无点的。如果你试图将它扩展到该管道中的中间函数,它会变得非常丑陋。

我的看法是,无点有时会导致代码更简洁、更易于阅读和更易于维护。但是如果不这样做,就没有理由去无点。

并不是说无指向代码本质上比指向代码更实用。我认为这个想法始于对其他语言的一种 Haskell 嫉妒。 Haskell 被认为是一种理想化的 FP 语言,而它恰好是一种自然产生无点的语言。但这至少是部分巧合。

我的标准示例是const sum = reduce(add, 0)const sum = (xs) =&gt; xs.reduce(add, 0) 更简洁、更易于理解。它还非常清楚地说明了与 const product = reduce(multiply, 1)const all = reduce(and, true) 的相似之处。但随着您变得越来越复杂,或者当您需要重用中间计算时(正如 Bergi 指出的那样),无点代码通常会成为一种负担。

关于这个版本是否是对原版的改进,我在这里没有真正的电话。如果是这样,那只是小事。进一步携带它会显着降低可读性。

【讨论】:

    【解决方案2】:

    变量通常表明你的函数做得太多,应该被分解。比如这个

    const myFun = c => {
        let id = c.id;
    
        return R.pipe(
            R.prop('list'),
            R.map(R.add(10)),
            R.sum,
            R.subtract(id),
        )(c)
    }
    

    可以重构为两个独立的函数:

    const compute = R.pipe(
        R.prop('list'),
        R.map(R.add(10)),
        R.sum,
    );
    
    const getId = R.prop('id');
    

    然后简单

    const myFun = c => getId(c) - compute(c)
    

    这对我来说已经足够好了,但是如果你想完全没有点,那么

    const myFun = R.converge(R.subtract, [getId, compute])
    

    Playground

    【讨论】:

    • 我喜欢这种方法。我不知道converge 函数,所以谢谢!我仍然想知道这种模式是否涵盖了我需要在管道中重用变量的大多数情况。如果R.subtract(id)R.sum 在原来的myFun 中切换呢? (意思是id不是在管道的最后一个函数中使用,而是在它的中间,并且可能在管道内重复使用N次)
    【解决方案3】:

    请注意,我使用 vanilla JS 来吸引更广泛的受众。

    pipe 只是一个复合函数只是一个函数。所以让我们使用一个高阶管道,它接受一个管道、一个函数和一个值,并返回另一个函数而不是结果。要真正得到结果,我们需要将值传递两次:

    const toUc = s => s.toUpperCase();
    const double = s => `${s}${s}`;
    const shout = s => `${s}!`;
    const concat = t => s => `${s} -> ${t}`;
    
    const pipe = g => f => x => f(g(x));
    const pipe3 = h => g => f => x => f(g(h(x)));
    
    const foo = {id: "hi"};
    const bar = pipe3(toUc) (shout) (double); // pipe
    
    const baz = pipe(bar) (concat); // higher order pipe
    
    // the result of the applied pipe is just another function because concat requires two arguments!
    
    console.log(
      baz(foo.id)) // s => `${s} -> ${t}`
    
    // let's apply it twice
    
    console.log(
      baz(foo.id)
        (foo.id)); // hi -> HI!HI!

    【讨论】:

      【解决方案4】:

      这样的事情怎么样:

      const getAnotherPropOfC = R.prop('message');
      const transformToArray = R.split('');
      const mapToSomething = R.map(R.toUpper);
      const filterSomething = R.reject(R.equals(' '));
      const chainMyIdWithResultOfPreviousFunction = R.pipe(
        R.prop('fragment'), 
        R.concat,
      );
      
      const myFun = R.converge(R.map, [
        chainMyIdWithResultOfPreviousFunction,
        R.pipe(
          getAnotherPropOfC,
          transformToArray,
          mapToSomething,
          filterSomething,
        ),
      ]);
      
      
      
      console.log(
        myFun({ message: 'Hello World', fragment: '!' }),
      );
      &lt;script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"&gt;&lt;/script&gt;

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-04-10
        • 1970-01-01
        • 2016-06-09
        • 2015-04-16
        • 1970-01-01
        • 2020-11-07
        相关资源
        最近更新 更多