foreach를 통해 뒤로 반복 할 수 있습니까?


129

for문을 사용 하고 동일한 효과를 얻을 수 있다는 것을 알고 있지만 foreachC # 의 루프를 통해 뒤로 루프 할 수 있습니까?


6
각각에 대해 수행하기 전에 목록에서 요소 (list.Reverse ())를 되돌릴 수 있습니다.
Akanthan December


열거의 모든 요소를 ​​인스턴스화하여 순서를 반대로 할 수 있습니다. 가능하지 않을 수 있습니다. 순서를 고려하십시오. IEnumerable<int> Infinity() { int i = 1; while (true) yield return i++; }어떻게 되돌릴까요?
Suncat2000

답변:


84

목록 작업 (직접 인덱싱)시 for루프 를 사용하는 것만 큼 효율적으로 수행 할 수 없습니다 .

편집 : 일반적으로 루프 를 사용할 수있을for 때이 작업에 대한 올바른 방법 일 수 있습니다. 또한, foreach순서대로 구현되는 한, 구조 자체는 요소 인덱스 및 반복 순서와 무관 한 루프를 표현하기 위해 빌드되며, 이는 병렬 프로그래밍 에서 특히 중요 합니다 . 이다 내 의견으로 반복 사용하지 말아야 순서에 의존하는 것을 foreach반복합니다.


3
나는 당신의 마지막 진술이 너무 일반적이라고 생각합니다. 예를 들어 어떤 종류의 IEnumerable을 순서대로 반복 해야하는 경우가 있습니까? 이 경우 foreach를 사용하지 않겠습니까? 무엇을 사용 하시겠습니까?
avl_sweden

1
업데이트 : Linq를 사용하고 목록과 다른 열거 형에 대해 논의하는보다 현대적인 답변에 대해서는 [비슷한 질문에 대한 Brian의 답변 [( stackoverflow.com/a/3320924/199364 ) 을 참조하십시오 .
ToolmakerSteve

업데이트 : Jon Skeet 은 훨씬 더 우아한 답변을 얻었습니다 . 이제 " yield return"를 사용하면 가능합니다 .
ToolmakerSteve

2
나는 @avl_sweden과 같은 초기 반응을 보였습니다. "순서에 의존하는 반복은 foreach를 사용해서는 안됩니다"라는 소리가 너무 큽니다. 예, foreach는 순서 독립적 인 병렬 작업을 표현하는 데 좋습니다. 그러나 그것은 현대의 반복자 기반 프로그래밍 의 필수 부분이기도합니다 . 아마도 요점은 두 개의 별개의 키워드 가 있다면 반복이 순서와 무관하다는 주장 을 분명히하는 것이 더 명확하고 안전하다는 것입니다 . [어설 션과 같은 계약을 전파 할 수있는 Eiffel과 같은 언어는 주어진 코드에 대해 사실이거나 거짓 일 수 있습니다.]
ToolmakerSteve

2
foreach "요소 인덱스 및 반복 순서와 독립적 인 루프를 표현하기위한 것"이라는 생각은 틀립니다. c # 언어 사양에는 각 프로세스 요소가 순서대로 필요합니다. 반복자에서 MoveNext를 사용하거나 0에서 시작하여 배열의 각 반복에서 1 씩 증가하는 인덱스를 처리합니다.
Donald Rich

145

.NET 3.5를 사용하는 경우 다음을 수행 할 수 있습니다.

IEnumerable<int> enumerableThing = ...;
foreach (var x in enumerableThing.Reverse())

기본적으로 열거자를 통해 모든 것을 스택에 넣은 다음 모든 것을 역순으로 팝해야하므로 매우 효율적이지 않습니다.

직접 색인 가능한 컬렉션 (예 : IList)이있는 경우 반드시 for 루프를 .

.NET 2.0에 있고 for 루프를 사용할 수없는 경우 (즉, IEnumerable 만있는 경우) 자신 만의 Reverse 함수를 작성하면됩니다. 이것은 작동해야합니다 :

static IEnumerable<T> Reverse<T>(IEnumerable<T> input)
{
    return new Stack<T>(input);
}

이것은 아마도 그다지 명백하지 않은 행동에 의존합니다. IEnumerable을 스택 생성자에 전달하면 해당 항목을 반복하고 항목을 스택으로 푸시합니다. 그런 다음 스택을 반복하면 물건이 역순으로 튀어 나옵니다.

이 항목과 .NET 3.5 Reverse()확장 방법은 항목 반환을 멈추지 않는 IEnumerable을 공급하면 분명히 폭발 할 것입니다.


또한 나는 그물 V2에만하시기 바랍니다 언급하는 것을 잊었다
JL합니다.

4
흥미로운 .NET 2.0 솔루션.
RichardOD

11
누락되었거나 .Net 3.5 솔루션이 실제로 작동하지 않습니까? Reverse ()는 목록을 제자리로 되돌리고 반환하지 않습니다. 그런 솔루션을 기대하면서 너무 나빴습니다.
user12861

2
일부 관리자는이 답변을 잘못된 것으로 표시하십시오. Reverse ()는 void [1]이므로 위의 예에서는 컴파일 오류가 발생합니다. [1] msdn.microsoft.com/en-us/library/b0axc2h2(v=vs.110).aspx
MickyD

6
@ user12861, Micky Duncan : 답변이 잘못되지 않았습니다. System.Collections.Generic.List <T>에는 역 위치를 수행하는 Reverse 메서드가 있습니다. .Net 3.5에는 IEnumerable <T>에 Reverse라는 확장 방법이 있습니다. 더 명확하게하기 위해 예제를 var에서 IEnumerable <int>로 변경했습니다.
Matt Howells

55

280Z28에서 IList<T>알 수 있듯이 인덱스를 사용할 수 있습니다. 확장 방법으로 이것을 숨길 수 있습니다.

public static IEnumerable<T> FastReverse<T>(this IList<T> items)
{
    for (int i = items.Count-1; i >= 0; i--)
    {
        yield return items[i];
    }
}

이것은 Enumerable.Reverse()모든 데이터를 먼저 버퍼링하는 것보다 빠릅니다 . ( Reverse이러한 방식으로 최적화가 적용 되지 않았다고 생각 합니다 Count().)이 버퍼링은 처음 반복을 시작할 때 데이터를 완전히 읽은 것을 의미하지만 FastReverse반복하는 동안 목록의 변경 사항을 "볼"것입니다. (반복 사이에 여러 항목을 제거하면 손상됩니다.)

일반적인 시퀀스의 경우 역순으로 반복하는 방법이 없습니다. 예를 들어 시퀀스는 무한 할 수 있습니다.

public static IEnumerable<T> GetStringsOfIncreasingSize()
{
    string ret = "";
    while (true)
    {
        yield return ret;
        ret = ret + "x";
    }
}

반대로 반복하려고하면 어떻게 될까요?


호기심, 왜 "> -1"대신 "> = 0"을 사용합니까?
Chris S

15
> "> -1"대신 "> = 0"을 사용하는 이유는 무엇입니까? > = 0은 코드를 읽는 사람에게 의도를 더 잘 전달하기 때문입니다. 컴파일러는 성능을 향상시킬 수 있으면> -1에 해당하는 값으로 최적화 할 수 있어야합니다.
Mark Maslar

1
FastReverse (이 IList <T> 항목)는 FastReverse <T> (this IList <T> 항목이어야합니다. :)
Rob

스틱 0 <= i를 나누는 것이 훨씬 좋습니다. 수년에 걸쳐 많은 수의 어린이 수학을 가르치고 코딩하는 사람들을 도운 후, 사람들이 항상 a> b를 b <a로 바꾸면 자연스럽게 정렬되는 경우 오류가 크게 줄어 듭니다. 숫자 (또는 좌표계의 X 축)-유일한 단점은 방정식을 XML에 붙여 넣는 경우입니다. .config
Eske Rahn

13

foreach반복에 사용하기 전에 다음 reverse방법으로 목록을 반대로하십시오 .

    myList.Reverse();
    foreach( List listItem in myList)
    {
       Console.WriteLine(listItem);
    }


무엇이 myList도움이 될지 주의해야 할 단어 . IEnumerable.Reverse는 작동하지 않습니다.
nawfal

2
@ MA-Maddin 두 가지가 다릅니다. 응답자가 List <T>에 의존하고 있다고 생각합니다.
nawfal

사실, 내가 왜 그런 말을했는지 모르겠습니다. 자세한 내용을 추가해야합니다 ... 어쨌든 이것은 혼동되는 코드이므로 myList유형 System.Collections.Generic.List<System.Windows.Documents.List>(또는 다른 사용자 정의 List유형) 인 것 같습니다. 그렇지 않으면이 코드가 작동하지 않습니다. P
Martin Schneider

5

때로는 고급 색인 생성 기능이 없거나 Linq 쿼리 결과를 반대로 바꾸거나 소스 컬렉션을 수정하고 싶지 않은 경우 Linq가 도움이 될 수 있습니다.

Linq에 익명 형식을 사용하는 Linq 확장 방법 Linq OrderByDescending에 대한 정렬 키를 제공하려면 선택하십시오.

    public static IEnumerable<T> Invert<T>(this IEnumerable<T> source)
    {
        var transform = source.Select(
            (o, i) => new
            {
                Index = i,
                Object = o
            });

        return transform.OrderByDescending(o => o.Index)
                        .Select(o => o.Object);
    }

용법:

    var eable = new[]{ "a", "b", "c" };

    foreach(var o in eable.Invert())
    {
        Console.WriteLine(o);
    }

    // "c", "b", "a"

"Reverse"와 동의어이고 List Reverse 구현과 명확성을 가지기 때문에 "Invert"로 명명됩니다.

Int32.MinValue 및 Int32.MaxValue가 모든 종류의 컬렉션 인덱스 범위를 벗어나므로 컬렉션의 특정 범위를 되돌릴 수도 있습니다.이를 주문 프로세스에 활용할 수 있습니다. 요소 인덱스가 지정된 범위 아래에있는 경우 OrderByDescending을 사용할 때 순서가 변경되지 않도록 Int32.MaxValue가 지정됩니다. 마찬가지로 지정된 범위보다 큰 인덱스의 요소에 Int32.MinValue가 지정됩니다. 주문 프로세스의 끝에 나타납니다. 지정된 범위 내의 모든 요소에는 일반 인덱스가 할당되고 그에 따라 반대로됩니다.

    public static IEnumerable<T> Invert<T>(this IEnumerable<T> source, int index, int count)
    {
        var transform = source.Select(
            (o, i) => new
            {
                Index = i < index ? Int32.MaxValue : i >= index + count ? Int32.MinValue : i,
                Object = o
            });

        return transform.OrderByDescending(o => o.Index)
                        .Select(o => o.Object);
    }

용법:

    var eable = new[]{ "a", "b", "c", "d" };

    foreach(var o in eable.Invert(1, 2))
    {
        Console.WriteLine(o);
    }

    // "a", "c", "b", "d"

Linq 구현의 성능 적중과 임시 List를 사용하여 역전을 위해 컬렉션을 래핑하는 것에 대해서는 확신하지 않습니다.


글을 쓰는 시점에서 Linq의 Reverse 구현을 알지 못했지만 여전히이 작업을 수행하는 것이 재미있었습니다. https://msdn.microsoft.com/en-us/library/vstudio/bb358497(v=vs.100).aspx


4

List <T>를 사용하는 경우이 코드를 사용할 수도 있습니다.

List<string> list = new List<string>();
list.Add("1");
list.Add("2");
list.Add("3");
list.Reverse();

목록 자체를 반대로 쓰는 방법입니다.

이제 foreach :

foreach(string s in list)
{
    Console.WriteLine(s);
}

출력은 다음과 같습니다.

3
2
1

3

그것은이다 당신이 컬렉션 코드를 변경할 수있는 경우에 가능 구현을 IEnumerable 또는 IEnumerable을가 (IList의 당신 자신의 구현을 예) 있음을.

예를 들어 IEnumerable 인터페이스를 통한 다음 구현과 같이이 작업을 수행 하는 반복자를 작성하십시오 ( 'items'가이 샘플의 목록 필드라고 가정).

public IEnumerator<TObject> GetEnumerator()
{
    for (var i = items.Count - 1; i >= 0; i--)
    { 
        yield return items[i];
    }
}

IEnumerator IEnumerable.GetEnumerator()
{
    return GetEnumerator();
}

이 때문에 목록은 목록을 거꾸로 반복합니다.

힌트 : 당신은 문서 내에서 목록 의이 특별한 행동을 명확하게 진술해야합니다 (스택 또는 대기열과 같은 자체 설명 클래스 이름을 선택하는 것이 더 좋습니다).


2

아니요. ForEach는 각 항목의 컬렉션을 반복하며 순서는 IEnumerable 또는 GetEnumerator ()를 사용하는지 여부에 따라 다릅니다.


1
그런데이 있는 컬렉션의 종류에 따라 질서의 보장.
Jon Skeet

1

Jon Skeet 의 좋은 답변에 대해 약간의 설명 이 있으면 다목적 일 수 있습니다.

public static IEnumerable<T> Directional<T>(this IList<T> items, bool Forwards) {
    if (Forwards) foreach (T item in items) yield return item;
    else for (int i = items.Count-1; 0<=i; i--) yield return items[i];
}

그런 다음

foreach (var item in myList.Directional(forwardsCondition)) {
    .
    .
}

-1

나는이 코드를 사용했다.

                if (element.HasAttributes) {

                    foreach(var attr in element.Attributes().Reverse())
                    {

                        if (depth > 1)
                        {
                            elements_upper_hierarchy_text = "";
                            foreach (var ancest  in element.Ancestors().Reverse())
                            {
                                elements_upper_hierarchy_text += ancest.Name + "_";
                            }// foreach(var ancest  in element.Ancestors())

                        }//if (depth > 1)
                        xml_taglist_report += " " + depth  + " " + elements_upper_hierarchy_text+ element.Name + "_" + attr.Name +"(" + attr.Name +")" + "   =   " + attr.Value + "\r\n";
                    }// foreach(var attr in element.Attributes().Reverse())

                }// if (element.HasAttributes) {

2
.Reverse()실제로 목록을 수정 하기 때문에 이것은 나쁩니다 .
콜린 바넷

-8

이것은 꽤 잘 작동합니다

List<string> list = new List<string>();

list.Add("Hello");
list.Add("Who");
list.Add("Are");
list.Add("You");

foreach (String s in list)
{
    Console.WriteLine(list[list.Count - list.IndexOf(s) - 1]);
}

9
이것은 나에게 비효율적으로 들린다.
Jean Azzopardi
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.