programing

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

minecode 2022. 8. 13. 23:00
반응형

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

어레이 내에서 뒤로 이동해야 하기 때문에 다음과 같은 코드가 있습니다.

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

더 좋은 방법이 있을까요?

업데이트: C#에는 다음과 같은 메커니즘이 내장되어 있을 것으로 기대하고 있습니다.

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

업데이트 2: 더 나은 방법이 있습니다.Rune는 다음과 같이 상을 받는다.

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

(Twotymz 덕분에) 일반 C가 더 보기 좋습니다.

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

조금 애매하긴 하지만, 이것을 하는 가장 좋은 방법은 타이포그래피상으로는

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

나는 '전형적으로 즐거운' 코드보다 항상 명확한 코드를 선호한다.따라서 항상 다음을 사용합니다.

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

루프를 거꾸로 돌리는 표준 방법으로 간주할 수 있습니다.
그냥 내 의견일 뿐이야

C++에서는 기본적으로 반복기 또는 인덱스를 사용하여 반복할 수 있습니다., """가 있는지에 따라 .std::vector.

std:: 벡터 사용

반복기 사용

하면 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_tsizeof 반환하다std::size_t★★★★★★★★★★★★★★★★★★.

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

포인터에 적용된 크기의 함정 방지

실제로 위의 배열 크기를 결정하는 방법은 형편없습니다.가 실제로 배열이 아닌 포인터일 경우(매우 빈번하게 발생하며 초보자가 혼동할 수 있음), 자동으로 실패합니다.더 나은 방법은 다음을 사용하는 것입니다. 포인터가 주어진 경우 컴파일 시 실패합니다.

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

먼저 전달된 배열의 크기를 가져온 다음 같은 크기의 char 유형 배열에 대한 참조를 반환하도록 선언하면 작동합니다. char에는 「이행하다」라고되어 있습니다.sizeof는 /1 입니다.sizeofOf: N * 1. 컴파일 시간 평가만 하고 런타임 오버헤드는 발생하지 않습니다.

하는 대신에

(sizeof a / sizeof *a)

코드를 변경하여 다음 작업을 수행합니다.

(sizeof array_size(a))

C#에 Visual Studio 2005 이상을 사용하여 'for'를 입력하고 [TAB] [TAB]를 누릅니다.이 경우 A로 확장됩니다.for이치노

(적어도) 틀리기 쉬워서 이 조각을 넣는 게 좋을 것 같아요.

하지만,Array.Reverse()Enumerable.Reverse()앞으로 더 잘 반복할 수 있습니다. 더 명확하게 의도를 나타냅니다.

C#에서는 Linq를 사용합니다.

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

나는 이것을 한다

if (list.Count > 0)
    for (size_t i = list.Count - 1; i >= 0; i--)
    {
        //do your thing
    
        if (i == 0) //for preventing unsigned wrap
            break;
    }

근데 왠지 비주얼 스튜디오 2019가 화를 내면서 '잘못 정의된 루프' 같은 걸 경고해줘요.그것은 나를 믿지 않는다

이는 길이가 서명된 일체형인 어레이에 가장 적합한 방법입니다. " 이이 type type type type ( ( ( ( (예예예예예예예예예예예예예예예")인 배열의 경우:std::vectorC++)의 경우는, 종료 조건을 약간 변경할 필요가 있습니다.

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

'아까부터'라고 하면i >= 0부호 없는 정수에 대해서는 항상 해당되므로 루프는 무한 루프입니다.

이치노하는 것보다 더 i loop의

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

메모: 이 투고는 훨씬 더 상세하게 끝났기 때문에 주제에서 벗어났습니다. 죄송합니다.

내 동료들이 그것을 읽고 '어디선가' 가치가 있다고 믿는다는 것은 말이다.이 실은 그 자리가 아니다.이 사이트를 처음 접하는 곳과 관련하여 피드백을 주시면 감사하겠습니다(저는 이 사이트를 처음 접합니다.


어쨌든 이것은 의 C# 버전입니다.정의된 의미론을 사용하여 모든 컬렉션 유형에서 작동한다는 점에서 NET 3.5는 놀랍습니다.이는 대부분의 일반적인 개발 시나리오에서 성능 또는 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 타입 시스템은 IENumerable 인터페이스로부터 상속됩니다(일반 IENumerable만이 IENumerable이 아닙니다).

처음부터 끝까지 반복하기만 하면 되지만 반대 방향으로 이동해야 합니다.IENumerable은 유형 'object'의 어레이에서 작동하므로 모든 유형이 유효합니다.

중요한 측정: 어떤 시퀀스를 역순으로 처리할 수 있으면 정수에 대해서만 처리할 수 있다고 가정합니다.

솔루션 a for.NET CLR 2.0~3.0:

설명:IEnumerable 실장 인스턴스가 포함된 각 인스턴스가 동일한 유형인 것을 승인합니다.따라서 어레이를 수신하면 어레이 전체에 유형 X의 인스턴스가 포함됩니다.다른 인스턴스가 !=X 유형일 경우 예외가 느려집니다.

싱글톤 서비스:

퍼블릭 클래스 ReverserService {private 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);
      }
 }

[TestFacture] 퍼블릭 클래스 테스트 123 {

    /// <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;
    }
}

제가 보기에는 좋아 보이는 걸요.인덱서가 서명되지 않은(uint 등) 경우 이 점을 고려해야 할 수 있습니다.게으르다고 불러도 좋지만, (부호 없이) 이 경우 카운터 변수를 사용할 수 있습니다.

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

(사실 여기에서도 ar와 같은 케이스는 주의해야 합니다.길이 = uint.최대값...어디선가!=일 수도 있고...물론, 그것은 매우 드문 경우입니다!)

C에서는 다음을 수행합니다.


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

Musi Genesis에 의해 추가된 C# 예:

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

C++에서 이를 위한 가장 좋은 방법은 아마도 반복기(또는 더 나은 범위) 어댑터를 사용하는 것입니다. 이 어댑터는 시퀀스를 통과할 때 서서히 변환합니다.

기본적으로는

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

범위 "범위"를 역순으로 표시합니다(여기서는 비어 있지만 직접 요소를 추가할 수 있습니다).물론 단순히 범위를 반복하는 것은 별로 쓸모가 없지만, 알고리즘 등에 새로운 범위를 전달하는 것은 꽤 멋진 일입니다.

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

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

모든 요소에 함수 "f"가 적용되어 "p"가 참이 아닌 요소가 제거되고 마지막으로 결과 범위가 반전되는 범위 "range"를 느릿느릿 계산합니다.

파이프 구문은 infix에 따라 가장 읽기 쉬운 IMO입니다.더 부스트리뷰 보류 중인 범위 라이브러리 업데이트는 이를 구현하지만 직접 수행하는 것도 매우 간단합니다.함수 f와 술어 p를 인라인으로 생성하기 위해 람다 DSEL을 사용하면 더욱 멋집니다.

// this is how I always do it
for (i = n; --i >= 0;){
   ...
}

원래 질문의 코드를 사용하고 싶지만, Foreach를 사용하여 C#에 정수 인덱스를 붙이고 싶다면:

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

C++의 경우:

다른 사람들이 언급했듯이, 가능한 경우(즉, 한 번에 각 요소만 원하는 경우) 반복기를 사용하여 명시적이고 일반적인 함정을 피하는 것이 매우 바람직하다.의 C는 C++로 을 가지고 .auto:

std::vector<int> vec = {1,2,3,4};
for (auto it = vec.rbegin(); it != vec.rend(); ++it) {
    std::cout<<*it<<" ";
}

인쇄하다4 3 2 1 .

루프 중에 값을 변경할 수도 있습니다.

std::vector<int> vec = {1,2,3,4};
for (auto it = vec.rbegin(); it != vec.rend(); ++it) {
    *it = *it + 10;
    std::cout<<*it<<" ";
}

로 이어지다14 13 12 11 인쇄되고 있다{11, 12, 13, 14}에 있다std::vector나중에.

루프 중에 값을 변경할 계획이 없는 경우 실수로 값을 변경하려고 하면 오류가 발생하는지 확인해야 합니다.for(const auto& element : vec)이치노

std::vector<int> vec = {1,2,3,4};
for (auto it = vec.crbegin(); it != vec.crend(); ++it) { // used crbegin()/crend() here...
    *it = *it + 10; // ... so that this is a compile-time error
    std::cout<<*it<<" ";
}

이 경우의 컴파일러 오류는 다음과 같습니다.

/tmp/main.cpp:20:9: error: assignment of read-only location ‘it.std::reverse_iterator<__gnu_cxx::__normal_iterator<const int*, std::vector<int> > >::operator*()’
   20 |     *it = *it + 10;
      |     ~~~~^~~~~~~~~~

또, 다른 반복 타입을 함께 사용하지 말아 주세요.

std::vector<int> vec = {1,2,3,4};
for (auto it = vec.rbegin(); it != vec.end(); ++it) { // mixed rbegin() and end()
    std::cout<<*it<<" ";
}

자세한 에러가 발생합니다.

/tmp/main.cpp: In function ‘int main()’:
/tmp/main.cpp:19:33: error: no match for ‘operator!=’ (operand types are ‘std::reverse_iterator<__gnu_cxx::__normal_iterator<int*, std::vector<int> > >’ and ‘std::vector<int>::iterator’ {aka ‘__gnu_cxx::__normal_iterator<int*, std::vector<int> >’})
   19 | for (auto it = vec.rbegin(); it != vec.end(); ++it) {
      |                              ~~ ^~ ~~~~~~~~~
      |                              |            |
      |                              |            std::vector<int>::iterator {aka __gnu_cxx::__normal_iterator<int*, std::vector<int> >}
      |                              std::reverse_iterator<__gnu_cxx::__normal_iterator<int*, std::vector<int> > >

스택에 C 스타일 배열이 있는 경우 다음과 같은 작업을 수행할 수 있습니다.

int vec[] = {1,2,3,4};
for (auto it = std::crbegin(vec); it != std::crend(vec); ++it) {
    std::cout<<*it<<" ";
}

인덱스가 꼭 필요한 경우 다음 옵션을 고려하십시오.

  • 범위를 확인한 다음 부호 있는 값으로 작업합니다. 예:
void loop_reverse(std::vector<int>& vec) {
    if (vec.size() > static_cast<size_t>(std::numeric_limits<int>::max())) {
        throw std::invalid_argument("Input too large");
    }
    const int sz = static_cast<int>(vec.size());
    for(int i=sz-1; i >= 0; --i) {
        // do something with i
    }
}
  • 부호 없는 값으로 작업하고 주의하며 다음과 같이 주석을 추가합니다.
void loop_reverse2(std::vector<int>& vec) {
    for(size_t i=vec.size(); i-- > 0;) { // reverse indices from N-1 to 0
        // do something with i
    }
}
  • 실제 지수를 별도로 계산한다. 예:
void loop_reverse3(std::vector<int>& vec) {
    for(size_t offset=0; offset < vec.size(); ++offset) {
        const size_t i = vec.size()-1-offset; // reverse indices from N-1 to 0
        // do something with i
    }
}

여기서 제 질문에 답해 보겠습니다만, 이것도 마음에 들지 않습니다.

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

언급URL : https://stackoverflow.com/questions/275994/whats-the-best-way-to-do-a-backwards-loop-in-c-c-c

반응형