我正在尝试以功能方式用Ramda实现LoDash的sampleSize方法。
有什么想法吗?除了从给定数组中获取randomIndex
之外,我完全不愿意采取任何其他措施。如何使用Ramda遍历递归?
因此,该函数应如下所示:
export const sampleSize = curry((size, list) => compose(
// two paths of code
// one to splice messages at the randomIndex
// recursion with the spliced array until length === size
randomIndex
)(list))
答案 0 :(得分:2)
我可能不会使用Ramda来做到这一点。请注意,我是Ramda的创始人之一,并且是忠实粉丝。但是Ramda是为函数式编程而设计的。函数式编程的主要宗旨之一是使用纯函数,纯函数在其参数外不使用任何输入,并且除了返回值外没有其他作用。对于相同的输入,它们应该始终返回相同的输出。当代码应该随机执行某项操作时,这将不起作用。 1
您可以使用类似lodash的代码,Fisher-Yates shuffle的早期返回版本,也可以使用类似的方法,这样也可以使结果保持原始数组中的顺序:>
const sampleSize = (size, list, collected = []) => size < 1 || list.length < 1
? collected
: size >= list.length
? [...collected, ...list] // or throw error?
: Math.random() < size / list.length
? sampleSize(size -1, list.slice(1), [...collected, list[0]])
: sampleSize(size, list.slice(1), collected)
console.log(sampleSize(4, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))
console.log(sampleSize(4, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))
console.log(sampleSize(4, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))
console.log(sampleSize(0, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))
console.log(sampleSize(10, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))
console.log(sampleSize(20, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))
这是急着写的,可能有错误,但是应该很接近。想法是一次检查每个元素以查看是否应将其包括在内,并根据剩余要包含的元素和列表中剩余的元素来调整机会。
Fisher-Yates版本将比此版本更有效,尤其是因为它使用了递归,即使规范要求已经使用了几年,该递归甚至在今天也无法被引擎有效地优化。但是Fisher-Yates不会保留原始排序顺序。如果您想要的话,这可能适合您。
1 请注意,Ramda确实有一个random number extension,但长期以来一直被丢弃。它使用了可重复的伪随机数生成器,听起来似乎很矛盾,但是在使用纯函数时才有意义。
答案 1 :(得分:1)
首先让我们定义一个函数,该函数将返回一个min
(包括)和max
(不包括)之间的随机数。我们可以使用它,因为min
将始终设置为0,
而max
将始终设置为新列表的长度-1
const random = curry((min, max) => Math.floor(Math.random() * (max - min) - min));
然后我们需要一个函数,该函数将获取一个列表并返回两件事(在数组中):
const takeFromList = converge(
converge(pair, [nth, flip(remove)(1)]) [compose(random(0), length), identity]);
将所有内容放在一起:
如您所见,如果请求的样本大小大于实际列表大小,它将以随机顺序返回整个列表。
const {curry, min, nth, identity, converge, pair, flip, compose, length, remove, flatten} = R;
const random = curry((min, max) => Math.floor(Math.random() * (max - min) - min));
const takeFromList = converge(converge(pair, [nth, flip(remove)(1)]), [compose(random(0), length), identity]);
const sampleSize = curry((size, list) => {
const [el, newList] = takeFromList(list);
const len = min(size, list.length);
return len - 1 > 0 ? flatten([el, sampleSize(len - 1, newList)]) : [el];
});
console.log(sampleSize(2, [1,2,3,4,5]));
console.log(sampleSize(2, [1,2,3,4,5]));
console.log(sampleSize(2, [1,2,3,4,5]));
console.log(sampleSize(20, [1,2,3,4,5]));
console.log(sampleSize(20, [1,2,3,4,5]));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>