اليوم ، شكل مقال غير عادي بالنسبة لي: أفضل أن أطرح سؤالاً على الجمهور بدلاً من مشاركة وصفة جاهزة. ومع ذلك ، أقترح أيضًا وصفة لبدء مناقشة. لذا سنتحدث اليوم عن الشعور بالجمال.
لقد كنت أكتب الكود لبعض الوقت ، ويحدث أنه دائمًا ما يكون في 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++];
}
:
.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() :
#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 ( ):
.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وحاول استخدامه لبضعة أيام على الأقل.