传感器在这里没有多大意义。您的数据结构是递归的。处理递归结构的最佳代码通常需要递归算法。
换能器的工作原理
(Roman Liutikov 写了一篇关于传感器的精彩介绍。)
转换器都是关于用一个单一的数据替换多次通过相同数据的行程,将步骤的原子操作组合成一个单一的操作。
转换器将非常适合转换此代码:
xs.map(x => x * 7).map(x => x + 3).filter(isOdd(x)).take(5)
// ^ ^ ^ ^
// \ \ \ `------ Iteration 4
// \ \ `--------------------- Iteration 3
// \ `-------------------------------------- Iteration 2
// `----------------------------------------------------- Iteration 1
变成这样的东西:
xs.reduce((r, x) => r.length >= 5 ? res : isOdd(x * 7 + 3) ? res.concat(x * 7 - 3) : res, [])
// ^
// `------------------------------------------------------- Just one iteration
在 Ramda 中,因为map
, filter
, 和take
启用了转换器,我们可以转换
const foo = pipe(
map(multiply(7)),
map(add(3)),
filter(isOdd),
take(3)
)
foo([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) //=> [17, 31, 45]
(通过数据迭代四次)到
const bar = compose(
map(multiply(7)),
map(add(3)),
filter(isOdd),
take(3)
)
into([], bar, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) //=> [17, 31, 45]
它只迭代一次。(注意从pipe
到的切换compose
。转换器的组成顺序与普通函数相反。)
请注意,此类换能器的关键在于它们的操作方式都相似。 map
将一个列表转换为另一个列表,就像filter
和一样take
。虽然您可以拥有对不同类型进行操作的转换器,并且map
也filter
可能以多态方式对此类类型起作用,但只有当您组合对相同类型进行操作的函数时,它们才能一起工作。
Flatten
不适合换能器
你的结构更复杂。虽然我们当然可以创建一个以某种方式(前序、后序)抓取它的函数,因此可能会用它启动一个转换器管道,但处理递归结构的逻辑方法是使用递归算法。
扁平化这种嵌套结构的简单方法如下:
const flatten = xs => xs.reduce(
(a, x) => concat(a, isArray(x) ? flatten(x) : [x]),
[]
);
(由于各种技术原因,Ramda 的代码要复杂得多。)
然而,这个递归版本并不适合与转换器一起工作,转换器基本上必须一步一步地工作。
Uniq
不适合换能器
uniq
,另一方面,对于这种传感器来说意义不大。问题是uniq
,如果您要从传感器中获得任何好处,使用的容器必须是具有快速插入和快速查找功能的容器,Set
或者Object
最有可能的容器。假设我们使用Set
. 然后我们有一个问题,因为我们flatten
在列表上操作。
不同的方法
由于我们不能轻松地将现有功能折叠成一个可以满足您所需的功能,因此我们可能需要编写一次性的。
早期解决方案的结构使得添加唯一性约束相当容易。同样,那是:
const flatten = xs => xs.reduce(
(a, x) => concat(a, isArray(x) ? flatten(x) : [x]),
[]
);
使用辅助函数将所有元素添加到 a Set
:
const addAll = (set, xs) => xs.reduce((s, x) => s.add(x), set)
我们可以编写一个扁平化的函数,只保留唯一值:
const flattenUniq = xs => xs.reduce(
(s, x) => addAll(s, isArray(x) ? flattenUniq(x) : [x]),
new Set()
)
请注意,这有很多上面的结构,只切换到使用 a Set
,因此从切换concat
到我们的addAll
.
当然,最后你可能想要一个数组。我们可以通过用一个Set -> Array
函数包装它来做到这一点,如下所示:
const flattenUniq = xs => Array.from(xs.reduce(
(s, x) => addAll(s, isArray(x) ? flattenUniq(x) : [x]),
new Set()
))
您也可以考虑将此结果保留为Set
. 如果您真的想要一组唯一值,aSet
是合乎逻辑的选择。
这样的函数没有无点转换函数的优雅,但它可以工作,并且暴露的管道使与原始数据结构和普通flatten
函数的关系更加清晰。
我想您可以将整个冗长的答案视为一种冗长的方式来指出 user633183 在评论中所说的话:“flatten 和 uniq 都不是传感器的好用例。”