Aralık (başlangıç, bitiş) neden sonu içermiyor?


305
>>> range(1,11)

sana verir

[1,2,3,4,5,6,7,8,9,10]

Neden 1-11 değil?

Sadece rastgele böyle yapmaya karar verdiler mi yoksa görmediğim bir değeri var mı?


11
okuma Dijkstra, ewd831
SilentGhost

11
Temel olarak bir diğeri için birer birer hatalar seçiyorsunuz. Bir kümenin döngülerinizin erken sonlanmasına neden olması, diğerinin de bir İstisnaya (veya diğer dillerde arabellek taşmasına) neden olması daha olasıdır. Bir sürü kod yazdıktan sonra, davranış seçiminin range()çok daha anlamlı olduğunu göreceksiniz
John La Rooy

32
Dijkstra, ewd831 bağlantısı: cs.utexas.edu/users/EWD/ewd08xx/EWD831.PDF
unutbu

35
@unutbu Djikstra makalesi bu konuda sık sık atıfta bulunuluyor ancak burada değerli bir şey sunmuyor, insanlar bunu sadece otoriteye başvurmak için kullanıyor. OP'nin sorusu için verdiği tek ilgili sahte neden, üst dizinin dahil edilmesinin, boş dizinin - tamamen öznel bir pozisyona ve kolayca tartışıldığı özel durumda , "doğal olmayan" ve "çirkin" olduğunu hissetmesi. bu yüzden masaya fazla bir şey getirmiyor. Mesa ile yapılan "deney", kendi özel kısıtlamalarını veya değerlendirme yöntemlerini bilmeden çok değerli değildir.
sundar - Monica'yı eski durumuna getir

6
@andreasdr Fakat kozmetik argüman geçerli olsa bile, Python'un yaklaşımı yeni bir okunabilirlik sorunu getirmiyor mu? Ortak-kullanımda İngilizce terim "aralık" bir şeyin aralıkları ima gelen bir şey için bir şey - aralıklarla gibi. Bu len (list (aralık (1,2))) 1 döndürür ve len (list (aralık (2))) 2 döndürür gerçekten sindirmeyi öğrenmek zorunda olduğunuz bir şeydir.
armin

Yanıtlar:


245

Çünkü 10 öğeye eşit range(0, 10)olan döndürmeyi çağırmak daha yaygındır . Programcıların 0 tabanlı indekslemeyi tercih ettiğini unutmayın.[0,1,2,3,4,5,6,7,8,9]len(range(0, 10))

Ayrıca, aşağıdaki ortak kod snippet'ini de göz önünde bulundurun:

for i in range(len(li)):
    pass

Eğer görebiliyordu range()tam olarak çıktım len(li)bu sorunlu olacağını? Programcı açıkça Bu aynı zamanda tercih programcılar ortak bir trend izlemektedir 1. çıkarmak gerekir for(int i = 0; i < 10; i++)üzerinde for(int i = 0; i <= 9; i++).

Aralığı 1 başlangıçıyla sık sık çağırıyorsanız, kendi işlevinizi tanımlamak isteyebilirsiniz:

>>> def range1(start, end):
...     return range(start, end+1)
...
>>> range1(1, 10)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

48
Sebep bu olsaydı parametreler olmaz mıydı range(start, count)?
Mark Ransom

3
0 başlangıç değeri varsayılan @shogun örneğin, range(10)eşdeğerdir range(0, 10).
moinudin

4
Sizin range1farklı bir adım boyutuna sahip aralıkları ile çalışmaz 1.
dimo414

6
(X) aralığının 0 ile başlaması gerektiğini ve x'in "aralığın uzunluğu" olacağını açıklarsınız. TAMAM. Ancak (x, y) aralığının neden x ile başlayıp y-1 ile bitmesi gerektiğini açıklamıyorsunuz. Programcı 1 ile 3 arasında değişen bir for-loop istiyorsa, açıkça 1 eklemesi gerekir. Bu gerçekten kolaylık hakkında mı?
armin

7
for i in range(len(li)):daha ziyade bir antipattern. Biri kullanmalı enumerate.
hans

27

Burada bazı yararlı algoritmik açıklamalar olmasına rağmen, konuyu neden bu şekilde çalıştığına dair basit bir 'gerçek hayat' mantığı eklemeye yardımcı olabileceğini düşünüyorum, bu da genç yeni gelenlere konuyu tanıtırken yararlı buldum:

'Range (1,10)' gibi bir şeyle karışıklık, parametre çiftinin "başlangıç ​​ve bitiş" i temsil ettiğini düşünmekten kaynaklanabilir.

Aslında başlangıç ​​ve "durma" dır.

O Şimdi eğer vardı sonra "son" değerini, evet, bu sayı, dizideki son girdi olarak dahil olacağını beklenebilir. Ama bu "son" değil.

Diğerleri yanlışlıkla bu parametreyi "sayım" olarak adlandırır, çünkü yalnızca 'range (n)' kullanırsanız, elbette 'n' kez tekrarlar. Start parametresini eklediğinizde bu mantık bozulur.

Yani kilit nokta ismini hatırlamaktır: " dur ". Bu, ulaşıldığında, yinelemenin hemen duracağı noktadır. Bu noktadan sonra değil .

Dolayısıyla, "start" gerçekten dahil edilecek ilk değeri temsil etse de, "stop" değerine ulaştığında, durmadan önce "bunu da" işlemeye devam etmek yerine "kırar".

Bunu çocuklara açıklarken kullandığım bir benzetme, ironik olarak, çocuklardan daha iyi davranmasıdır! O durmuyor sonra bu gerekiyordu - o ne yaptığını bitirmeden hemen durur. (Bunu alırlar;))

Başka bir benzetme - Bir araba zaman yok geçmesi bir durdurma / verim / 'vermek yolu' işareti ve arkasında arabanızı bir yere yanında oturan ya ile bitirmek. Teknik olarak durduğunuzda hala ulaşmadınız. 'Yolculuğunuzda geçtiğiniz şeylere' dahil değildir.

Umarım bunların bazıları Pythonitos / Pythonitas'a açıklamada yardımcı olur!


Bu açıklama daha sezgiseldir. Teşekkürler
Fred

Çocuklar açıklama sadece komik!
Antony Hatchkins

1
Bir rujun üzerine ruj sürmeye çalışıyorsun. "Dur" ve "son" arasındaki ayrım saçmadır. 1'den 7'ye gidersem, 7'yi geçmedim. Başlangıç ​​ve bitiş pozisyonları için farklı sözleşmelere sahip olmak sadece bir Python kusuru. İnsan dilleri de dahil olmak üzere diğer dillerde, "X'den Y'ye", "X'den Y'ye" anlamına gelir. Python'da "X: Y", "X: Y-1" anlamına gelir. 9'dan 11'e kadar bir toplantınız varsa, insanlara bunun 9'dan 12'ye veya 8'den 11'e kadar olduğunu söyler misiniz?
bzip2

24

Özel aralıkların bazı faydaları vardır:

Birincisi, her bir öğe range(0,n)uzunluk listeleri için geçerli bir indekstir n.

Ayrıca range(0,n)bir uzunluğa sahiptir ndeğil, n+1hangi kapsayıcı aralığı olur.


18

Sıfır tabanlı indeksleme ve ile birlikte iyi çalışır len(). Örneğin, listede 10 öğe varsa x, bunlar 0-9 olarak numaralandırılır. range(len(x))size 0-9 verir.

Tabii ki, insanlar daha fazla Pythonic yapmak olduğunu söyleyecektir for item in xveya for index, item in enumerate(x)ziyade for i in range(len(x)).

Dilimleme de bu şekilde çalışır: foo[1:4]1-3 arasındaki öğelerdir foo(öğe 1'in sıfır tabanlı dizinleme nedeniyle aslında ikinci öğe olduğunu unutmayın). Tutarlılık için her ikisi de aynı şekilde çalışmalıdır.

Ben olarak düşünüyorum: "İlk numarası takip istediğiniz ilk sayı, yok . İstiyoruz" 1-10 istiyorsanız, ilk istemediğiniz sayı 11'dir, yani range(1, 11).

Belirli bir uygulamada hantal hale gelirse, bitiş dizinine ve çağrılarına 1 ekleyen küçük bir yardımcı işlev yazmak yeterince kolaydır range().


1
Dilimleme konusunda anlaşın. w = 'abc'; w[:] == w[0:len(w)]; w[:-1] == w[0:len(w)-1];
kevpie

def full_range(start,stop): return range(start,stop+1) ## helper function
nobar

belki numaralandırma for index, item in enumerate(x)karışıklığı önlemek için okumak gerekir
seans

@seans Teşekkürler, düzeltildi.
kindall

12

Ayrıca aralıkları bölmek için de yararlıdır; range(a,b)içine bölünebilir range(a, x)ve range(x, b)kapsayıcı aralıkla ya x-1ya yazabilirsiniz x+1. Aralıkları nadiren bölmeniz gerekse de, listeleri sık sık bölme eğilimindesiniz, bu da bir listeyi dilimlemenin l[a:b]b-th öğesini değil, a-öğesini içerir. Daha sonra rangeaynı özelliğe sahip olmak onu güzel tutar.


11

Aralığın uzunluğu üst değer eksi alt değerdir.

Gibi bir şeye çok benzer:

for (var i = 1; i < 11; i++) {
    //i goes from 1 to 10 in here
}

C tarzı bir dilde.

Ayrıca Ruby'nin menzili gibi:

1...11 #this is a range from 1 to 10

Bununla birlikte, Ruby birçok kez terminal değerini dahil etmek isteyeceğinizi kabul eder ve alternatif sözdizimini sunar:

1..10 #this is also a range from 1 to 10

17
Gah! Ruby kullanmayın, ama bunu tahmin edebilirsiniz 1..10vs 1...10kod okurken ayırt etmek zor olmak!
moinudin

6
@marcog - iki formun var olduğunu bildiğinizde
gözünüzün

11
Ruby'nin menzil operatörü mükemmel bir şekilde sezgisel. Uzun form size daha kısa bir sıra sunar. öksürük
Russell

4
@Russell, belki 1 ............ 20, 1..10 ile aynı aralığı vermelidir. Şimdi bu değişmeye değer bir sözdizimsel şeker olurdu. ;)
kevpie

4
@Russell Ekstra nokta, aralığın dışındaki son öğeyi
sıkıyor

5

Temel olarak python zamanlarını range(n)yineler n, bu da özel niteliktedir, bu yüzden yazdırılırken son değeri vermez, kapsayıcı değer veren bir işlev yaratabiliriz, bu da aralıkta belirtilen son değeri de basacağı anlamına gelir.

def main():
    for i in inclusive_range(25):
        print(i, sep=" ")


def inclusive_range(*args):
    numargs = len(args)
    if numargs == 0:
        raise TypeError("you need to write at least a value")
    elif numargs == 1:
        stop = args[0]
        start = 0
        step = 1
    elif numargs == 2:
        (start, stop) = args
        step = 1
    elif numargs == 3:
        (start, stop, step) = args
    else:
        raise TypeError("Inclusive range was expected at most 3 arguments,got {}".format(numargs))
    i = start
    while i <= stop:
        yield i
        i += step


if __name__ == "__main__":
    main()

4

Kodu düşünün

for i in range(10):
    print "You'll see this 10 times", i

Fikir, y-x(yukarıda gördüğünüz gibi) tekrarlayabileceğiniz bir uzunluk listesi almanızdır.

Menzil için python belgelerini okuyun - döngüsel yinelemeyi birincil kullanım alanını düşünürler.


1

Birçok durumda mantık yürütmek daha uygundur.

Temel olarak, bir aralığı startve arasındaki bir aralık olarak düşünebiliriz end. Eğer start <= end, aralarındaki aralığın uzunluğu end - start. Eğer lengerçekte uzunluğu olarak tanımlandı, sen olurdu:

len(range(start, end)) == start - end

Ancak, aralığın uzunluğunu ölçmek yerine aralığa dahil edilen tam sayıları sayıyoruz. Yukarıdaki özelliği doğru tutmak için uç noktalardan birini dahil etmeli ve diğerini hariç tutmalıyız.

stepParametre eklemek, bir uzunluk birimi eklemek gibidir. Bu durumda, beklersiniz

len(range(start, end, step)) == (start - end) / step

uzunluk için. Sayımı almak için sadece tamsayı bölmesini kullanırsınız.


Python'un tutarsızlığının bu savunmaları çok komik. İki sayı arasındaki aralığı isteseydim, neden aralık yerine farkı elde etmek için çıkartmayı kullanayım? Başlangıç ​​ve bitiş konumları için farklı indeksleme kuralları kullanmak tutarsızdır. 5-21 arası pozisyonlara ulaşmak için neden "5:22" yazmalısınız?
bzip2

Bu Python'un değil, genel olarak oldukça yaygın. C, Java, Ruby'de, adını siz verin
Arseny

Dizinleme için yaygın olduğunu söylemek istedim, diğer diller mutlaka aynı türden bir nesneye sahip değil
Arseny
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.