Как уже упоминалось, вы не можете переопределить закрытый метод S4 «+». Однако вам не нужно определять новый класс, чтобы определить функцию сложения для строк; это не идеально, поскольку оно заставляет вас преобразовать класс строк и, таким образом, привести к более уродливому коду. Вместо этого можно просто переписать функцию «+»:
«+» = function (x, y) {if (is.character (x) || is.character (y)) {return (paste (x, y, sep = ""))} else {.Primitive ("+") (x, y)}}
Тогда все должны работать как Ожидаемое:
1 + 4 1:10 + 4 «Помощь» + «Я»
Это решение кажется немного взломанным, так как вы больше не использует формальные методы, но это единственный способ получить точное поведение, которое вы хотели.
300 не очень много, поэтому да, сделайте это List:
IQueryable<Detail> details = ...
IList<Detail> detailList = details.ToList();
И теперь вы можете выбрать случайный элемент:
var randomItem = detailList[rand.Next(detailList.Count)];
, и вы могли бы повторить это 50 раз. Это, однако, приведет к дублированию, и процесс их устранения станет беспорядочным.
Итак, используйте стандартный алгоритм shuffle , а затем выберите первые 50:
Shuffle(detailList);
var selection = detailList.Take(50);
IQueryable<Detail> details = myList.OrderBy(x => Guid.NewGuid()).ToList();
После этого просто пройдите по ней линейно:
var item1 = details[0];
Это позволит избежать дублирования.
Collections.Shuffle
, вы захотите сослаться на этот алгоритм или разместить свой встроенный.
– Mike Perrenoud
7 November 2013 в 14:59
Guid
определенно самый простой.
– Jeroen Vannevel
7 November 2013 в 15:00
Если вы заранее знаете общее количество элементов для случайного выбора, вы можете сделать это, не переходя в список в первую очередь.
Следующий метод сделает это за вас:
/// <summary>Randomly selects items from a sequence.</summary>
/// <typeparam name="T">The type of the items in the sequence.</typeparam>
/// <param name="sequence">The sequence from which to randomly select items.</param>
/// <param name="count">The number of items to randomly select from the sequence.</param>
/// <param name="sequenceLength">The number of items in the sequence among which to randomly select.</param>
/// <param name="rng">The random number generator to use.</param>
/// <returns>A sequence of randomly selected items.</returns>
/// <remarks>This is an O(N) algorithm (N is the sequence length).</remarks>
public static IEnumerable<T> RandomlySelectedItems<T>(IEnumerable<T> sequence, int count, int sequenceLength, System.Random rng)
{
if (sequence == null)
{
throw new ArgumentNullException("sequence");
}
if (count < 0 || count > sequenceLength)
{
throw new ArgumentOutOfRangeException("count", count, "count must be between 0 and sequenceLength");
}
if (rng == null)
{
throw new ArgumentNullException("rng");
}
int available = sequenceLength;
int remaining = count;
var iterator = sequence.GetEnumerator();
for (int current = 0; current < sequenceLength; ++current)
{
iterator.MoveNext();
if (rng.NextDouble() < remaining/(double)available)
{
yield return iterator.Current;
--remaining;
}
--available;
}
}
(Ключевым моментом здесь является необходимость знать с самого начала количество элементов на выбор, это немного уменьшает полезность. Но если получение счета выполняется быстро и буферизует все элементы, то потребуется слишком много памяти , это полезное решение.)
Вот еще один подход, который использует выборки резервуаров
. Этот подход НЕ требуется знать общее количество элементы на выбор, но он должен буферизовать вывод. Разумеется, он также должен перечислить всю коллекцию ввода.
Поэтому это действительно полезно, если вы заранее не знаете количество элементов на выбор (или количество элементов для выбора из очень большой).
Я бы рекомендовал просто перетасовать список в соответствии с ответом Хенка, а не делать это таким образом, но я включаю его здесь ради интереса:
// n is the number of items to randomly choose.
public static List<T> RandomlyChooseItems<T>(IEnumerable<T> items, int n, Random rng)
{
var result = new List<T>(n);
int index = 0;
foreach (var item in items)
{
if (index < n)
{
result.Add(item);
}
else
{
int r = rng.Next(0, index + 1);
if (r < n)
result[r] = item;
}
++index;
}
return result;
}
В качестве дополнения к ответу Хенка, это каноническая реализация алгоритма Shuffle, который он упоминает. В этом случае _rng
является экземпляром Random
:
/// <summary>Shuffles the specified array.</summary>
/// <typeparam name="T">The type of the array elements.</typeparam>
/// <param name="array">The array to shuffle.</param>
public void Shuffle<T>(IList<T> array)
{
for (int n = array.Count; n > 1;)
{
int k = _rng.Next(n);
--n;
T temp = array[n];
array[n] = array[k];
array[k] = temp;
}
}
var l = new List<string>();
l.Add("A");
l.Add("B");
l.Add("C");
l.Add("D");
l.Add("E");
l.Add("F");
l.Add("G");
l.Add("H");
l.Add("I");
var random = new Random();
var nl = l.Select(i=> new {Value=i,Index = random.Next()});
var finalList = nl.OrderBy(i=>i.Index).Take(3);
foreach(var i in finalList)
{
Console.WriteLine(i.Value);
}
Random rnd = new Random();
IQueryable<Detail> details = myList.OrderBy(x => rnd.Next()).Take(50);