Вопрос описывает специализированную логику, которая, кажется, несколько, как скользящее среднее, но только с использованием существующих значений для вычисления среднего значения. Для этого требуется подход «вперед», поскольку вы не можете знать, будет ли «текущее» значение использоваться в окончательной последовательности без использования следующего значения в последовательности. Хотя этот тип логики может быть выполнен с использованием методов расширения Linq, таких как Skip и Take, я не вижу разумного способа использования синтаксиса Linq для этого. Это дает хорошие академические учения. Но в реальном мире этот случай использования требует простого подхода. Даже если бы вы могли заставить его работать, синтаксис Linq был бы гораздо менее читабельным и поддерживаемым и почти наверняка поразил бы производительность по сравнению с более прямыми подходами.
Однако одной из наиболее полезных функций Linq является библиотека методов расширения. Они обеспечивают «свободный интерфейс», который позволяет выполнять несколько операций в одном заявлении. Следующий пример использует тот же подход и возвращает результаты, ожидаемые OP (как упоминалось в вопросе и некоторых комментариях). Он гибкий, выразительный и должен хорошо работать. И это близко к Linq, поскольку вы собираетесь получить этот вид приложения.
static public class MyExtensions
{
static public IEnumerable<double> GetPointAverages(this IEnumerable<double> points)
{
var e = points.GetEnumerator();
var reading = e.MoveNext();
while (reading)
{
var value = e.Current;
reading = e.MoveNext();
if (reading && e.Current - value < .5)
{
value = (value + e.Current)/2;
reading = e.MoveNext();
}
yield return value;
}
}
}
class Program
{
static void Main()
{
var pointsArray = new[] { 2.1, 2.2, 2.6, 4, 4.2, 4.7, 4.8 };
var averages = String.Join(", ",
pointsArray.GetPointAverages().Select(p => p.ToString())
);
Console.WriteLine("Result: {0}", averages);
Console.WriteLine();
var pointsList = new List<double>() { 8.8, 9.0 };
averages = String.Join(", ",
pointsList.GetPointAverages().Select(p => p.ToString())
);
Console.WriteLine("Result: {0}", averages);
}
}
-
Result: 2.15, 2.6, 4.1, 4.75
Result: 8.9
Использование как массив и список предназначен для демонстрации, что это не имеет значения, какой из коллекции используется для хранения значений до тех пор он поддерживает IEnumerable<double>
,
-
Это может быть полезно, чтобы увидеть различные конструкции, которые были расследованы до прибытия в ответе выше. ОП упоминается с использованием цикла «while». Это привело к некоторому экспериментированию для воспроизведения желаемых результатов с целью преобразования тестируемого подхода в синтаксис Linq.
Вот пример Linq, используя один оператор, и предполагает «аналогичные» означает иметь то же целое значение:
var points =
from p in new[] { 2.1, 2.2, 4, 4.1, 8, 8.2 }
group p by (int) p into avgs
select avgs.Average();
Console.WriteLine(String.Join(", ", points.Select(p => p.ToString())));
Result: 2.15, 4.05, 8.1
ОП спросил о среднем в пределах .5 «текущего» номер. Отменив определение «текущий» на данный момент, вот пример усреднения только чисел, которые находятся в пределах .5 их целочисленного значения.
var points =
from p in new[] { 2.1, 2.2, 2.6, 4, 4.2, 4.7, 4.8 }
let intp = (double)((int)p)
let grp = (p - intp < .5) ? intp : p
group p by grp into avgs
select avgs.Average();
var averages = String.Join(", ", points.Select(p => p.ToString()));
Console.WriteLine(averages);
Result: 2.15, 2.6, 4.1, 4.7, 4.8
Концепция «текущего» номера исключает синтаксис Linq в качестве опции. При использовании Linq, поскольку он предназначен для работы, вы всегда смотрите только на один элемент в последовательности. Вы, теоретически, не знаете положения внутри последовательности или других элементов в последовательности. Механизм группировки позволяет использовать методы агрегирования, которые накапливают значения в режиме «как вы». Использование «текущего» числа, заданного в вопросе, требует перспективного подхода и возрастающей упорядоченной последовательности. Знания и их использование не являются частью дизайна Linq. Однако перевод того, что мы сделали в Linq в логику циклов, может помочь привести к решению, подобному Linq.
выше Linq заявление будет трансформироваться в нечто петли, как это:
var points = new[] { 2.1, 2.2, 2.6, 4, 4.2, 4.7, 4.8 };
var groups = new Dictionary<double, List<double>>();
foreach (var p in points)
{
var intp = (double)((int)p);
if (p - intp < .5)
{
if (!groups.ContainsKey(intp))
{
groups[intp] = new List<double>();
}
groups[intp].Add(p);
}
else
{
groups[p] = new List<double> { p };
}
}
points = groups.Select(dict => dict.Value.Average()).ToArray();
«За» перевод цикл будет выглядеть примерно так:
for (int i = 0; i < points.Length; i++)
{
var p = points[i];
var intp = (double)((int)points[i]);
if (p - intp < .5)
{
if (!groups.ContainsKey(intp))
{
groups[intp] = new List<double>();
}
groups[intp].Add(p);
}
else
{
groups[p] = new List<double> { p };
}
}
Как правило, если вы можете представить, что итерация с использованием foreach, вы почти наверняка можете использовать Linq. Если вам нужен доступ к другим элементам в последовательности во время итерации, синтаксис Linq не будет работать.
Следующий цикл «для» возвращает ожидаемые результаты и дает нам представление о том, как мы могли бы использовать перечислитель для решения нашей проблемы. Метод расширения, показанный в верхней части ответа, был получен из этой логики. Он более гибкий и должен работать так же хорошо.
var tmpPoints = new List<double>();
for (var i = 0; i < points.Length;)
{
var value = points[i];
var next = i + 1;
if (next < points.Length && points[next] - points[i] < .5)
{
value = (points[i] + points[next])/2;
i = next + 1;
}
else
{
i++;
}
tmpPoints.Add(value);
}
points = tmpPoints.ToArray();
// Results using the two example sequences
points = new[] { 2.1, 2.2, 2.6, 4, 4.2, 4.7, 4.8 };
Result: 2.15, 2.6, 4.1, 4.75
points = new[] { 8.8, 9.0 };
Result: 8.9
Вы ищете ответ с помощью Linq, или тонкая петля? – ispiro
@ispiro Либо один. Кроме того, я уже пробовал использовать 'Except()', в моем цикле, который я использую, чтобы пройти через список «points», но я все еще заканчиваю повторение числа, которое я удалил, используя 'Except()'. – John
Опираясь на приоритет слева направо? Для '{1.1,1.2,1.3,1.4,1.5,1.6}' ли '{1.1,1.2,1.3}' сглаживается вместе с {{1.4.1.5,1.6} '. Потому что это также может быть '{1.1.1.2}' then '{1.3,1.4,1.5}' then '{1.6}' или многие другие комбинации. – Derpy