이것은 절름발이 들릴지 모르지만에 대한 좋은 설명을 찾지 못했습니다 Aggregate
.
좋은 것은 작고 명확한 예와 함께 짧고 설명 적이며 포괄적이라는 것을 의미합니다.
이것은 절름발이 들릴지 모르지만에 대한 좋은 설명을 찾지 못했습니다 Aggregate
.
좋은 것은 작고 명확한 예와 함께 짧고 설명 적이며 포괄적이라는 것을 의미합니다.
답변:
가장 이해하기 쉬운 정의 Aggregate
는 이전에 수행 한 작업을 고려하여 목록의 각 요소에 대해 작업을 수행한다는 것입니다. 즉, 첫 번째 요소와 두 번째 요소에 대해 작업을 수행하고 결과를 전달합니다. 그런 다음 이전 결과와 세 번째 요소에서 작동하여 계속 진행됩니다. 기타
예 1. 숫자 합산
var nums = new[]{1,2,3,4};
var sum = nums.Aggregate( (a,b) => a + b);
Console.WriteLine(sum); // output: 10 (1+2+3+4)
이 추가 1
하고 2
만들 수 3
있습니다. 그런 다음 3
(이전 결과) 및 3
(다음 요소 순서대로)를 추가하여 만듭니다 6
. 그런 다음 6
and 4
을 추가합니다 10
.
예제 2. 문자열 배열에서 csv 만들기
var chars = new []{"a","b","c", "d"};
var csv = chars.Aggregate( (a,b) => a + ',' + b);
Console.WriteLine(csv); // Output a,b,c,d
이것은 거의 같은 방식으로 작동합니다. a
쉼표를 연결 하고 b
만드십시오 a,b
. 그런 다음 a,b
쉼표로 연결하고을 c
만듭니다 a,b,c
. 등등.
예 3. 시드를 사용하여 숫자 곱하기
완전성 들어있다 과부하 의 Aggregate
시드 값을 취한다.
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)
위의 예 5
와 마찬가지로이 값은 값으로 시작 하여 시퀀스의 첫 번째 요소에 곱하여 10
결과를 제공합니다 50
. 이 결과는 다음의 숫자로 이월되어 승산되어 20
결과를 제공합니다 1000
. 이것은 시퀀스의 나머지 2 요소를 통해 계속됩니다.
실제 예 : http://rextester.com/ZXZ64749
문서 : http://msdn.microsoft.com/en-us/library/bb548651.aspx
추가
위의 예 2에서는 문자열 연결을 사용하여 쉼표로 구분 된 값 목록을 만듭니다. 이것은 Aggregate
이 답변의 의도 인 사용법을 설명하는 간단한 방법 입니다. 그러나이 기술을 사용하여 실제로 쉼표로 구분 된 대량의 데이터를 작성하는 경우을 사용하는 것이 더 적합 하며 시드 과부하를 사용하여을 시작하는 것과 StringBuilder
완전히 호환됩니다 .Aggregate
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);
업데이트 된 예 : http://rextester.com/YZCVXV6464
TakeWhile
후 Aggregate
Enumerable에서 확장의 beatuty 그게 전부 - - 그들은 쉽게 체인 가능합니다. 그래서 당신은 결국 TakeWhile(a => a == 'a').Aggregate(....)
. 이 예를 참조하십시오 : rextester.com/WPRA60543
var csv = string.Join(",", chars)
(집계 또는 stringbuilders가 필요 없음). 그렇습니다. 응답의 요점은 집계의 사용 예를 제시하여 멋지다는 것을 알고 있습니다. 그러나 나는 여전히 문자열을 결합하는 것이 권장되지 않는다고 언급하고 싶었습니다. 이미 전용 메소드가 있습니다 ....
var biggestAccount = Accounts.Aggregate((a1, a2) => a1.Amount >= a2.Amount ? a1 : a2);
어떤 오버로드에 대해 부분적으로 의존하지만 기본 아이디어는 다음과 같습니다.
(currentValue, sequenceValue)
으로를(nextValue)
currentValue = nextValue
currentValue
Aggregate
내 Edulinq 시리즈 의 게시물이 유용하다는 것을 알 수 있습니다. 자세한 내용 (다양한 과부하 포함)과 구현이 포함되어 있습니다.
하나의 간단한 예는 다음에 Aggregate
대한 대안으로 사용 하는 것입니다 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);
또는 일련의 문자열에서 모든 문자열 길이를 합산하십시오.
int total = sequence.Aggregate(0, (current, item) => current + item.Length);
개인적으로 나는 거의 찾을 수없는 Aggregate
유용한 -은 "맞춤형"집계 방법은 나를 위해 일반적으로 충분하다.
Super short Aggregate는 Haskell / ML / F #에서 접기처럼 작동합니다.
약간 더 긴 .Max (), .Min (), .Sum (), .Average ()는 모든 요소를 순서대로 반복하고 각 집계 함수를 사용하여 집계합니다. .Aggregate ()는 개발자가 시작 상태 (일명 시드)와 집계 함수를 지정할 수 있다는 점에서 일반화 된 집계 자입니다.
나는 당신이 짧은 설명을 요구했다는 것을 알고 있지만 다른 사람들이 짧은 대답을 몇 개 주었을 때 약간 더 긴 것에 관심이 있다고 생각했습니다.
코드가있는 긴 버전 foreach를 사용하고 .Aggregate를 사용하여 한 번 샘플 표준 편차 를 구현하는 방법을 보여줄 수있는 한 가지 방법이 있습니다 . 참고 : 나는 여기서 성능 우선 순위를 정하지 않았으므로 불필요하게 colleciton을 여러 번 반복합니다.
먼저 이차 거리의 합을 만드는 데 사용되는 도우미 함수 :
static double SumOfQuadraticDistance (double average, int value, double state)
{
var diff = (value - average);
return state + diff * diff;
}
그런 다음 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));
}
그런 다음 .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));
}
sumOfQuadraticDistance가 계산되는 방식을 제외하고 이러한 함수는 동일합니다.
var state = seed;
foreach (var value in ints)
{
state = SumOfQuadraticDistance (average, value, state);
}
var sumOfQuadraticDistance = state;
대:
var sumOfQuadraticDistance = ints
.Aggregate (
seed,
(state, value) => SumOfQuadraticDistance (average, value, state)
);
따라서 .Aggregate 가하는 것은이 집계 자 패턴을 캡슐화하고 .Aggregate의 구현이 다음과 같이 보일 것으로 기대합니다.
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;
}
표준 편차 함수를 사용하면 다음과 같습니다.
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);
이모
그렇다면 .Aggregate는 가독성에 도움이됩니까? 일반적으로 나는 .LINQ를 좋아합니다. 완전성 때문에 집계가 Linq에 있어야하지만 개인적으로 그렇게 확신하지는 않습니다. 집계는 잘 작성된 foreach에 비해 가독성을 추가합니다.
SampleStandardDeviation_Aggregate()
과 SampleStandardDeviation_ForEach()
수 없습니다 private
그래서 중 하나에 의해 발생되어 있어야합니다 (액세스 한정자의 부재에서 기본적으로) public
또는 internal
, 그것은 나에게 보인다
알림 :
Func<X, Y, R>
유형X
및의 입력이 두 개인 함수이며 유형Y
의 결과를 반환합니다R
.
집계 가능 : 집계에는 세 가지 과부하가 있습니다.
과부하 1 :
A Aggregate<A>(IEnumerable<A> a, Func<A, A, A> f)
예:
new[]{1,2,3,4}.Aggregate((x, y) => x + y); // 10
이 과부하는 간단하지만 다음과 같은 제한이 있습니다.
InvalidOperationException
시킵니다.과부하 2 :
B Aggregate<A, B>(IEnumerable<A> a, B bIn, Func<B, A, B> f)
예:
var hayStack = new[] {"straw", "needle", "straw", "straw", "needle"};
var nNeedles = hayStack.Aggregate(0, (n, e) => e == "needle" ? n+1 : n); // 2
이 과부하가 더 일반적입니다.
bIn
).과부하 3 :
C Aggregate<A,B,C>(IEnumerable<A> a, B bIn, Func<B,A,B> f, Func<B,C> f2)
세 번째 과부하는 그다지 유용한 IMO가 아닙니다.
오버로드 2를 사용하고 결과를 변환하는 함수를 사용하여 더 간결하게 작성할 수 있습니다.
그림은 이 훌륭한 블로그 포스트 에서 수정되었습니다 .
Aggegate
.net 에는 과부하가 없습니다 Func<T, T, T>
.
seed
는 누산기 기능을 N -1 번 적용합니다 . (다른 과부하 상태 않는 걸릴는 seed
) 누적 함수를 적용 N 번.
집계는 기본적으로 데이터를 그룹화하거나 요약하는 데 사용됩니다.
MSDN에 따르면 "집계 함수 시퀀스에 누산기 기능을 적용합니다."
예 1 : 배열의 모든 숫자를 추가하십시오.
int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate((total, nextValue) => total + nextValue);
* 중요 : 기본적으로 초기 집계 값은 콜렉션 순서에서 1 요소입니다. 즉, 총 변수 초기 값은 기본적으로 1입니다.
변수 설명
total : func이 반환 한 합계 값 (집계 값)을 보유합니다.
nextValue : 배열 순서에서 다음 값입니다. 이 값은 집계 된 값, 즉 총계에 더해지는 것보다 큽니다.
예 2 : 배열의 모든 항목을 추가하십시오. 또한 10부터 시작하여 초기 누산기 값을 설정하십시오.
int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate(10, (total, nextValue) => total + nextValue);
인수 설명 :
첫 번째 인수는 배열의 다음 값으로 더하기 시작하는 데 사용되는 초기 값 (시작 값, 즉 시드 값)입니다.
두 번째 인수는 2 int를 취하는 func 인 func입니다.
1. 총계 : 계산 후 func에 의해 반환 된 합계 값 (집계 값) 이전과 동일합니다.
2.nextValue : : 배열 순서에서 다음 값입니다. 이 값은 집계 된 값, 즉 총계에 더해지는 것보다 큽니다.
또한이 코드를 디버깅하면 집계가 어떻게 작동하는지 더 잘 이해할 수 있습니다.
Jamiec의 답변 에서 많은 것을 배웠습니다 .
CSV 문자열을 생성하는 것만 필요한 경우 시도해 볼 수 있습니다.
var csv3 = string.Join(",",chars);
백만 개의 문자열을 사용한 테스트입니다.
0.28 seconds = Aggregate w/ String Builder
0.30 seconds = String.Join
소스 코드는 여기
이미 여기에있는 모든 훌륭한 답변 외에도 일련의 변형 단계를 통해 항목을 안내하는 데 사용했습니다.
변환이로 구현 된 경우 Func<T,T>
여러 변환을에 추가 하고 각 단계 의 인스턴스를 진행 하는 데 List<Func<T,T>>
사용할 수 있습니다 .Aggregate
T
당신은 먹고 싶어 string
값을, 그리고 프로그래밍 방식으로 구축 할 수있는 텍스트 변환하는 일련의를 통해 도보.
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);
이렇게하면 일련의 변형이 생성됩니다. 선행 및 후행 공백 제거-> 첫 문자 제거-> 마지막 문자 제거-> 대문자로 변환 이 체인의 단계는 필요에 따라 추가, 제거 또는 재정렬되어 필요한 모든 종류의 변환 파이프 라인을 생성 할 수 있습니다.
이 특정 파이프 라인의 최종 결과는, 즉 " cat "
이된다 "A"
.
당신 T
이 무엇이든 할 수 있다는 것을 깨닫게되면 이것은 매우 강력해질 수 있습니다 . BitMap
예를 들어 필터와 같은 이미지 변환에 사용할 수 있습니다 .
정의
집계 방법은 일반 컬렉션의 확장 방법입니다. 집계 방법은 함수를 컬렉션의 각 항목에 적용합니다. 함수를 적용 할뿐만 아니라 결과를 다음 반복의 초기 값으로 사용합니다. 결과적으로 컬렉션에서 계산 된 값 (최소, 최대, 평균 또는 기타 통계 값)을 얻습니다.
따라서 집계 방법은 재귀 함수의 안전한 구현 형식입니다.
재귀는 컬렉션의 각 항목을 반복하고 잘못된 종료 조건으로 인해 무한 루프 일시 중단을 얻을 수 없기 때문에 안전 합니다. 현재 함수의 결과가 다음 함수 호출의 매개 변수로 사용되기 때문에 재귀 .
통사론:
collection.Aggregate(seed, func, resultSelector);
작동 방식 :
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
실제 사용법 :
int n = 7;
var numbers = Enumerable.Range(1, n);
var factorial = numbers.Aggregate((result, x) => result * x);
이 기능과 동일한 작업을 수행합니다.
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
Aggregate
Linq Sorting과 같은 Fluent API 사용 에 대한 설명 입니다.
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);
필드 집합을 취하는 정렬 함수를 구현하고자한다면 Aggregate
다음과 같이 for-loop 대신에 사용 하는 것이 매우 쉽습니다 .
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));
}
그리고 우리는 이것을 다음과 같이 사용할 수 있습니다 :
var sorted = list.MySort(
s => s.LastName,
s => s.FirstName,
s => s.Age,
s => s.Grading,
s => s.TotalCourses);
모두가 그의 설명을했습니다. 내 설명은 그와 같습니다.
집계 방법은 함수를 컬렉션의 각 항목에 적용합니다. 예를 들어 {6, 2, 8, 3} 컬렉션과 Add (operator +) 함수를 수행하고 (((6 + 2) +8) +3) 함수를 반환하고 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
이 예제에서는 람다 식 대신 Add라는 메서드가 전달되었습니다.
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; }
다차원 정수 배열에서 열을 합산하는 데 사용되는 집계
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()
);
선택 기능은 집계 기능 내에서 일치하는 열을 합산하고 새 배열을 반환하는 데 사용됩니다. {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
그러나 Boolean 배열에서 true 수를 계산하는 것은 누적 유형 (int)이 소스 유형 (bool)과 다르기 때문에 더 어렵습니다. 여기서 두 번째 과부하를 사용하려면 시드가 필요합니다.
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]
할 것이다[3,3,4]
다음[6,4]
마지막에[10]
. 그러나 단일 값의 배열을 반환하는 대신 값 자체를 얻습니다.