Điều này nghe có vẻ khập khiễng, nhưng tôi đã không thể tìm thấy một lời giải thích thực sự tốt về Aggregate
.
Tốt có nghĩa là ngắn gọn, mô tả, toàn diện với một ví dụ nhỏ và rõ ràng.
Điều này nghe có vẻ khập khiễng, nhưng tôi đã không thể tìm thấy một lời giải thích thực sự tốt về Aggregate
.
Tốt có nghĩa là ngắn gọn, mô tả, toàn diện với một ví dụ nhỏ và rõ ràng.
Câu trả lời:
Định nghĩa dễ hiểu nhất Aggregate
là nó thực hiện một thao tác trên từng thành phần của danh sách có tính đến các hoạt động đã đi trước đó. Đó là để nói rằng nó thực hiện hành động trên phần tử thứ nhất và thứ hai và mang kết quả về phía trước. Sau đó, nó hoạt động trên kết quả trước đó và yếu tố thứ ba và tiến về phía trước. Vân vân.
Ví dụ 1. Tổng hợp số
var nums = new[]{1,2,3,4};
var sum = nums.Aggregate( (a,b) => a + b);
Console.WriteLine(sum); // output: 10 (1+2+3+4)
Điều này thêm 1
và 2
để thực hiện 3
. Sau đó thêm 3
(kết quả của trước đó) và 3
(yếu tố tiếp theo trong chuỗi) để thực hiện 6
. Sau đó thêm 6
và 4
thực hiện 10
.
Ví dụ 2. tạo một csv từ một chuỗi các chuỗi
var chars = new []{"a","b","c", "d"};
var csv = chars.Aggregate( (a,b) => a + ',' + b);
Console.WriteLine(csv); // Output a,b,c,d
Điều này hoạt động theo nhiều cách tương tự. Nối a
một dấu phẩy và b
thực hiện a,b
. Sau đó nối a,b
với dấu phẩy và c
thực hiện a,b,c
. và như thế.
Ví dụ 3. Nhân số bằng cách sử dụng hạt giống
Để hoàn thiện, có một sự quá tải trong Aggregate
đó có một giá trị hạt giống.
var multipliers = new []{10,20,30,40};
var multiplied = multipliers.Aggregate(5, (a,b) => a * b);
Console.WriteLine(multiplied); //Output 1200000 ((((5*10)*20)*30)*40)
Giống như các ví dụ trên, điều này bắt đầu bằng một giá trị 5
và nhân nó với phần tử đầu tiên của chuỗi 10
cho kết quả 50
. Kết quả này được chuyển tiếp và nhân với số tiếp theo trong chuỗi 20
để đưa ra kết quả 1000
. Điều này tiếp tục thông qua 2 yếu tố còn lại của chuỗi.
Ví dụ trực tiếp: http://rextester.com/ZXZ64749
Tài liệu: http://msdn.microsoft.com/en-us/l Library / bb548651.aspx
Phụ lục
Ví dụ 2, ở trên, sử dụng nối chuỗi để tạo danh sách các giá trị được phân tách bằng dấu phẩy. Đây là một cách đơn giản để giải thích việc sử dụng Aggregate
đó là ý định của câu trả lời này. Tuy nhiên, nếu sử dụng kỹ thuật này để thực sự tạo ra một lượng lớn dữ liệu được phân tách bằng dấu phẩy, thì sẽ phù hợp hơn khi sử dụng StringBuilder
và điều này hoàn toàn tương thích với Aggregate
việc sử dụng quá tải hạt giống để bắt đầu StringBuilder
.
var chars = new []{"a","b","c", "d"};
var csv = chars.Aggregate(new StringBuilder(), (a,b) => {
if(a.Length>0)
a.Append(",");
a.Append(b);
return a;
});
Console.WriteLine(csv);
Ví dụ cập nhật: http://rextester.com/YZCVXV6464
TakeWhile
sau đó Aggregate
- đó là nhịp đập của các tiện ích mở rộng - chúng có thể dễ dàng xâu chuỗi . Vì vậy, bạn kết thúc với TakeWhile(a => a == 'a').Aggregate(....)
. Xem ví dụ này: rextester.com/WPRA60543
var csv = string.Join(",", chars)
(không cần tổng hợp hoặc trình tạo chuỗi) - nhưng vâng, tôi biết điểm của câu trả lời là đưa ra cách sử dụng tổng hợp sao cho thật tuyệt. Nhưng tôi vẫn muốn đề cập đến việc không nên tham gia chuỗi, đã có một phương pháp dành riêng cho điều đó ....
var biggestAccount = Accounts.Aggregate((a1, a2) => a1.Amount >= a2.Amount ? a1 : a2);
Nó một phần phụ thuộc vào mức độ quá tải mà bạn đang nói đến, nhưng ý tưởng cơ bản là:
(currentValue, sequenceValue)
thành(nextValue)
currentValue = nextValue
currentValue
Bạn có thể thấy Aggregate
bài đăng trong loạt Edulinq của tôi hữu ích - nó bao gồm một mô tả chi tiết hơn (bao gồm các tình trạng quá tải khác nhau) và việc triển khai.
Một ví dụ đơn giản là sử dụng Aggregate
thay thế cho Count
:
// 0 is the seed, and for each item, we effectively increment the current value.
// In this case we can ignore "item" itself.
int count = sequence.Aggregate(0, (current, item) => current + 1);
Hoặc có lẽ tổng hợp tất cả độ dài của chuỗi trong chuỗi chuỗi:
int total = sequence.Aggregate(0, (current, item) => current + item.Length);
Cá nhân tôi hiếm khi thấy Aggregate
hữu ích - các phương pháp tổng hợp "phù hợp" thường đủ tốt cho tôi.
Tổng hợp siêu ngắn hoạt động như nếp gấp trong Haskell / ML / F #.
Hơi dài hơn một chút .Max (), .Min (), .Sum (), .Alusive () tất cả lặp lại trên các phần tử trong một chuỗi và tổng hợp chúng bằng cách sử dụng hàm tổng hợp tương ứng. .Aggregate () là tổng hợp tổng quát ở chỗ nó cho phép nhà phát triển chỉ định trạng thái bắt đầu (còn gọi là seed) và hàm tổng hợp.
Tôi biết bạn đã yêu cầu một lời giải thích ngắn nhưng tôi đoán rằng những người khác đã đưa ra một vài câu trả lời ngắn gọn. Tôi nghĩ rằng bạn có thể sẽ quan tâm đến một câu hỏi dài hơn
Phiên bản dài với mã Một cách để minh họa những gì nó có thể được hiển thị cách bạn triển khai Độ lệch chuẩn mẫu một lần bằng cách sử dụng foreach và một lần sử dụng .Aggregate. Lưu ý: Tôi không ưu tiên hiệu suất ở đây vì vậy tôi lặp đi lặp lại nhiều lần so với colleciton một cách không cần thiết
Đầu tiên một hàm trợ giúp được sử dụng để tạo tổng khoảng cách bậc hai:
static double SumOfQuadraticDistance (double average, int value, double state)
{
var diff = (value - average);
return state + diff * diff;
}
Sau đó, Độ lệch chuẩn mẫu sử dụng ForEach:
static double SampleStandardDeviation_ForEach (
this IEnumerable<int> ints)
{
var length = ints.Count ();
if (length < 2)
{
return 0.0;
}
const double seed = 0.0;
var average = ints.Average ();
var state = seed;
foreach (var value in ints)
{
state = SumOfQuadraticDistance (average, value, state);
}
var sumOfQuadraticDistance = state;
return Math.Sqrt (sumOfQuadraticDistance / (length - 1));
}
Sau đó, một lần sử dụng .Aggregate:
static double SampleStandardDeviation_Aggregate (
this IEnumerable<int> ints)
{
var length = ints.Count ();
if (length < 2)
{
return 0.0;
}
const double seed = 0.0;
var average = ints.Average ();
var sumOfQuadraticDistance = ints
.Aggregate (
seed,
(state, value) => SumOfQuadraticDistance (average, value, state)
);
return Math.Sqrt (sumOfQuadraticDistance / (length - 1));
}
Lưu ý rằng các hàm này giống hệt nhau ngoại trừ cách tính sumOfQuadraticDistance:
var state = seed;
foreach (var value in ints)
{
state = SumOfQuadraticDistance (average, value, state);
}
var sumOfQuadraticDistance = state;
Đấu với:
var sumOfQuadraticDistance = ints
.Aggregate (
seed,
(state, value) => SumOfQuadraticDistance (average, value, state)
);
Vì vậy, những gì .Aggregate làm là nó gói gọn mẫu tổng hợp này và tôi hy vọng rằng việc triển khai .Aggregate sẽ trông giống như thế này:
public static TAggregate Aggregate<TAggregate, TValue> (
this IEnumerable<TValue> values,
TAggregate seed,
Func<TAggregate, TValue, TAggregate> aggregator
)
{
var state = seed;
foreach (var value in values)
{
state = aggregator (state, value);
}
return state;
}
Sử dụng các hàm độ lệch chuẩn sẽ trông giống như thế này:
var ints = new[] {3, 1, 4, 1, 5, 9, 2, 6, 5, 4};
var average = ints.Average ();
var sampleStandardDeviation = ints.SampleStandardDeviation_Aggregate ();
var sampleStandardDeviation2 = ints.SampleStandardDeviation_ForEach ();
Console.WriteLine (average);
Console.WriteLine (sampleStandardDeviation);
Console.WriteLine (sampleStandardDeviation2);
IMHO
Vì vậy, .Aggregate giúp dễ đọc? Nói chung, tôi yêu LINQ bởi vì tôi nghĩ Tổng hợp phải ở Linq vì lý do đầy đủ nhưng cá nhân tôi không tin lắm .Aggregate thêm khả năng đọc so với một bài thuyết minh được viết tốt.
SampleStandardDeviation_Aggregate()
và SampleStandardDeviation_ForEach()
không thể private
(theo mặc định là không có vòng loại truy cập), do đó, nên đã được tích lũy bởi một trong hai public
hoặc internal
, dường như đối với tôi
Nhắc nhở:
Func<X, Y, R>
là một hàm có hai đầu vào loạiX
vàY
, trả về kết quả của loạiR
.
Có thể đếm được. Đăng ký có ba lần quá tải:
Quá tải 1:
A Aggregate<A>(IEnumerable<A> a, Func<A, A, A> f)
Thí dụ:
new[]{1,2,3,4}.Aggregate((x, y) => x + y); // 10
Quá tải này là đơn giản, nhưng nó có những hạn chế sau:
InvalidOperationException
.Quá tải 2:
B Aggregate<A, B>(IEnumerable<A> a, B bIn, Func<B, A, B> f)
Thí dụ:
var hayStack = new[] {"straw", "needle", "straw", "straw", "needle"};
var nNeedles = hayStack.Aggregate(0, (n, e) => e == "needle" ? n+1 : n); // 2
Quá tải này là tổng quát hơn:
bIn
).Quá tải 3:
C Aggregate<A,B,C>(IEnumerable<A> a, B bIn, Func<B,A,B> f, Func<B,C> f2)
Quá tải thứ ba không phải là IMO rất hữu ích.
Điều tương tự có thể được viết ngắn gọn hơn bằng cách sử dụng quá tải 2 theo sau là một hàm biến đổi kết quả của nó.
Các minh họa được điều chỉnh từ blogpost tuyệt vời này .
Aggegate
trong .net mà phải mất một Func<T, T, T>
.
seed
, áp dụng hàm tích lũy N -1 lần; trong khi các quá tải khác ( thực hiện a seed
) áp dụng hàm tích lũy N lần.
Tổng hợp về cơ bản được sử dụng để Nhóm hoặc Tổng hợp dữ liệu.
Theo MSDN "Hàm tổng hợp Áp dụng hàm tích lũy trên một chuỗi."
Ví dụ 1: Thêm tất cả các số trong một mảng.
int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate((total, nextValue) => total + nextValue);
* quan trọng: Giá trị tổng hợp ban đầu theo mặc định là phần tử 1 trong chuỗi bộ sưu tập. tức là: tổng giá trị ban đầu của biến sẽ là 1 theo mặc định.
giải thích biến
tổng: nó sẽ giữ giá trị tổng (giá trị tổng hợp) được trả về bởi func.
nextValue: nó là giá trị tiếp theo trong chuỗi mảng. Giá trị này được thêm vào giá trị tổng hợp tức là tổng.
Ví dụ 2: Thêm tất cả các mục trong một mảng. Đồng thời đặt giá trị tích lũy ban đầu để bắt đầu thêm từ 10.
int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate(10, (total, nextValue) => total + nextValue);
lập luận giải thích:
đối số đầu tiên là giá trị ban đầu (giá trị khởi đầu tức là giá trị seed) sẽ được sử dụng để bắt đầu bổ sung với giá trị tiếp theo trong mảng.
đối số thứ hai là một func là một func có 2 int.
1.total: giá trị này sẽ giữ nguyên như trước giá trị tổng (giá trị tổng hợp) được trả về bởi func sau khi tính toán.
2.nextValue :: nó là giá trị tiếp theo trong chuỗi mảng. Giá trị này được thêm vào giá trị tổng hợp tức là tổng.
Ngoài ra gỡ lỗi mã này sẽ cho bạn hiểu rõ hơn về cách tổng hợp làm việc.
Đã học được rất nhiều từ câu trả lời của Jamiec .
Nếu nhu cầu duy nhất là tạo chuỗi CSV, bạn có thể thử điều này.
var csv3 = string.Join(",",chars);
Đây là một thử nghiệm với 1 triệu chuỗi
0.28 seconds = Aggregate w/ String Builder
0.30 seconds = String.Join
Mã nguồn ở đây
Ngoài tất cả các câu trả lời tuyệt vời ở đây, tôi cũng đã sử dụng nó để dẫn một mục qua một loạt các bước chuyển đổi.
Nếu một phép biến đổi được triển khai như một Func<T,T>
, bạn có thể thêm một vài phép biến đổi vào a List<Func<T,T>>
và sử dụng Aggregate
để đi qua một thể hiện của T
từng bước.
Bạn muốn nhận một string
giá trị và đưa nó qua một loạt các biến đổi văn bản có thể được xây dựng theo chương trình.
var transformationPipeLine = new List<Func<string, string>>();
transformationPipeLine.Add((input) => input.Trim());
transformationPipeLine.Add((input) => input.Substring(1));
transformationPipeLine.Add((input) => input.Substring(0, input.Length - 1));
transformationPipeLine.Add((input) => input.ToUpper());
var text = " cat ";
var output = transformationPipeLine.Aggregate(text, (input, transform)=> transform(input));
Console.WriteLine(output);
Điều này sẽ tạo ra một chuỗi các phép biến đổi: Xóa các khoảng trắng ở đầu và cuối -> xóa ký tự đầu tiên -> xóa ký tự cuối cùng -> chuyển đổi sang chữ hoa. Các bước trong chuỗi này có thể được thêm, xóa hoặc sắp xếp lại khi cần thiết, để tạo ra bất kỳ loại đường ống chuyển đổi nào được yêu cầu.
Kết quả cuối cùng của đường ống cụ thể này, là " cat "
trở thành "A"
.
Điều này có thể trở nên rất mạnh mẽ một khi bạn nhận ra rằng T
có thể là bất cứ điều gì . Điều này có thể được sử dụng để biến đổi hình ảnh, như bộ lọc, sử dụng BitMap
làm ví dụ;
Định nghĩa
Phương pháp tổng hợp là một phương pháp mở rộng cho các bộ sưu tập chung. Phương pháp tổng hợp áp dụng một chức năng cho từng mục của bộ sưu tập. Không chỉ áp dụng một hàm, mà lấy kết quả của nó làm giá trị ban đầu cho lần lặp tiếp theo. Do đó, kết quả là chúng ta sẽ nhận được một giá trị được tính toán (min, max, avg hoặc giá trị thống kê khác) từ một bộ sưu tập.
Do đó, phương pháp Tổng hợp là một hình thức thực hiện an toàn của hàm đệ quy.
An toàn , vì đệ quy sẽ lặp lại qua từng mục của bộ sưu tập và chúng tôi không thể có bất kỳ đình chỉ vòng lặp vô hạn nào do điều kiện thoát sai. Đệ quy , vì kết quả của hàm hiện tại được sử dụng làm tham số cho lệnh gọi hàm tiếp theo.
Cú pháp:
collection.Aggregate(seed, func, resultSelector);
Làm thế nào nó hoạt động:
var nums = new[]{1, 2};
var result = nums.Aggregate(1, (result, n) => result + n); //result = (1 + 1) + 2 = 4
var result2 = nums.Aggregate(0, (result, n) => result + n, response => (decimal)response/2.0); //result2 = ((0 + 1) + 2)*1.0/2.0 = 3*1.0/2.0 = 3.0/2.0 = 1.5
Cách sử dụng thực tế:
int n = 7;
var numbers = Enumerable.Range(1, n);
var factorial = numbers.Aggregate((result, x) => result * x);
đang làm điều tương tự như chức năng này:
public static int Factorial(int n)
{
if (n < 1) return 1;
return n * Factorial(n - 1);
}
var numbers = new[]{3, 2, 6, 4, 9, 5, 7};
var avg = numbers.Aggregate(0.0, (result, x) => result + x, response => (double)response/(double)numbers.Count());
var min = numbers.Aggregate((result, x) => (result < x)? result: x);
var path = @“c:\path-to-folder”;
string[] txtFiles = Directory.GetFiles(path).Where(f => f.EndsWith(“.txt”)).ToArray<string>();
var output = txtFiles.Select(f => File.ReadAllText(f, Encoding.Default)).Aggregate<string>((result, content) => result + content);
File.WriteAllText(path + “summary.txt”, output, Encoding.Default);
Console.WriteLine(“Text files merged into: {0}”, output); //or other log info
Đây là một lời giải thích về việc sử dụng Aggregate
API thông thạo như Linq Sắp xếp.
var list = new List<Student>();
var sorted = list
.OrderBy(s => s.LastName)
.ThenBy(s => s.FirstName)
.ThenBy(s => s.Age)
.ThenBy(s => s.Grading)
.ThenBy(s => s.TotalCourses);
và hãy xem chúng tôi muốn triển khai một hàm sắp xếp lấy một tập các trường, điều này rất dễ sử dụng Aggregate
thay vì vòng lặp for, như thế này:
public static IOrderedEnumerable<Student> MySort(
this List<Student> list,
params Func<Student, object>[] fields)
{
var firstField = fields.First();
var otherFields = fields.Skip(1);
var init = list.OrderBy(firstField);
return otherFields.Skip(1).Aggregate(init, (resultList, current) => resultList.ThenBy(current));
}
Và chúng ta có thể sử dụng nó như thế này:
var sorted = list.MySort(
s => s.LastName,
s => s.FirstName,
s => s.Age,
s => s.Grading,
s => s.TotalCourses);
Mọi người đã đưa ra lời giải thích của mình. Lời giải thích của tôi là như thế.
Phương pháp tổng hợp áp dụng một chức năng cho từng mục của bộ sưu tập. Ví dụ: hãy có bộ sưu tập {6, 2, 8, 3} và hàm Add (toán tử +) nó thực hiện (((6 + 2) +8) +3) và trả về 19
var numbers = new List<int> { 6, 2, 8, 3 };
int sum = numbers.Aggregate(func: (result, item) => result + item);
// sum: (((6+2)+8)+3) = 19
Trong ví dụ này có phương thức được đặt tên Thêm thay vì biểu thức lambda.
var numbers = new List<int> { 6, 2, 8, 3 };
int sum = numbers.Aggregate(func: Add);
// sum: (((6+2)+8)+3) = 19
private static int Add(int x, int y) { return x + y; }
Một định nghĩa ngắn và thiết yếu có thể là thế này: Phương thức mở rộng Linq Aggregate cho phép khai báo một loại hàm đệ quy được áp dụng trên các phần tử của danh sách, các toán hạng có hai: các phần tử theo thứ tự chúng có trong danh sách, một yếu tố tại một thời điểm và kết quả của lần lặp đệ quy trước đó hoặc không có gì nếu chưa đệ quy.
Theo cách này, bạn có thể tính giai thừa của các số hoặc nối chuỗi.
Tổng hợp được sử dụng để tính tổng các cột trong một mảng số nguyên đa chiều
int[][] nonMagicSquare =
{
new int[] { 3, 1, 7, 8 },
new int[] { 2, 4, 16, 5 },
new int[] { 11, 6, 12, 15 },
new int[] { 9, 13, 10, 14 }
};
IEnumerable<int> rowSums = nonMagicSquare
.Select(row => row.Sum());
IEnumerable<int> colSums = nonMagicSquare
.Aggregate(
(priorSums, currentRow) =>
priorSums.Select((priorSum, index) => priorSum + currentRow[index]).ToArray()
);
Chọn với chỉ mục được sử dụng trong hàm tổng hợp để tổng các cột khớp và trả về một mảng mới; {3 + 2 = 5, 1 + 4 = 5, 7 + 16 = 23, 8 + 5 = 13}.
Console.WriteLine("rowSums: " + string.Join(", ", rowSums)); // rowSums: 19, 27, 44, 46
Console.WriteLine("colSums: " + string.Join(", ", colSums)); // colSums: 25, 24, 45, 42
Nhưng việc đếm số lượng dấu vết trong một mảng Boolean khó khăn hơn vì loại tích lũy (int) khác với loại nguồn (bool); ở đây một hạt giống là cần thiết để sử dụng quá tải thứ hai.
bool[][] booleanTable =
{
new bool[] { true, true, true, false },
new bool[] { false, false, false, true },
new bool[] { true, false, false, true },
new bool[] { true, true, false, false }
};
IEnumerable<int> rowCounts = booleanTable
.Select(row => row.Select(value => value ? 1 : 0).Sum());
IEnumerable<int> seed = new int[booleanTable.First().Length];
IEnumerable<int> colCounts = booleanTable
.Aggregate(seed,
(priorSums, currentRow) =>
priorSums.Select((priorSum, index) => priorSum + (currentRow[index] ? 1 : 0)).ToArray()
);
Console.WriteLine("rowCounts: " + string.Join(", ", rowCounts)); // rowCounts: 3, 1, 2, 2
Console.WriteLine("colCounts: " + string.Join(", ", colCounts)); // colCounts: 3, 2, 1, 2
[1,2,3,4]
sẽ là[3,3,4]
sau đó[6,4]
và cuối cùng[10]
. Nhưng thay vì trả về một mảng của một giá trị, bạn chỉ cần lấy chính giá trị đó.