C / C # / C ++에서 역방향 루프를 수행하는 가장 좋은 방법은 무엇입니까?


101

배열을 통해 뒤로 이동해야하므로 다음과 같은 코드가 있습니다.

for (int i = myArray.Length - 1; i >= 0; i--)
{
    // Do something
    myArray[i] = 42;
}

이 작업을 수행하는 더 좋은 방법이 있습니까?

업데이트 : C #에 다음과 같은 내장 메커니즘이 있기를 바라고 있습니다.

foreachbackwards (int i in myArray)
{
    // so easy
}

업데이트 2 :이 있습니다 더 나은 방법이. 룬은 다음과 함께 상을받습니다 :

for (int i = myArray.Length; i-- > 0; )
{    
    //do something
}
//or
for (int i = myArray.Length; i --> 0; )
{
    // do something
}

일반 C에서 더 좋아 보입니다 (Twotymz 덕분에).

for (int i = lengthOfArray; i--; )
{    
    //do something
}

선에 명확성이나 유지 관리가 포함되어 있다면 대안이 더 좋은 이유를 알 수 없습니다.
dkretz

진실. 나는 이것을 꽤 자주해야하기 때문에 이것을하기위한 더 합리적인 방법을 찾고 싶었다.
MusiGenesis

3
이 질문의 대부분은 아래 답변에 대한 일종의 검토입니다. 상당히 짧게 제안하십시오.
einpoklum 2013

제목과 해시 태그에서 C 및 C ++를 제거하십시오.
jaskmar

답변:


149

분명히 약간 모호하지만 인쇄 상으로 가장 즐거운 방법은 다음과 같습니다.

for (int i = myArray.Length; i --> 0; )
{
    //do something
}

32
내가 처음 당신의 대답을 읽었을 때 그것이 컴파일조차하지 않을 것 같았 기 때문에 나는 당신이 미친 사람이라고 생각했습니다. 그러나 그것은 정확히 내가 찾던 것입니다 : 역방향 for 루프를 작성하는 더 좋은 방법입니다.
MusiGenesis

3
나는 내가-> 0; 일부러 있습니다. 이것이 바로 그가 "전형적으로 즐겁다"라는 의미입니다
Johannes Schaub-litb

16
나는 그것이 "전형적으로 혼란 스럽다"는 것을 발견했다. 저에게 "-"는 영향을 미치는 변수에 인접하지 않는 한 올바르게 보이지 않습니다.
MusiGenesis

26
너무 모호하고 모호합니다. 나는 ... 생산 코드에이 같은 결코 쓰기 뭔가를 거라고
미하이 Todor에게

9
Aah go to 연산자 (->) 가 트릭을 수행합니다!
nawfal 2014 년

118

C ++에서는 기본적으로 반복자 또는 인덱스를 사용하여 반복 할 수 있습니다. 일반 배열 std::vector을 사용 하는지 또는 을 사용 하는지에 따라 다른 기술을 사용합니다.

std :: vector 사용

반복기 사용

C ++를 사용하면 다음을 사용할 수 있습니다. std::reverse_iterator:

for(std::vector<T>::reverse_iterator it = v.rbegin(); it != v.rend(); ++it) {
    /* std::cout << *it; ... */
}

인덱스 사용

에서 반환 된 부호없는 정수 유형 std::vector<T>::size이 항상 은 아닙니다std::size_t . 더 크거나 작을 수 있습니다. 이것은 루프가 작동하는 데 중요합니다.

for(std::vector<int>::size_type i = someVector.size() - 1; 
    i != (std::vector<int>::size_type) -1; i--) {
    /* std::cout << someVector[i]; ... */
}

부호없는 정수 유형 값은 모듈로 비트 수를 통해 정의되기 때문에 작동합니다. 따라서을 설정 -N하면(2 ^ BIT_SIZE) -N

배열 사용

반복기 사용

우리는 std::reverse_iterator반복을 위해 사용 하고 있습니다.

for(std::reverse_iterator<element_type*> it(a + sizeof a / sizeof *a), itb(a); 
    it != itb; 
    ++it) {
    /* std::cout << *it; .... */
}

인덱스 사용

항상 정의에 따라 반환 std::size_t되기 때문에 위와 반대로 여기서 안전하게 사용할 수 있습니다 .sizeofstd::size_t

for(std::size_t i = (sizeof a / sizeof *a) - 1; i != (std::size_t) -1; i--) {
   /* std::cout << a[i]; ... */
}

포인터에 sizeof를 적용하여 함정 피하기

실제로 배열의 크기를 결정하는 위의 방법은 짜증납니다. a가 실제로 배열 대신 포인터 인 경우 (매우 자주 발생하며 초보자가 혼동 할 것임) 조용히 실패합니다. 더 좋은 방법은 다음을 사용하는 것입니다. 포인터가 주어지면 컴파일 타임에 실패합니다.

template<typename T, std::size_t N> char (& array_size(T(&)[N]) )[N];

먼저 전달 된 배열의 크기를 가져온 다음 동일한 크기의 char 유형 배열에 대한 참조를 반환하도록 선언하여 작동합니다. char이 정의된다 sizeof반환 된 배열이있을 것이다 그래서 1 :의 sizeof우리는 컴파일 타임 평가 제로 런타임 오버 헤드, 무엇을 찾고있다 N * 1 :의를.

하는 대신

(sizeof a / sizeof *a)

이제 코드를 변경하십시오.

(sizeof array_size(a))

또한 컨테이너에 역 반복
기가없는

귀하의 array_size는 정적으로 할당 된 배열에서 작동하는 것 같지만 예를 들어 a가 'new int [7]'일 때 실패했습니다.
Nate Parsons

2
네, 그것이 의도입니다. :) new int [7]은 포인터를 반환합니다. 따라서 sizeof (new int [7])은 7 * sizeof (int)가 아니라 sizeof (int *)를 반환합니다. array_size는 자동으로 작동하지 않고 해당 경우 컴파일 오류를 발생시킵니다.
Johannes Schaub-litb 2008-12-29

+ 연산자가 배열을 첫 번째 요소에 대한 포인터로 만들기 때문에 또한 실패하는 array_size (+ statically_allocated_array)를 시도하십시오. plain sizeof를 사용하면 포인터의 크기를 다시 얻을 수 있습니다.
Johannes Schaub-litb 2008-12-29

첫 번째로-그냥 사용 end하고 begin역순으로 사용하지 않는 이유는 무엇입니까?
Tomáš Zato-Monica 복원

54

C # 에서는 Visual Studio 2005 이상을 사용하여 'forr'를 입력하고 [TAB] [TAB]을 누릅니다 . for컬렉션을 거꾸로 진행 하는 루프로 확장됩니다 .

(적어도 저에게는) 오해하기가 너무 쉽기 때문에이 스 니펫을 넣는 것이 좋은 생각이라고 생각했습니다.

즉, 나는 Array.Reverse()/ 좋아 Enumerable.Reverse()하고 앞으로 더 잘 반복합니다 -그들은 더 명확하게 의도를 나타냅니다.


41

나는 항상 ' 인쇄 상으로 유쾌한 '코드보다 명확한 코드를 선호 합니다. 따라서 항상 다음을 사용합니다.

for (int i = myArray.Length - 1; i >= 0; i--)  
{  
    // Do something ...  
}    

거꾸로 반복하는 표준 방법으로 간주 할 수 있습니다.
내 2 센트 만 ...


일했다. 아직 C #에서이 작업을 수행하지 않았지만 감사합니다.
PCPGMR

그렇다면 배열이 정말 큰 경우 (인덱스가 부호없는 유형이어야 함) 어떻게해야합니까? size_t는 실제로 서명되지 않았습니까?
lalala '

i를 uint로 선언 할 수 있습니다. Marc Gravell의 게시물을 참조하세요.
Jack Griffin

인쇄 상 만족스러운 유형과는 달리, 포장으로 인해 서명되지 않은 유형에는 작동하지 않습니다. 안좋다.
Ocelot

17

에서 C #을 사용하여 Linq에를 :

foreach(var item in myArray.Reverse())
{
    // do something
}

이것은 확실히 가장 간단하지만 현재 버전의 CLR에서 목록 반전은 항상 O (1) 작업이 아니라 O (n) 작업이며 IList <T> 일 수 있습니다. connect.microsoft.com/VisualStudio/feedback/…
Greg Beech

1
.Reverse ()는 배열의 로컬 복사본을 만든 다음 역방향으로 반복합니다. 반복 할 수 있도록 배열을 복사하는 것은 비효율적 인 것 같습니다. "Linq"가 포함 된 게시물은 투표 할 가치가 있다고 생각합니다. :)
MusiGenesis

트레이드 오프가있을 수 있지만, "voted on answer"(i-> 0)로 인해 코드가 느려질 수 있다는 문제가 발생합니다. (i) 매번 배열 크기에 대해 확인해야하기 때문입니다. 색인에서와 같이 사용됩니다.
Keltex

1
Linq가 훌륭하다는 데 동의하지만이 접근 방식의 문제점은 반복기가 배열의 항목을 선택적으로 선택할 수 없다는 것입니다. 배열의 일부를 탐색하고 싶었지만 전체가 아닌 상황을 고려하십시오. 일 ...
jesses.co.tt

11

길이가 부호있는 정수 유형 인 배열에 대해 확실히 가장 좋은 방법입니다. 길이가 부호없는 정수 유형 (예 : std::vectorC ++) 인 배열의 경우 종료 조건을 약간 수정해야합니다.

for(size_t i = myArray.size() - 1; i != (size_t)-1; i--)
    // blah

방금 말한 경우 i >= 0부호없는 정수의 경우 항상 참이므로 루프는 무한 루프가됩니다.


C ++에서 "i--"는 내가 0이면 자동으로 가장 높은 값으로 래핑됩니다. 죄송합니다. 저는 C ++ 장애입니다.
MusiGenesis

놀랄 만한. 여러분은 제가 12 년 전에 작성한 내용에서 하드 드라이브 채우기 버그의 원인을 이해하도록 도와주었습니다. C ++에서 작동하지 않는 다행입니다.
MusiGenesis

종료 조건은 i <myArray.size () 여야합니다. unsigned int의 오버플로 속성에만 의존합니다. 정수 및 캐스트의 구현 세부 사항이 아닙니다. 결과적으로 읽기 쉽고 대체 정수 표현이있는 언어로 작동합니다.
ejgottl

더 나은 해결책은 아무도 해칠 수없는 C # 세계에 머무르는 것입니다.
MusiGenesis

4

나에게 좋아 보인다. 인덱서가 서명되지 않은 경우 (uint 등)이를 고려해야 할 수 있습니다. lazy라고 부르지 만 (서명되지 않은) 경우에는 카운터 변수를 사용할 수 있습니다.

uint pos = arr.Length;
for(uint i = 0; i < arr.Length ; i++)
{
    arr[--pos] = 42;
}

(실제로 여기에서도 arr.Length = uint.MaxValue ... 어딘가에! = 어딘가에있는 경우와 같은 경우에주의해야합니다 ... 물론 가능성이 매우 낮은 경우입니다!)


하지만 "--pos"는 까다 롭습니다. 나는 과거에 그들에게 옳지 않은 코드를 무작위로 "수정"하는 것을 좋아했던 동료들이 있었다.
MusiGenesis

1
그런 다음 .arr.Length-1 및 pos를 사용합니다.
Marc Gravell

4

CI에서는 다음과 같이합니다.


int i = myArray.Length;
while (i--) {
  myArray[i] = 42;
}

MusiGenesis에서 추가 한 C # 예제 :

{int i = myArray.Length; while (i-- > 0)
{
    myArray[i] = 42;
}}

나는 이것을 좋아한다. 당신의 방법이 효과가 있다는 것을 깨닫는 데 1 ~ 2 초가 걸렸습니다. 추가 된 C # 버전에 신경 쓰지 않기를 바랍니다 (추가 중괄호는 인덱스가 for 루프에서와 동일하게 범위가 지정되도록합니다).
MusiGenesis

나는 이것이 보편적 인 "WTF is this?"를 이끌어 낼 것이기 때문에 프로덕션 코드에서 이것을 사용할 것인지 확신하지 못한다. 유지해야하는 사람의 반응이지만 실제로는 일반 for 루프보다 입력하기가 더 쉽고 빼기 기호 나> = 기호가 없습니다.
MusiGenesis

1
안타깝게도 C # 버전에는 추가 "> 0"이 필요합니다.
MusiGenesis

나는 그것이 어떤 언어인지 잘 모르겠습니다. 그러나 첫 번째 코드 스 니펫은 확실히 C가 아닙니다. 어쩌면 당신은 C #을 작성하고 싶었고 html이 그것을 파싱하지 못했습니까?
Johannes Schaub-litb

@litb : "myArray.Length"가 유효한 C (?)가 아니라고 생각하지만 while 루프 부분은 작동하고 멋지게 보입니다.
MusiGenesis

3

C ++에서이를 수행하는 가장 좋은 방법은 순회되는 시퀀스를 느리게 변환하는 반복기 (또는 더 나은 범위) 어댑터를 사용하는 것입니다.

원래,

vector<value_type> range;
foreach(value_type v, range | reversed)
    cout << v;

범위 "범위"(여기서는 비어 있지만 요소를 직접 추가 할 수 있다고 확신합니다)를 역순으로 표시합니다. 물론 단순히 범위를 반복하는 것은별로 소용이 없지만 새로운 범위를 알고리즘과 물건에 전달하는 것은 꽤 멋지다.

이 메커니즘은 훨씬 더 강력한 용도로도 사용할 수 있습니다.

range | transformed(f) | filtered(p) | reversed

함수 "f"가 모든 요소에 적용되고 "p"가 참이 아닌 요소는 제거되고 최종적으로 결과 범위가 반전되는 "범위"범위를 느리게 계산합니다.

파이프 구문은 중위를 감안할 때 가장 읽기 쉬운 IMO입니다. Boost.Range 라이브러리 업데이트 보류 검토가이를 구현하지만 직접 수행하는 것도 매우 간단합니다. 함수 f와 술어 p를 인라인으로 생성하는 람다 DSEL을 사용하면 훨씬 더 멋집니다.


"foreach"가 어디에서 왔는지 말씀해 주시겠습니까? BOOST_FOREACH에 대한 #define입니까? 끝까지 귀여워 보인다.
Johannes Schaub-litb


1

while 루프를 선호합니다. ifor 루프의 조건에서 감소하는 것보다 더 분명합니다.

int i = arrayLength;
while(i)
{
    i--;
    //do something with array[i]
}

0

원래 질문에서 코드를 사용하고 싶지만 실제로 foreach를 사용하고 C #에서 정수 인덱스를 사용하려면 다음을 수행하십시오.

foreach (int i in Enumerable.Range(0, myArray.Length).Reverse())
{
    myArray[i] = 42; 
}

-1

여기에서 내 질문에 답해 보려고하지만 이것도별로 마음에 들지 않습니다.

for (int i = 0; i < myArray.Length; i++)
{
    int iBackwards = myArray.Length - 1 - i; // ugh
    myArray[iBackwards] = 666;
}

.Length-1-i를 매번 수행하는 대신 두 번째 변수를 고려할까요? 내 [업데이트 된] 게시물을 참조하십시오.
Marc Gravell

내 질문에 반대표를 던졌습니다. 거친. 원본보다 어색하지 않습니다.
MusiGenesis

-4

참고 :이 게시물은 훨씬 더 자세하게 작성되어 주제에서 벗어났습니다. 사과드립니다.

내 동료들은 그것을 읽고 그것이 '어딘가'에 가치 있다고 믿습니다. 이 실은 장소가 아닙니다. 이것이 어디로 가야하는지에 대한 귀하의 의견에 감사드립니다 (저는 사이트를 처음 사용합니다).


어쨌든 이것은 정의 된 의미 체계를 사용하는 모든 컬렉션 유형에서 작동한다는 점에서 놀라운 .NET 3.5의 C # 버전입니다. 이것은 대부분의 일반적인 개발 시나리오에서 성능이나 CPU주기 최소화가 아닌 기본 측정 (재사용!)이지만 실제 환경에서 발생하는 것처럼 보이지는 않습니다 (조기 최적화).

*** 모든 컬렉션 유형에 대해 작동하고 유형의 단일 값을 예상하는 작업 대리자를 취하는 확장 메서드, 모두 역순으로 각 항목에 대해 실행 **

요구 사항 3.5 :

public static void PerformOverReversed<T>(this IEnumerable<T> sequenceToReverse, Action<T> doForEachReversed)
      {
          foreach (var contextItem in sequenceToReverse.Reverse())
              doForEachReversed(contextItem);
      }

이전 .NET 버전 또는 Linq 내부를 더 잘 이해하고 싶습니까? 계속 읽어봐 .. 아니면 ..

가정 : .NET 유형 시스템에서 Array 유형은 IEnumerable 인터페이스에서 상속됩니다 (일반 IEnumerable 전용 IEnumerable이 아님).

이것은 처음부터 끝까지 반복하는 데 필요한 전부이지만 반대 방향으로 이동하고 싶습니다. IEnumerable이 '객체'유형의 배열에서 작동하므로 모든 유형이 유효합니다.

중요 측정 : '더 나은'시퀀스를 역순으로 처리 할 수 ​​있다면 정수에서만 처리 할 수 ​​있다고 가정합니다.

.NET CLR 2.0-3.0 용 솔루션 A :

설명 : 포함 된 각 인스턴스가 동일한 유형임을 요구하는 모든 IEnumerable 구현 인스턴스를 허용합니다. 따라서 배열을 받으면 전체 배열에 X 유형의 인스턴스가 포함됩니다. 다른 인스턴스가! = X 유형이면 예외가 발생합니다.

싱글 톤 서비스 :

공용 클래스 ReverserService {개인 ReverserService () {}

    /// <summary>
    /// Most importantly uses yield command for efficiency
    /// </summary>
    /// <param name="enumerableInstance"></param>
    /// <returns></returns>
    public static IEnumerable ToReveresed(IEnumerable enumerableInstance)
    {
        if (enumerableInstance == null)
        {
            throw new ArgumentNullException("enumerableInstance");
        }

        // First we need to move forwarad and create a temp
        // copy of a type that allows us to move backwards
        // We can use ArrayList for this as the concrete
        // type

        IList reversedEnumerable = new ArrayList();
        IEnumerator tempEnumerator = enumerableInstance.GetEnumerator();

        while (tempEnumerator.MoveNext())
        {
            reversedEnumerable.Add(tempEnumerator.Current);
        }

        // Now we do the standard reverse over this using yield to return
        // the result
        // NOTE: This is an immutable result by design. That is 
        // a design goal for this simple question as well as most other set related 
        // requirements, which is why Linq results are immutable for example
        // In fact this is foundational code to understand Linq

        for (var i = reversedEnumerable.Count - 1; i >= 0; i--)
        {
            yield return reversedEnumerable[i];
        }
    }
}



public static class ExtensionMethods
{

      public static IEnumerable ToReveresed(this IEnumerable enumerableInstance)
      {
          return ReverserService.ToReveresed(enumerableInstance);
      }
 }

[TestFixture] 공개 클래스 Testing123 {

    /// <summary>
    /// .NET 1.1 CLR
    /// </summary>
    [Test]
    public void Tester_fornet_1_dot_1()
    {
        const int initialSize = 1000;

        // Create the baseline data
        int[] myArray = new int[initialSize];

        for (var i = 0; i < initialSize; i++)
        {
            myArray[i] = i + 1;
        }

        IEnumerable _revered = ReverserService.ToReveresed(myArray);

        Assert.IsTrue(TestAndGetResult(_revered).Equals(1000));
    }

    [Test]
    public void tester_why_this_is_good()
    {

        ArrayList names = new ArrayList();
        names.Add("Jim");
        names.Add("Bob");
        names.Add("Eric");
        names.Add("Sam");

        IEnumerable _revered = ReverserService.ToReveresed(names);

        Assert.IsTrue(TestAndGetResult(_revered).Equals("Sam"));


    }

    [Test]
    public void tester_extension_method()
  {

        // Extension Methods No Linq (Linq does this for you as I will show)
        var enumerableOfInt = Enumerable.Range(1, 1000);

        // Use Extension Method - which simply wraps older clr code
        IEnumerable _revered = enumerableOfInt.ToReveresed();

        Assert.IsTrue(TestAndGetResult(_revered).Equals(1000));


    }


    [Test]
    public void tester_linq_3_dot_5_clr()
    {

        // Extension Methods No Linq (Linq does this for you as I will show)
        IEnumerable enumerableOfInt = Enumerable.Range(1, 1000);

        // Reverse is Linq (which is are extension methods off IEnumerable<T>
        // Note you must case IEnumerable (non generic) using OfType or Cast
        IEnumerable _revered = enumerableOfInt.Cast<int>().Reverse();

        Assert.IsTrue(TestAndGetResult(_revered).Equals(1000));


    }



    [Test]
    public void tester_final_and_recommended_colution()
    {

        var enumerableOfInt = Enumerable.Range(1, 1000);
        enumerableOfInt.PerformOverReversed(i => Debug.WriteLine(i));

    }



    private static object TestAndGetResult(IEnumerable enumerableIn)
    {
      //  IEnumerable x = ReverserService.ToReveresed(names);

        Assert.IsTrue(enumerableIn != null);
        IEnumerator _test = enumerableIn.GetEnumerator();

        // Move to first
        Assert.IsTrue(_test.MoveNext());
        return _test.Current;
    }
}

2
Dude 또는 dudette : "for (int i = myArray.Length-1; i> = 0; i--)"를 작성하는 덜 깔끔한 방법을 찾고있었습니다.
MusiGenesis

1
이 답글에는 가치가 전혀 없습니다
Matt Melton
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.