gpt4 book ai didi

javascript - 以累加器作为最终参数的无点归约函数 - 函数式编程 - Javascript - Immutable.js

转载 作者:行者123 更新时间:2023-11-29 20:49:37 25 4
gpt4 key购买 nike

我遇到了一种模式,我觉得它可能是某种反模式,或者也许有更好的方法来完成。

考虑以下重命名对象中键的实用函数,类似于使用终端命令 mv 重命名文件。

import { curry, get, omit, pipe, set, reduce } from 'lodash/fp'

const mv = curry(
(oldPath, newPath, source) =>
get(oldPath, source)
? pipe(
set(newPath, get(oldPath, source)),
omit(oldPath)
)(source)
: source
)

test('mv', () => {
const largeDataSet = { a: 'z', b: 'y', c: 'x' }
const expected = { a: 'z', q: 'y', c: 'x' }
const result = mv('b', 'q', largeDataSet)

expect(result).toEqual(expected)
})

这只是一个可以在任何地方使用的示例函数。接下来考虑一个大型数据集,其中可能有一小部分要重命名的键。

test('mvMore', () => {
const largeDataSet = { a: 'z', b: 'y', c: 'x' }
const expected = { a: 'z', q: 'y', m: 'x' }
const keysToRename = [['b', 'q'], ['c', 'm']]

const result = reduce(
(acc, [oldPath, newPath]) => mv(oldPath, newPath, acc),
largeDataSet,
keysToRename
)

expect(result).toEqual(expected)
})

现在我们开始讨论我的问题,它围绕着一个模式,您可能有一个大数据集和许多类似于 mv 的不同操作的小列表要对所述数据集执行。设置一个无点管道以将数据集从一个 reduce 函数向下传递到下一个似乎是理想的;但是,每个都必须将数据集作为累加器参数传递,因为我们不是在数据集上迭代,而是在一小部分操作上迭代。

test('pipe mvMore and similar transforms', () => {
const largeDataSet = { a: 'z', b: 'y', c: 'x' }
const expected = { u: 'z', r: 'y', m: 'x' }
const keysToRename = [['b', 'q'], ['c', 'm']]
const keysToRename2 = [['q', 'r'], ['a', 'u']]
const mvCall = (source, [oldPath, newPath]) => mv(oldPath, newPath, source)
const reduceAccLast = curry((fn, it, acc) => reduce(fn, acc, it))

const result = pipe(
// imagine other similar transform
reduceAccLast(mvCall, keysToRename),
// imagine other similar transform
reduceAccLast(mvCall, keysToRename2)
)(largeDataSet)

expect(result).toEqual(expected)
})

我的问题是这是否是某种反模式,或者是否有更好的方法来实现相同的结果。令我惊愕的是,通常将 reducer 函数的累加器参数用作内部状态,并对数据集进行迭代;然而,这里是相反的。大多数 reducer iteratee 函数都会改变累加器,因为它只在内部使用。在这里,数据集作为累加器参数从 reducer 传递到 reducer,因为在大型数据集上迭代没有意义,其中只有几个操作列表要对数据集执行。只要 reducer iteratee 函数(例如 mv)不改变累加器,这种模式是否有任何问题,或者我是否遗漏了一些简单的东西?


根据@tokland 的回答,我重写了测试以使用 Immutable.js 来查看不可变性的保证和潜在的性能提升是否值得付出努力。互联网上有一些关于 Immutable.js 不适合点自由风格函数式编程的喧嚣。这是有道理的;然而,并不多。据我所知,您所要做的就是编写一些调用您要使用的方法的基本函数,例如 mapfilterreduce 。不处理 Javascript 数组或对象的 Lodash 函数仍然可以使用;换句话说,处理函数的 Lodash 函数,如 currypipe,或处理字符串,如 upperCase 似乎没问题。

import { curry, pipe, upperCase } from 'lodash/fp'
import { Map } from 'immutable'

const remove = curry((oldPath, imm) => imm.remove(oldPath))
const get = curry((path, imm) => imm.get(path))
const set = curry((path, source, imm) => imm.set(path, source))
const reduce = curry((fn, acc, it) => it.reduce(fn, acc))
const reduceAcc = curry((fn, it, acc) => reduce(fn, acc, it))
const map = curry((fn, input) => input.map(fn))

const mv = curry((oldPath, newPath, source) =>
pipe(
set(newPath, get(oldPath, source)),
remove(oldPath)
)(source)
)

const mvCall = (acc, newPath, oldPath) => mv(oldPath, newPath, acc)

function log(x) {
console.log(x)
return x
}

test('mv', () => {
const largeDataSet = Map({ a: 'z', b: 'y', c: 'x' })
const expected = Map({ a: 'z', q: 'y', c: 'x' })
const result = mv('b', 'q', largeDataSet)

expect(result).toEqual(expected)
})

test('mvMore', () => {
const largeDataSet = Map({ a: 'z', b: 'y', c: 'x' })
const expected = Map({ a: 'z', q: 'y', m: 'x' })
const keysToRename = Map({ b: 'q', c: 'm' })
const result = reduce(mvCall, largeDataSet, keysToRename)

expect(result).toEqual(expected)
})

test('pipe mvMore and similar transforms', () => {
const largeDataSet = Map({ a: 'z', b: 'y', c: 'x' })
const expected = Map({ u: 'Z', r: 'Y', m: 'X' })
const keysToRename = Map({ b: 'q', c: 'm' })
const keysToRename2 = Map({ q: 'r', a: 'u' })

const result = pipe(
reduceAcc(mvCall, keysToRename),
reduceAcc(mvCall, keysToRename2),
map(upperCase)
)(largeDataSet)

const result2 = keysToRename2
.reduce(mvCall, keysToRename.reduce(mvCall, largeDataSet))
.map(upperCase)

expect(result).toEqual(expected)
expect(result2).toEqual(expected)
})

Typescript 在处理高阶函数时似乎存在一些问题,因此如果您使用 进行测试,则必须在 pipe 之前抛出 //@ts-ignore >tsc.

最佳答案

您的方法没有任何问题。有时你折叠输入对象,有时你将它用作初始累加器,这取决于算法。如果 reducer 改变了函数调用者传递的值,那么只要需要不变性,就不能使用这个 reducer。

也就是说,您的代码可能存在性能问题,具体取决于对象的大小(输入、键映射)。每次更改 key 时,都会创建一个全新的对象。如果您发现这是一个问题,您通常会使用一些有效的不可变结构来重用输入数据(映射不需要,因为您不更新它们)。在 Map 中查找示例来自 immutable.js。

关于javascript - 以累加器作为最终参数的无点归约函数 - 函数式编程 - Javascript - Immutable.js,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52581232/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com