مثالي للحلقة

اليوم ، شكل مقال غير عادي بالنسبة لي: أفضل أن أطرح سؤالاً على الجمهور بدلاً من مشاركة وصفة جاهزة. ومع ذلك ، أقترح أيضًا وصفة لبدء مناقشة. لذا سنتحدث اليوم عن الشعور بالجمال.



لقد كنت أكتب الكود لبعض الوقت ، ويحدث أنه دائمًا ما يكون في C ++. لا يمكنني حتى تقدير عدد المرات التي كتبت فيها مثل هذا البناء:



for (int i=0; i<size; i++) {
    [...]
}


رغم أنني لا أستطيع ، يمكنني حقًا:



find . \( -name \*.h -o -name \*.cpp \) -exec grep -H "for (" {} \; | wc -l
43641


يحتوي مشروعنا الحالي على 43 ألف دورة. لست الوحيد الذي شاهد المشروع ، لكن الفريق صغير ومشروعي ليس الأول (وآمل ألا يكون الأخير) ، لذلك سيتم استخدامه كتقدير تقريبي. ما مدى forجودة هذا التسجيل الدائري ؟ في الواقع ، في الواقع ، من المهم عدم حتى عدد المرات التي كتبت فيها الدورة ، ولكن عدد المرات التي قرأت فيها الدورة (انظر تصحيح الأخطاء ومراجعة الكود). وهنا من الواضح أننا نتحدث عن الملايين.



على عقدة KPDV تسمى "حلقة كاملة" (حلقة الكمال).



صورة



إذن ما هي الدورة المثالية؟



ما هي المشكلة؟



; , , , . , :



for (int iter=0; iter<nb_iter; iter++) {          // some iterative computation
    for (int c=0; c<mesh.cells.nb(); c++)         // loop through all tetrahedra
        for (int lv0=0; lv0<4; lv0++)             // for every pair of
            for (int lv1 = lv0+1; lv1<4; lv1++)   // vertices in the tet
                for (int d=0; d<3; d++) {         // do stuff for each of 3 dimensions
                    nlRowScaling(weight);
                    nlBegin(NL_ROW);
                    nlCoefficient(mesh.cells.vertex(c, lv0)*3 + d,  1);
                    nlCoefficient(mesh.cells.vertex(c, lv1)*3 + d, -1);
                    nlEnd(NL_ROW);
                }
    [...]
}


, , . , , --, .



; . ( ) , for (int i=0; i<size; i++) — : for , .



for(;;), : , . , 0 size-1, . , ? , — .



c++11 , :



#define FOR(I,UPPERBND) for(int I = 0; I<int(UPPERBND); ++I)


:



FOR(iter, nb_iter) {
    FOR(c, mesh.cells.nb())
        FOR(lv0, 4)
            for (int lv1 = lv0+1; lv1<4; lv1++)
                FOR(d, 3) {
                    nlRowScaling(weight);
                    nlBegin(NL_ROW);
                    nlCoefficient(mesh.cells.vertex(c, lv0)*3 + d,  1);
                    nlCoefficient(mesh.cells.vertex(c, lv1)*3 + d, -1);
                    nlEnd(NL_ROW);
                }
    [...]
}


, for (;;), , (, , ). FOR(,) 0 n-1 - . , , , (. ), (, , ).



, , , : " , ?"



11 , range for



? , . , :



for i in range(n):
    print(i)


, range() , . c++11 !



#include <iostream>
int main() {
    int range[] = {0,1,2,3,4,5};
    for (int i : range) {
        std::cerr << i;
    }
}


, , . C++ !



range(int n):



#include <iostream>

constexpr auto range(int n) {
    struct iterator {
        int i;
        void operator++() { ++i; }
        bool operator!=(const iterator& rhs) const { return i != rhs.i; }
        const int &operator*() const { return i; }
    };
    struct wrapper {
        int n;
        auto begin() { return iterator{0}; }
        auto end()   { return iterator{n}; }
    };
    return wrapper{n};
}

int main() {
    for (int i: range(13)) {
        std::cerr << i;
    }
    return 0;
}


, int vs size_t, . gcc 10.2 -std=c++17 -Wall -Wextra -pedantic -O1, ( ):



[...]
.L2:
        mov     esi, ebx
        mov     edi, OFFSET FLAT:_ZSt4cerr
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        add     ebx, 1
        cmp     ebx, 13
        jne     .L2
[...]


, , for (int i=0; i<13; i++).



, for (int i: range(n)) , FOR(,), , .



: enumerate



Range for c++11 . , , , :



#include <vector>
#include <iostream>

struct vec3 {  double x,y,z;  };

int main() {
    std::vector<vec3> points = {{6,5,8},{1,2,3},{7,3,7}};
    for (vec3 &p: points) {
        std::cerr << p.x;
    }
    return 0;
}


for (vec3 &p: points) , , . , ? , . , , , :



    std::vector<vec3> points = {{6,5,8},{1,2,3},{7,3,7}};
    std::vector<double> weights = {4,6,9};
    int i = 0;
    for (vec3 &p: points) {
        std::cerr << p.x << weights[i++];
    }


:



asm
.L2:
        movsd   xmm0, QWORD PTR [r13+0]
        mov     edi, OFFSET FLAT:_ZSt4cerr
        call    std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<double>(double)
        movsd   xmm0, QWORD PTR [rbp+0]
        mov     rdi, rax
        call    std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<double>(double)
        add     rbp, 8
        add     r13, 24
        cmp     r14, rbp
        jne     .L2


, , , , , c++17 structural binding!



, :



#include <vector>
#include <iostream>
#include "range.h"

struct vec3 {
    double x,y,z;
};

int main() {
    std::vector<vec3> points = {{6,5,8},{1,2,3},{7,3,7}};
    std::vector<double> weights = {4,6,9};

    for (auto [i, p]: enumerate(points)) {
        std::cerr << p.x << weights[i];
    }

    return 0;
}


enumerate() :



range.h
#ifndef __RANGE_H__
#define __RANGE_H__

#include <tuple>
#include <utility>
#include <iterator>

constexpr auto range(int n) {
    struct iterator {
        int i;
        void operator++() { ++i; }
        bool operator!=(const iterator& rhs) const { return i != rhs.i; }
        const int &operator*() const { return i; }
    };
    struct wrapper {
        int n;
        auto begin() { return iterator{0}; }
        auto end()   { return iterator{n}; }
    };
    return wrapper{n};
}

template <typename T> constexpr auto enumerate(T && iterable) {
    struct iterator {
        int i;
        typedef decltype(std::begin(std::declval<T>())) iterator_type;
        iterator_type iter;
        bool operator!=(const iterator& rhs) const { return iter != rhs.iter; }
        void operator++() { ++i; ++iter; }
        auto operator*() const { return std::tie(i, *iter); }
    };
    struct wrapper {
        T iterable;
        auto begin() { return iterator{0, std::begin(iterable)}; }
        auto end()   { return iterator{0, std::end  (iterable)}; }
    };
    return wrapper{std::forward<T>(iterable)};
}

#endif // __RANGE_H__


-std=c++17 -Wall -Wextra -pedantic -O2 ( ):



ASM
.L14:
        movsd   xmm0, QWORD PTR [rbx]
        mov     edi, OFFSET FLAT:_ZSt4cerr
        call    std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<double>(double)
        mov     rdi, rax
        mov     rax, QWORD PTR [rsp+32]
        movsd   xmm0, QWORD PTR [rax+rbp]
        call    std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<double>(double)
        add     rbx, 24
        add     rbp, 8
        cmp     r12, rbx
        jne     .L14


ومرة أخرى ، قام المترجم بإزالة الغلاف (ومع ذلك ، كان من الضروري رفع مستوى التحسين من -O1إلى -O2).

بالمناسبة ، ظهر في c ++ 20 std::ranges، مما يجعل كتابة مثل هذه الوظيفة أسهل ، لكنني لست مستعدًا بعد للتبديل إلى هذا المعيار.



سؤال للجمهور



برأيك ، ما هي الدورة المثالية لعام 2020؟ علمني!



إذا لم تكن قد طرحت هذا السؤال بعد ، فقم بنسخ ملف الرأس إلى مشروع الحيوانات الأليفة range.hوحاول استخدامه لبضعة أيام على الأقل.




All Articles