Реализация этого в качестве генератора делает работу с ним довольно приятной.Обратите внимание, что эта реализация отличается от тех, которые требуют перетасовки всего входного массива в первую очередь.
Эта sample
функция работает лениво, давая вам 1 случайный элемент за итерацию до N
элементов, которые вы запрашиваете.Это хорошо, потому что если вы просто хотите 3 пунктов из списка 1000 , вам не нужно сначала трогать все 1000 элементов.
// sample :: Integer -> [a] -> [a]
const sample = n => function* (xs) {
let ys = xs.slice(0);
let len = xs.length;
while (n > 0 && len > 0) {
let i = (Math.random() * len) >> 0;
yield ys.splice(i,1)[0];
n--; len--;
}
}
// example inputs
let items = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
let numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
// get 3 random items
for (let i of sample(3) (items))
console.log(i); // f g c
// partial application
const lotto = sample(3);
for (let i of lotto(numbers))
console.log(i); // 3 8 7
// shuffle an array
const shuffle = xs => Array.from(sample (Infinity) (xs))
console.log(shuffle(items)) // [b c g f d e a]
Я решил реализовать sample
таким образом, чтобы не изменять входной массив, но можно легко утверждать, что мутантная реализация предпочтительна.
Например, функция shuffle
может захотеть изменить исходный массив ввода.Или вы можете выбрать один и тот же вход в разное время, обновляя вход каждый раз.
// sample :: Integer -> [a] -> [a]
const sample = n => function* (xs) {
let len = xs.length;
while (n > 0 && len > 0) {
let i = (Math.random() * len) >> 0;
yield xs.splice(i,1)[0];
n--; len--;
}
}
// deal :: [Card] -> [Card]
const deal = xs => Array.from(sample (2) (xs));
// setup a deck of cards (13 in this case)
// cards :: [Card]
let cards = 'A234567890JQK'.split('');
// deal 6 players 2 cards each
// players :: [[Card]]
let players = Array.from(Array(6), $=> deal(cards))
console.log(players);
// [K, J], [6, 0], [2, 8], [Q, 7], [5, 4], [9, A]
// `cards` has been mutated. only 1 card remains in the deck
console.log(cards);
// [3]
sample
больше не является pure функцией из-за входной мутации массива, но в определенных обстоятельствах (продемонстрированных выше) это может иметь больше смысла.
Еще одна причина, по которой я выбрал генератор вместо функции, которая просто возвращает массив, заключается в том, что вы можете продолжить выборку до какого-то определенного условия.число из списка из 1 000 000 случайных чисел.
- «Сколько мне нужно выбрать?» - вам не нужно указывать
- "Должен ли я сначала найти все простые числа, а затем выбрать случайное простое число? " - Нет.
Поскольку мы работаем с генератором, эта задача тривиальна
const randomPrimeNumber = listOfNumbers => {
for (let x of sample(Infinity) (listOfNumbers)) {
if (isPrime(x))
return x;
}
return NaN;
}
Это будет непрерывно производить выборку 1 случайного числа за раз, x
, проверять, является ли оно простым, а затем возвращать x
, если оно есть.Если список чисел исчерпан до того, как найдено простое число, возвращается NaN
.
Примечание:
Этот ответ Первоначально был поделен на другой вопрос, который был закрыт как дубликат этого.Поскольку он сильно отличается от других решений, представленных здесь, я решил поделиться этим здесь