Heute ist das Format eines Artikels für mich ungewöhnlich: Ich stelle dem Publikum lieber eine Frage, als ein fertiges Rezept zu teilen. Ich schlage jedoch auch ein Rezept vor, um eine Diskussion einzuleiten. Also werden wir heute über das Gefühl der Schönheit sprechen.
Ich schreibe schon seit einiger Zeit Code und es kommt einfach so vor, dass er fast immer in C ++ ist. Ich kann nicht einmal abschätzen, wie oft ich eine solche Konstruktion geschrieben habe:
for (int i=0; i<size; i++) {
[...]
}
Obwohl warum ich nicht kann, kann ich wirklich:
find . \( -name \*.h -o -name \*.cpp \) -exec grep -H "for (" {} \; | wc -l
43641
Unser aktuelles Projekt enthält 43.000 Zyklen. Ich bin nicht der einzige, der das Projekt sieht, aber das Team ist klein und mein Projekt ist nicht das erste (und ich hoffe nicht das letzte), daher wird es als grobe Schätzung verwendet. Wie forgut ist diese Loop-Aufnahme ? Tatsächlich ist es nicht einmal wichtig , wie oft ich den Zyklus geschrieben habe , sondern wie oft ich den Zyklus gelesen habe (siehe Debugging und Codeüberprüfung). Und hier sprechen wir offensichtlich von Millionen.
Bei KPDV wird ein Knoten als "Perfektionsschleife" bezeichnet .

Was ist der perfekte Zyklus?
Was ist das Problem?
; , , , . , :
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 .L14Und wieder entfernte der Compiler den Wrapper sauber (dafür war es jedoch notwendig, die Optimierungsstufe von -O1auf zu erhöhen -O2).
Übrigens erschien es in c ++ 20 std::ranges, was das Schreiben einer solchen Funktion noch einfacher macht, aber ich bin noch nicht bereit, auf diesen Standard umzusteigen.
Frage an das Publikum
Was sollte Ihrer Meinung nach der perfekte Zyklus im Jahr 2020 sein? Lehre mich!
Wenn Sie diese Frage noch nicht gestellt haben, kopieren Sie die Header-Datei in Ihr Haustierprojekt range.hund versuchen Sie, sie mindestens einige Tage lang zu verwenden.