【问题标题】:How to compose two functions together into 1 using FP's compose() javascript?如何使用 FP 的 compose() javascript 将两个函数组合成 1?
【发布时间】:2017-07-11 18:50:37
【问题描述】:

如何使用 FP 的 compose() 将两个函数组合为 1 这是实时代码:https://repl.it/JXMl/1

我有 3 个纯函数:

// groups By some unique key
const groupBy = function(xs, key) {
  return xs.reduce(function(rv, x) {
    (rv[x[key]] = rv[x[key]] || []).push(x);
    return rv;
  }, {});
};

// ungroups the group by
const ungroup = (obj) => {
  return Object.keys(obj)
               .map(x => obj[x]);
};

// flatten array
const flatten = (arrs) => {
  return arrs.reduce((acc, item) => acc.concat(item), [])
}

还有一个来自Functional Jargon的函数式实用程序组合函数

const compose = (f, g) => (a) => f(g(a))

最后,我想要一个通过compose()创建的ungroupAndFlatten函数。

大致如下:

const ungroupAndFlatten = compose(ungroup, flatten) // Usage doesn't work.
console.log(ungroupAndFlatten(obj)) 

示例代码:

const arrs = [
  {name: 'abc', effectiveDate: "2016-01-01T00:00:00+00:00"},
  {name: 'abcd', effectiveDate: "2016-02-01T00:00:00+00:00"},
  {name: 'abcd', effectiveDate: "2016-09-01T00:00:00+00:00"},
  {name: 'abc', effectiveDate: "2016-04-01T00:00:00+00:00"},
  {name: 'abc', effectiveDate: "2016-05-01T00:00:00+00:00"},
]; 

const groupedByName = groupBy(arrs, 'name');

// Example Output
//
// var obj = {
//    abc: [
//      { name: 'abc', effectiveDate: '2016-01-01T00:00:00+00:00' },
//      { name: 'abc', effectiveDate: '2016-04-01T00:00:00+00:00' },
//      { name: 'abc', effectiveDate: '2016-05-01T00:00:00+00:00' }
//    ],
//    abcd: [ 
//      { name: 'abcd', effectiveDate: '2016-02-01T00:00:00+00:00' },
//      { name: 'abcd', effectiveDate: '2016-09-01T00:00:00+00:00' }
//    ]
//  }

const ungroupAndFlatten = compose(ungroup, flatten) // Usage doesn't work.
console.log(ungroupAndFlatten(groupedByName)) 

// Output:
//  var arrs = [
//    {name: 'abc', effectiveDate: "2016-01-01T00:00:00+00:00"},
//    {name: 'abcd', effectiveDate: "2016-02-01T00:00:00+00:00"},
//    {name: 'abcd', effectiveDate: "2016-09-01T00:00:00+00:00"},
//    {name: 'abc', effectiveDate: "2016-04-01T00:00:00+00:00"},
//    {name: 'abc', effectiveDate: "2016-05-01T00:00:00+00:00"},
//  ];

【问题讨论】:

    标签: javascript function lambda functional-programming function-composition


    【解决方案1】:

    我相信你犯了一个简单的错误,你将 ungroup 设置为你的 f 函数,而它是你的 g 函数:

    const ungroupAndFlatten = compose(flatten, ungroup)
    

    切换取消组合和展平,一切都会正常

    【讨论】:

    • 你说得对!哈哈 奇怪的是它是从右到左而不是从左到右。
    • @MatthewHarwood 它是从右到左的,因为它在数学中是这样运作的。
    • @ftor 您能否更清楚地说明“数学就是这样?”大声笑哪个数学?我看到 compose 在执行过程中从内到外,但是 compose 的 API 是否试图模仿它?是在里面吗?
    • @MatthewHarwood 从右到左的顺序占了上风,因为该符号在数学上提供了很好的对称性:(f∘g)(z) = f(g(z))。使用倒序,您将得到(g∗f)(z) = f(g(z))。就是这样。
    【解决方案2】:

    功能构成

    函数compose 的求值顺序是从右到左

    /*
     * 1. take x
     * 2. execute g(x)
     * 3. take the return value of g(x) and execute f with it
    */
    const compose = (f, g) => x => f(g(x))
    

    还有另一个名为pipe 的函数从从左到右进行计算

    const pipe = (g, f) => x => f(g(x))
    

    您的代码

    正如@Nima Hakimi 已经提到的,您已经颠倒了参数顺序。

    为了解决你的问题,我们可以

    • 切换参数的顺序

      常量 ungroupAndFlatten = compose( 压扁, 取消组合 )

      const compose = (f, g) => x => 
          f(g(x))
      
      const ungroup = obj =>
          Object.keys(obj)
              .map(x => obj[x]);
      
      const flatten = arrs =>
          arrs.reduce((acc, item) => acc.concat(item), [])
      
      const groupedByName = {
          abc: [
              { name: 'abc', effectiveDate: '2016-01-01T00:00:00+00:00' },
              { name: 'abc', effectiveDate: '2016-04-01T00:00:00+00:00' },
              { name: 'abc', effectiveDate: '2016-05-01T00:00:00+00:00' }
          ],
          abcd: [
              { name: 'abcd', effectiveDate: '2016-02-01T00:00:00+00:00' },
              { name: 'abcd', effectiveDate: '2016-09-01T00:00:00+00:00' }
          ]
      }
      
      const ungroupAndFlatten = compose(
          flatten,
          ungroup
      )
      
      console.log(
        ungroupAndFlatten(groupedByName)
      )
    • 或使用pipe

      常量 ungroupAndFlatten = 管道( 取消分组, 展平 )

      const pipe = (g, f) => x => 
          f(g(x))
      
      const ungroup = obj =>
          Object.keys(obj)
              .map(x => obj[x]);
      
      const flatten = arrs =>
          arrs.reduce((acc, item) => acc.concat(item), [])
      
      const groupedByName = {
          abc: [
              { name: 'abc', effectiveDate: '2016-01-01T00:00:00+00:00' },
              { name: 'abc', effectiveDate: '2016-04-01T00:00:00+00:00' },
              { name: 'abc', effectiveDate: '2016-05-01T00:00:00+00:00' }
          ],
          abcd: [
              { name: 'abcd', effectiveDate: '2016-02-01T00:00:00+00:00' },
              { name: 'abcd', effectiveDate: '2016-09-01T00:00:00+00:00' }
          ]
      }
      
      const ungroupAndFlatten = pipe(
          ungroup,
          flatten
      )
      
      console.log(
        ungroupAndFlatten(groupedByName)
      )

    带有n 参数的函数组合

    我们又来了

    • 撰写 const compose = (...fns) => fns.reduceRight((f, g) => (...args) => g(f(...args)))
    • 管道 const pipe = (...fns) => fns.reduce((f, g) => (...args) => g(f(...args)))

    我们的目标是编写ungroupflatten 函数groupBy
    我们可以试试

    const groupByNameAndFlattenAndUngroup = compose(
        flatten,
        ungroup,
        groupBy('name', x)  // <-- this is our problem
    )
    

    但这不起作用。我们只能用一个参数组合函数。解决方案是将groupBy 重写为 curried 版本:

    const groupBy = xs => key =>
    xs.reduce((rv, x) => {
        (rv[x[key]] = rv[x[key]] || []).push(x)
        return rv
    }, {})
    
    const groupByNameAndFlattenAndUngroup = (key, xs) => compose(
        flatten,
        ungroup,
        groupBy ('name')
    ) (xs)
    

    但是这个解决方案可以更短。如果我们将参数的顺序从groupBy 切换到groupBy = key =&gt; xs =&gt; {/*..*/},我们可以这样做:

    const groupBy = key => xs =>
        xs.reduce((rv, x) => {
            (rv[x[key]] = rv[x[key]] || []).push(x)
            return rv
        }, {})
    
    const groupByNameAndFlattenAndUngroup = compose(
        flatten,
        ungroup,
        groupBy ('name')
    )
    

    工作示例

    const arrs = [
      {name: 'abc', effectiveDate: "2016-01-01T00:00:00+00:00"},
      {name: 'abcd', effectiveDate: "2016-02-01T00:00:00+00:00"},
      {name: 'abcd', effectiveDate: "2016-09-01T00:00:00+00:00"},
      {name: 'abc', effectiveDate: "2016-04-01T00:00:00+00:00"},
      {name: 'abc', effectiveDate: "2016-05-01T00:00:00+00:00"},
    ]
    
    const compose = (...fns) => fns.reduceRight((f, g) => (...args) => 
        g(f(...args)))
        
    const ungroup = obj =>
        Object.keys(obj)
            .map(x => obj[x]);
    
    const flatten = arrs =>
        arrs.reduce((acc, item) => acc.concat(item), [])
        
    const groupBy = key => xs =>
      xs.reduce((rv, x) => {
          (rv[x[key]] = rv[x[key]] || []).push(x)
          return rv
      }, {})
      
    const groupByName = groupBy ('name')
    const groupByEffectiveDate = groupBy ('effectiveDate')
      
    const groupByNameAndFlattenAndUngroup = compose(
        flatten,
        ungroup,
        groupByName
    )
    
    const groupBygroupByEffectiveDateAndFlattenAndUngroup = compose(
        flatten,
        ungroup,
        groupByEffectiveDate
    )
    
    console.log(
      groupByNameAndFlattenAndUngroup (arrs)
    )
    
    console.log(
      groupBygroupByEffectiveDateAndFlattenAndUngroup (arrs)
    )

    【讨论】:

      【解决方案3】:

      您可以在单个函数中取消组合和展平。

      const ungroupAndFlatten = (obj) => Object.keys(obj).reduce((a, k) => {obj[k].forEach(e => a.push(e)); return a}, [])
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-06-06
        • 1970-01-01
        • 2020-07-08
        • 2018-11-17
        • 1970-01-01
        • 1970-01-01
        • 2016-11-12
        相关资源
        最近更新 更多