gpt4 book ai didi

functional-programming - 有条件的 Ramda 管道?

转载 作者:行者123 更新时间:2023-12-04 01:35:41 25 4
gpt4 key购买 nike

我有一个过滤器函数,它基本上具有多个确定过滤逻辑的变量。如果定义了变量,我想过滤——如果没有,我不想过滤(即在管道中执行函数)。更一般地说,有一个谓词,我可以检查管道的每个参数,以确定我应该调用它还是只传递给下一个函数。

我这样做是为了防止复杂的分支逻辑,但是对于函数式编程来说还是很新的,我认为这将是重构的最佳方式。

例如:

resources = R.pipe(
filterWithRadius(lat, lng, radius), // if any of these arguments are nil, act like R.identity
filterWithOptions(filterOptions)(keyword), // if either filterOptions or keyword is nil, act like R.identity
filterWithOptions(tagOptions)(tag) // same as above.
)(resources);

我正在考虑使用 R.unless/ R.when但它似乎不适用于具有多个参数的函数。 R.pipeWith如果它处理函数参数而不是在这里会很有用。

作为示例实现:

const filterWithRadius = R.curry((lat, long, radius, resources) =>
R.pipe(
filterByDistance(lat, long, radius), // simply filters down a geographic location, will fail if any of lat/long/radius are not defined
R.map(addDistanceToObject(lat, long)), // adds distance to the lat and long to prop distanceFromCenter
R.sortBy(R.prop("distanceFromCenter")) // sorts by distance
)(resources)
);
resources是这些资源对象的数组。本质上,每个函数, filterRadiusfilterOptions是纯函数,需要一组资源和有效参数(不是未定义),并输出一个新的过滤列表。所以这里的目标是以某种方式组合(或重构),如果参数都是未定义的,它将运行函数,否则只是充当身份。

有比这更清洁/更好的方法吗?

resources = R.pipe(
lat && lng && radius
? filterWithRadius(lat, lng, radius)
: R.identity,
keyword ? filterWithOptions(filterOptions)(keyword) : R.identity,
tag ? filterWithOptions(tagOptions)(tag) : R.identity
)(resources);

最佳答案

我认为您希望将这种行为的责任放在错误的地方。如果您希望管道函数对某些数据具有一种行为,而对其他数据具有不同的行为(或者在这种情况下,缺少数据),那么这些单独的函数应该处理它,而不是包装它们的管道函数。

但是,正如 Ori Drori 指出的那样,您可以编写一个函数装饰器来实现这一点。

这里有一个建议:

// Dummy implementations
const filterWithRadius = (lat, lng, radius, resources) =>
({...resources, radiusFilter: `${lat}-${lng}-${radius}`})
const filterWithOptions = (opts, val, resources) =>
({...resources, [`optsFilter-${opts}`]: val})

// Test function (to be used in pipelines, but more general)
const ifNonNil = (fn) => (...args) => any(isNil, args)
? identity
: (data) => fn (...[...args, data])
// alternately, for variadic result : (...newArgs) => fn (...[...args, ...newArgs])

// Pipeline call
const getUpdatedResources = (
{lat, lng, radius, filterOptions, keyword, tagOptions, tag}
) => pipe (
ifNonNil (filterWithRadius) (lat, lng, radius),
ifNonNil (filterWithOptions) (filterOptions, keyword),
ifNonNil (filterWithOptions) (tagOptions, tag)
)

// Test data
const resources = {foo: 'bar'}

const query1 = {
lat: 48.8584, lng: 2.2945, radius: 10,
filterOptions: 'baz', keyword: 'qux',
tagOptions: 'grault', tag: 'corge'
}

const query2 = {
lat: 48.8584, lng: 2.2945, radius: 10,
tagOptions: 'grault', tag: 'corge'
}

const query3 = {
lat: 48.8584, lng: 2.2945, radius: 10,
filterOptions: 'baz', keyword: 'qux',
}

const query4 = {
filterOptions: 'baz', keyword: 'qux',
tagOptions: 'grault', tag: 'corge'
}

const query5 = {
lat: 48.8584/*, lng: 2.2945*/, radius: 10,
filterOptions: 'baz', keyword: 'qux',
tagOptions: 'grault', tag: 'corge'
}

const query6 = {}

// Demo
console .log (getUpdatedResources (query1) (resources))
console .log (getUpdatedResources (query2) (resources))
console .log (getUpdatedResources (query3) (resources))
console .log (getUpdatedResources (query4) (resources))
console .log (getUpdatedResources (query5) (resources))
console .log (getUpdatedResources (query6) (resources))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script> const {any, isNil, pipe, identity} = R </script>


我们从您的 filter* 的虚拟实现开始。函数,它们只是将属性添加到输入对象。

这里的重要功能是 ifNotNil .它的函数为 n参数,返回 n - 1 的函数调用时检查这些参数是否为 nil 的参数.如果有,则返回标识函数;否则它返回一个有一个参数的函数,该函数又用 n - 1 调用原始函数论据和最新的一个。

我们使用它来构建一个管道,该管道将从接受所需变量的函数返回(这里从潜在的查询对象天真地解构)。通过传递查询然后传递要转换的实际数据来调用该函数。

这些示例显示了包含和排除的参数的各种组合。

这假设您的函数没有柯里化(Currying),也就是说, filterWithRadius看起来像 (lat, lng, radius, resources) => ...如果它们是 curry ,我们可以这样写:

const ifNonNil = (fn) => (...args) => any(isNil, args) 
? identity
: reduce ((f, arg) => f(arg), fn, args)



const filterWithRadius = (lat) => (lng) => (radius) => (resources) => 
({...resources, radiusFilter: `${lat}-${lng}-${radius}`})

但仍在管道中称为

pipe (
ifNonNil (filterWithRadius) (lat, lng, radius),
// ...
)

您甚至可以在同一管道中混合和匹配 curry 和非 curry 版本,尽管我希望这会增加困惑。

关于functional-programming - 有条件的 Ramda 管道?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59676424/

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