0

我希望能够折叠/缩小地图,就像使用 Array 和 Set 一样。我看到的最接近的是一种叫做getFoldableWithIndex但我不知道如何使用它或让它与 Typescript 一起编译的东西。我觉得烦人的一件事是它需要一个 ORD。也许这是使函数更具确定性所必需的,但排序对于许多折叠/归约任务并不重要,并且会降低性能。我的解决方法是离开 fp-ts,生成一个数组或可迭代的 [key,value] 对,并对其进行简单的缩减以找到年龄最大的人。

import { map as MAP, ord as ORD } from "fp-ts"

type Person = string;
type Age = number;

const oldestPerson = (ps:Map<Person, Age>) =>
  pipe(ps, MAP.getFoldableWithIndex<Person>(ORD.fromCompare<Person>((a,b)=>0)))......

刚刚注意到一个更新的未发布版本支持减少。仍然不知道为什么需要 ORD 或如何使用 getFolderableWithIndex 或更新版本何时发布。

https://github.com/gcanti/fp-ts/blob/2.11/src/ReadonlyMap.ts

4

2 回答 2

1

如果您想留在“内部fp-ts”,您可以执行以下操作:

import { flow, pipe, tuple } from "fp-ts/function"
import * as Mn from "fp-ts/Monoid"
import * as N from "fp-ts/number"
import * as O from "fp-ts/Option"
import * as Ord from "fp-ts/Ord"
import * as RM from "fp-ts/ReadonlyMap"
import * as RT from "fp-ts/ReadonlyTuple"
import * as Sg from "fp-ts/Semigroup"
import * as S from "fp-ts/string"

type Person = string
type Age = number
type PersonAge = readonly [Person, Age]

const ordByAge: Ord.Ord<PersonAge> = pipe(N.Ord, Ord.contramap(RT.snd))

const monoidMaxByAge: Mn.Monoid<O.Option<PersonAge>> = pipe(
  ordByAge,
  Sg.max,
  O.getMonoid
)

const oldestPerson = (ps: ReadonlyMap<Person, Age>) =>
  pipe(
    ps,
    RM.foldMapWithIndex(S.Ord)(monoidMaxByAge)(flow(tuple, O.some)),
    O.map(RT.fst)
  )

这非常冗长,并没有解决您对性能的担忧。

我觉得烦人的一件事是它需要一个 ORD。也许这是使函数更具确定性所必需的,但排序对于许多折叠/归约任务并不重要,并且会降低性能。

你说得对,它是关于决定论的。本机 JSMap以插入顺序存储条目,因此如果没有Ord键的实例,此oldestPerson函数可能会为两个给定Map的 s 产生不同的结果,除了插入顺序之外它们是等效的。我会认为这是出乎意料的行为。

这是我期望持有的:

import * as assert from "assert"

const aliceBob: ReadonlyMap<Person, Age> = new Map([
  ["Alice", 25],
  ["Bob", 25],
])

const bobAlice: ReadonlyMap<Person, Age> = new Map([
  ["Bob", 25],
  ["Alice", 25],
])

const emptyMap: ReadonlyMap<Person, Age> = new Map([])

assert.deepStrictEqual(oldestPerson(aliceBob), oldestPerson(bobAlice))
assert.deepStrictEqual(oldestPerson(emptyMap), O.none)

我同意其中的许多函数fp-ts/ReadonlyMap效率不高,我认为它们只是作为本机数据结构的最小“功能 API”包装器,不能很好地处理不变性和确定性。如果性能更受关注,我可能会像您一样使用本机方法。

一个叫的东西,getFoldableWithIndex但我不知道如何使用它或让它与 Typescript 一起编译。

getFoldableWithIndex将返回一个FoldableWithIndex类型类实例,在这里对您没有用处。这是一个,您可以将其作为参数传递给需要FoldableWithIndex实例的函数,我什至不确定是否有一个很好的示例 one 。有一个traverse_in fp-ts/Foldablewhich 接受一个Foldable实例,但没有对应traverseWithIndex_的 ,尽管该假设函数接受一个FoldableWithIndex实例。中有一些 函数fp-ts/FoldableWithIndex但这些函数是关于将两个实例组合FoldableWithIndex成另一个实例(这在这里没有用)。

您可以FoldableWithIndex直接使用它:类型类实例是一个带有uncurried foldMapWithIndex方法的对象,因此如果您想让原始代码片段更加详细,您可以这样做:

const oldestPerson = (ps: ReadonlyMap<Person, Age>) =>
  pipe(
    RM.getFoldableWithIndex(S.Ord).foldMapWithIndex(monoidMaxByAge)(
      ps,
      flow(tuple, O.some)
    ),
    O.map(RT.fst)
  )

但这些实例并不是真的要直接在 a 中使用pipe,这就是我使用顶级RM.foldMapWithIndex. 这里有更多关于类型类如何fp-ts工作的信息

于 2021-08-09T03:35:21.357 回答
0

你可以:

import { ordNumber } from 'fp-ts/Ord'

type Name = string
type Age = number
type Persons = Map<Name, Age>
const persons: Persons = new Map()

persons.set('Joao', 13)
persons.set('Roberto', 20)

const sorted = new Map(
  [...persons.entries()].sort((a, b) => ordNumber.compare(b[1], a[1]))
)

但是,重要的是要注意,在此示例中,您persons通过调用来改变变量.set

一种更纯粹、更实用的方法是:

import { ordNumber } from 'fp-ts/Ord'
import { Eq as StringEq } from 'fp-ts/string'
import * as F from 'fp-ts/function'
import * as M from 'fp-ts/Map'

type Name = string
type Age = number
const insertIntoPersons = M.upsertAt(StringEq)

const persons = F.pipe(
  new Map<Name, Age>(),
  insertIntoPersons('Joao', 13),
  insertIntoPersons('Roberto', 20)
)

const sorted = new Map(
  [...persons.entries()].sort((a, b) => ordNumber.compare(b[1], a[1]))
)
于 2021-07-20T12:41:44.143 回答