Standart neden end()
asıl sondan ziyade bir son olarak tanımlanıyor ?
Standart neden end()
asıl sondan ziyade bir son olarak tanımlanıyor ?
Yanıtlar:
En iyi argüman Dijkstra'nın kendisi tarafından yapılan tartışmadır :
Sen aralığın büyüklüğü basit bir fark olmasını istediğiniz uç - başlayacak ;
sekanslar boş olanlara dejenere olduğunda alt sınır dahil olmak daha "doğal" dır ve ayrıca alternatif ( alt sınır hariç ) bir "başlangıçtan önce" sentinel değerinin varlığını gerektireceğinden.
Hala neden bir sayı yerine sıfırdan saymaya başladığınızı gerekçelendirmeniz gerekiyor, ancak bu sorunuzun bir parçası değildi.
[Başlangıç, bitiş) kuralının ardındaki bilgelik, doğal olarak zincirlenen aralık tabanlı yapılara birden çok iç içe veya yinelenen çağrı ile ilgilenen herhangi bir algoritmaya sahip olduğunuz zaman tekrar tekrar öder. Buna karşılık, iki kat kapalı bir aralık kullanılması, tek tek ve son derece hoş olmayan ve gürültülü bir kodla sonuçlanabilir. Örneğin, bir bölümü [ n 0 , n 1 ) [ n 1 , n 2 ) [ n 2 , n 3 ) ele alalım . Başka bir örnek, süreleri for (it = begin; it != end; ++it)
çalıştıran standart yineleme döngüsüdür end - begin
. Her iki uç da kapsayıcıysa, ilgili kod daha az okunabilir olacaktır ve boş aralıkları nasıl ele alacağınızı hayal edin.
Son olarak, saymanın neden sıfırdan başlaması gerektiği konusunda güzel bir argüman yapabiliriz: Yeni oluşturduğumuz aralıklar için yarı açık kuralla, bir dizi N öğesi verilirse (bir dizinin üyelerini saymayı söyleyin), 0 doğal "başlangıç" tır, böylece aralığı herhangi bir garip ofset veya düzeltme olmadan [0, N ) olarak yazabilirsiniz .
Özetle: 1
menzil tabanlı algoritmalarda her yerde sayıyı görmüyor olmamız , [başlangıç, bitiş) kuralının doğrudan bir sonucu ve motivasyonudur.
begin
ve sırasıyla, mükemmel bir uyum sağlar. Tartışmalı olarak, geleneksel olandan daha doğal olan durumdur , ancak daha genel koleksiyonları düşünmeye başlayana kadar bunu asla keşfetmedik. end
int
0
N
!=
<
++
eklenebilir yineleyici şablonu yazmalısınız step_by<3>
.
!=
gerektiğinde kullanırsa <
, bu bir hatadır. Bu arada, bu hata kralı birim testi veya iddialarla bulmak kolaydır.
Eğer yineleyiciler göstermiyorsa düşünün Aslında, eğer yineleyici ilgili bir sürü şey aniden çok daha mantıklı en dizisinin elemanları fakat aradaki yanında eleman hakkı erişen çözümleyecek ile. Sonra "bir geçmiş son" yineleyici aniden hemen mantıklı:
+---+---+---+---+
| A | B | C | D |
+---+---+---+---+
^ ^
| |
begin end
Açıkçası begin
dizinin başlangıcını end
ve aynı dizinin sonunu işaret eder. Dereferencing begin
öğeye erişir A
ve dereferencing end
hiçbir anlam ifade etmez, çünkü öğesinde doğru öğe yoktur. Ayrıca, i
ortada bir yineleyici eklemek
+---+---+---+---+
| A | B | C | D |
+---+---+---+---+
^ ^ ^
| | |
begin i end
ve hemen gelen unsurların aralığı görüyoruz begin
için i
öğeleri içerir A
ve B
gelen unsurların aralığı ise i
hiç end
öğeleri içerir C
ve D
. Dereferencing i
, öğeye doğru, yani ikinci dizinin ilk öğesi olan öğeyi verir.
Ters yineleyiciler için "birebir" bile aniden bu şekilde belirginleşir: Bu diziyi ters çevirmek:
+---+---+---+---+
| D | C | B | A |
+---+---+---+---+
^ ^ ^
| | |
rbegin ri rend
(end) (i) (begin)
Karşılık gelen (ters) yineleyicileri aşağıdaki parantez içinde yazdım. Görüyorsunuz, ters yineleyici ait i
(I adlandırdığınız hangi ri
) hala elemanları arasında yer noktaları B
ve C
. Ancak diziyi tersine çevirme nedeniyle, şimdi öğe B
onun sağındadır.
foo[i]
) konumdan hemen sonra öğe için bir kısayol olması daha iyi gösterilebileceğini düşünüyorum i
. Bunu düşünerek, bir dilin "konum i'den hemen sonra öğe" ve "konum i'den hemen önce öğe" için ayrı işleçlere sahip olmasının yararlı olup olmayacağını merak ediyorum, çünkü birçok algoritma bitişik öğe çiftleriyle çalışıyor ve " İ "konumunun her iki yanındaki öğeler" i ve i + 1 konumlarındaki öğeler "den daha temiz olabilir.
begin[0]
(rasgele bir erişim yineleyicisi varsayarak) öğeye erişebilir 1
, çünkü 0
örnek dizilimde hiçbir öğe yoktur .
start()
belirli bir işlemi başlatmak için sınıfınızda bir işlev tanımlamanız gerektiğinde veya her ne olursa olsun, zaten varolan bir işlemle çakışması rahatsız edici olacaktır).
Standart neden end()
asıl sondan ziyade bir son olarak tanımlanıyor ?
Çünkü:
begin()
için
end()
& end()
ulaşılmadığı sürece devam eder .Çünkü o zaman
size() == end() - begin() // For iterators for whom subtraction is valid
ve böyle garip şeyler yapmak zorunda kalmayacaksın
// Never mind that this is INVALID for input iterators...
bool empty() { return begin() == end() + 1; }
ve yanlışlıkla hatalı kod yazmazsınız.
bool empty() { return begin() == end() - 1; } // a typo from the first version
// of this post
// (see, it really is confusing)
bool empty() { return end() - begin() == -1; } // Signed/unsigned mismatch
// Plus the fact that subtracting is also invalid for many iterators
Ayrıca: Geçerli bir öğeye işaret edilirse ne find()
dönecektir end()
? Gerçekten geçersiz bir yineleyici döndüren adlı başka bir üye ister
misiniz ?!
İki yineleyici zaten yeterince acı verici ...invalid()
Oh, ve bakın bu ilişkili bir yazıya .
Eğer end
son öğe önceydi, nasıl olur insert()
gerçek sonunda ?!
Yarı kapalı aralıkların yineleyici deyimi [begin(), end())
başlangıçta düz diziler için işaretçi aritmetiğine dayanır. Bu çalışma modunda, bir diziye ve bir boyuta geçirilen işlevleriniz olur.
void func(int* array, size_t size)
[begin, end)
Bu bilgilere sahip olduğunuzda yarı kapalı aralıklara dönüştürmek çok basittir:
int* begin;
int* end = array + size;
for (int* it = begin; it < end; ++it) { ... }
Tamamen kapalı aralıklarla çalışmak daha zordur:
int* begin;
int* end = array + size - 1;
for (int* it = begin; it <= end; ++it) { ... }
Dizilere işaretçiler C ++ 'da yineleyiciler olduğundan (ve sözdizimi buna izin vermek için tasarlandığından), aramaktan std::find(array, array + size, some_value)
daha kolay std::find(array, array + size - 1, some_value)
.
Ayrıca, yarı kapalı aralıklarla çalışıyorsanız, !=
son koşulu kontrol etmek için operatörü kullanabilirsiniz , çünkü (operatörleriniz doğru tanımlanmışsa) <
ima eder !=
.
for (int* it = begin; it != end; ++ it) { ... }
Ancak bunu tamamen kapalı aralıklarla yapmanın kolay bir yolu yoktur. İle sıkışıp kaldın <=
.
C ++ 'da destekleyen <
ve çalışan tek yineleyici >
rasgele erişimli yineleyicilerdir. <=
C ++ 'da her yineleyici sınıfı için bir operatör yazmak zorunda olsaydınız, tüm yineleyicilerinizi tamamen karşılaştırılabilir hale getirmeniz gerekir ve daha az yetenekli yineleyiciler (üzerinde çift yönlü yineleyiciler std::list
veya giriş yineleyiciler gibi) oluşturmak için daha az seçenek olurdu faaliyet gösterdiklerini iostreams
C ++ tamamen kapalı aralıkları kullanılıyorsa).
İle end()
sonuna geçmiş işaret biri, bu döngü için bir koleksiyon yineleme kolaydır:
for (iterator it = collection.begin(); it != collection.end(); it++)
{
DoStuff(*it);
}
İle end()
son öğeye işaret ederek, bir döngü daha karmaşık olacaktır:
iterator it = collection.begin();
while (!collection.empty())
{
DoStuff(*it);
if (it == collection.end())
break;
it++;
}
begin() == end()
,.!=
yerine kullanma eğilimindedir <
, bu nedenle end()
uç noktadan bir pozisyona işaret etmek uygundur.