【问题标题】:Can this be refactored to use generic functional principles?这可以重构为使用通用功能原理吗?
【发布时间】:2018-05-18 04:24:13
【问题描述】:

比较器函数 ascending 接受两个参数 - ab。它必须返回一个整数来比较两者。

我有一个想要按名称排序的列表,所以我编写了以下函数。

我是否可以使用函数式惯用语来组合这两个函数,而不是让byName 负责组合生成的函数?

const ascending = (a, b) => a.localeCompare(b);
const byName = (i) => i.get('name');
const useTogether = (...fns) => ...; // is there an idiomatic function like this?

// usage
items.sort(useTogether(byName(ascending))); 

【问题讨论】:

  • 顺便说一句,按字符串对 JavaScript 数组进行排序最好使用 String#localeCompare 完成,因此您的排序函数将是 const ascendingString = (a, b) => a.localeCompare(b);,这是不可组合的。
  • const ascending = g => (a, b) =>((a,b) => a < b ? ... )(g(a), g(b)); 然后item.sort(ascending(by => by.name))
  • 看看thenBy.js

标签: javascript functional-programming


【解决方案1】:

您正在寻找contravariant functors

要正确理解它们,让我们从研究最基本的排序程序开始

const compare = (a, b) =>
  a.localeCompare (b)

const data =
  [ 'Cindy'
  , 'Alice'
  , 'Darius'
  , 'Bertrand'
  ]
  
data.sort (compare)

console.log (data)
// Alice, Bertrand, Cindy, Darius

那里没什么特别的。让我们创建第一个逆变函子,Comparison。排序会导致突变,但无论如何它只是为了演示。关注contramap

const compare = (a, b) =>
  a.localeCompare (b)

const Comparison = (f = compare) =>
  ({ contramap : g =>
       Comparison ((a, b) => f (g (a), g (b)))
   , sort : xs =>
       xs.sort (f)
   })
   
const data =
  [ { name: 'Cindy' }
  , { name: 'Alice' }
  , { name: 'Darius' }
  , { name: 'Bertrand' }
  ]
  
Comparison ()
  .contramap (x => x.name)
  .sort (data)
  
console.log (data)
// Alice, Bertrand, Cindy, Darius

组成法成立

m.contramap(f).contramap(g) == m.contramap(compose(f,g))

const compare = (a, b) =>
  a.localeCompare (b)

const Comparison = (f = compare) =>
  ({ contramap : g =>
       Comparison ((a, b) => f (g (a), g (b)))
   , sort : xs =>
       xs.sort (f)
   })

const data =
  [ { name: 'Cindy' }
  , { name: 'Alice' }
  , { name: 'Darius' }
  , { name: 'Bertrand' }
  ]

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

Comparison ()
  .contramap (compose (x => x.substr (1), x => x.name))
  // equivalent to
  // .contramap (x => x.substr (1)) // sort starting with second letter
  // .contramap (x => x.name)       // get name property
  .sort (data)
  
console.log (data)
// sorted by second letter this time (A, E, I, L)
// Darius, Bertrand, Cindy, Alice
//  ^       ^         ^      ^

实现 monoid 接口为您提供了一些很酷的东西,例如“多排序”

const Eq =
  0

const Lt =
  -1

const Gt =
  1

const Ord =
  { empty: Eq
  , concat: (a,b) =>
      a === Eq ? b : a
  }

const compare = (a, b) =>
  a < b ? Lt
    : a > b ? Gt
      : Eq

const Comparison = (f = compare) =>
  ({ compare: f
   , contramap : g =>
       Comparison ((a, b) => f (g (a), g (b)))
   , concat : m =>
       Comparison ((a, b) =>
         Ord.concat (f (a, b), m.compare (a, b)))   
   , sort : xs =>
       xs.sort (f)
   })
     
const data =
  [ { name: 'Alicia', age: 10 }
  , { name: 'Alice', age: 15 }
  , { name: 'Alice', age: 10 }
  , { name: 'Alice', age: 16 }
  ]

const sortByName =
  Comparison ()
    .contramap (x => x.name)
    
const sortByAge =
  Comparison ()
    .contramap (x => x.age)

sortByName
  .concat (sortByAge)
  .sort (data)
  
console.log ('sorted by (name,age)', data)
// Alice 10
// Alice 15
// Alice 16
// Alicia 10

sortByAge
  .concat (sortByName)
  .sort (data)

console.log ('sorted by (age,name)', data)
// Alice 10
// Alicia 10
// Alice 15
// Alice 16

阅读链接文章以获取更多有用信息和传感器简介

【讨论】:

    【解决方案2】:

    我不确定它是否满足您的需求,但这是您的useTogether(签名略有不同)的一种可能的表述,它确实有效。我不知道具有这种效果的标准函数。

    const ascending = (a, b) => a.localeCompare(b);
    const byName = (i) => i['name'];
    const useTogether = (selector, consumer) => (...fnArgs) => consumer(...fnArgs.map(selector));
    
    var items = [{ name: "C" }, { name: "A" }, { name: "B" }];
    
    console.log(
      items.sort(useTogether(byName, ascending))
    )

    【讨论】:

      猜你喜欢
      • 2011-07-09
      • 1970-01-01
      • 1970-01-01
      • 2021-11-11
      • 1970-01-01
      • 1970-01-01
      • 2021-07-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多