web-dev-qa-db-ja.com

範囲(開始、終了)が終了を含まないのはなぜですか?

>>> range(1,11)

あなたにあげる

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

なぜ1-11じゃないの?

彼らはただ無作為にそのようにすることを決心したのですか、それとも私が見ていない値を持っていますか?

253
MetaGuru

range(0, 10)と等しい10個の要素を含む[0,1,2,3,4,5,6,7,8,9]を返すlen(range(0, 10))を呼び出すのがより一般的だからです。プログラマは0から始まるインデックスを好むことを忘れないでください。

また、次の一般的なコードスニペットを検討してください。

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

もしrange()が正確にlen(li)に上がったとしたら、これは問題になるでしょうか?プログラマは明示的に1を引く必要があります。これはプログラマがfor(int i = 0; i < 10; i++)よりfor(int i = 0; i <= 9; i++)を好む一般的な傾向にも従います。

1から始まるrangeを頻繁に呼び出している場合は、独自の関数を定義したいと思うかもしれません。

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

排他的範囲にはいくつかの利点があります。

まず第一に、range(0,n)の各項目は長さnのリストに対する有効なインデックスです。

またrange(0,n)の長さはnであり、包含範囲のようなn+1ではありません。

21
sepp2k

ここでは便利なアルゴリズムの説明がいくつかありますが、なぜそれがこのように機能するのかについての単純な「現実」の推論を追加することが役立つかもしれないと思います。

'range(1,10)'のようなものでは、パラメータのペアが "開始と終了"を表すと考えることから混乱が生じる可能性があります。

それは実際には開始と停止です。

さて、もしそれがwere"end"の値なら、はい、あなたはその数がシーケンスの最後のエントリとして含まれることを期待するかもしれません。しかし、それは「終わり」ではありません。

他の人が誤ってそのパラメータを "count"と呼ぶのは、 'range(n)'だけを使うのであれば、当然それは 'n'回繰り返すからです。 startパラメータを追加すると、このロジックは壊れます。

重要な点は、その名前を覚えておくことです: "stop"。これは、到達したときに反復がすぐに停止する時点であることを意味します。その点ではありません。

したがって、「開始」は実際に含まれる最初の値を表しますが、「停止」値に達すると、停止する前にその「処理」を継続するのではなく「中断」します。

私がこれを子供に説明するのに使った類推の一つは、皮肉なことに、それは子供より行動が良いということです!それはafterを停止しません - それはそれがしていたことを終了せずにすぐに停止します。 (彼らはこれを得ます)

別の例え - あなたが車を運転するとき、あなたはパス停止/降伏/「道を譲る」ことをやめてそれの隣りに、または後ろのどこかに座ってしまうあなたの車技術的には、停止してもまだ到達していません。それは「あなたがあなたの旅で渡したもの」には含まれていません。

そのうちのいくつかがPythonitos/Pythonitasへの説明に役立つことを願っています!

19
dingles

ゼロベースのインデックスとlen()との組み合わせでうまく機能します。たとえば、リストxに10個の項目がある場合、それらの項目には0から9までの番号が付けられます。 range(len(x))はあなたに0-9を与えます。

もちろん、for index, item in enumerate(x)ではなくfor item in xまたはfor i in range(len(x))を実行するほうがPythonicであることを人々は言うでしょう。

スライスも同様に機能します。foo[1:4]fooの項目1〜3です(項目1は0から始まるインデックスのため、実際には2番目の項目です)。一貫性を保つために、両者は同じように機能するべきです。

私はそれを次のように考えています:「あなたが欲しい最初の数、あなたが欲しい最初の数が続く」あなたが1-10が欲しいなら、あなたが望まない最初の数は11です、それでそれはrange(1, 11)です。

それが特定のアプリケーションで面倒になるなら、終わりのインデックスに1を加えてrange()を呼び出す小さなヘルパー関数を書くことは十分簡単です。

18
kindall

範囲を分割するのにも便利です。 range(a,b)range(a, x)range(x, b)に分けることができますが、包括的な範囲ではx-1x+1のどちらかを書くでしょう。範囲を分割する必要はめったにありませんが、リストを頻繁に分割する傾向があります。これは、リストをスライスするのにl[a:b]がa番目の要素を含み、b番目は含まない理由の1つです。それからrangeは同じ性質を持ち、それはうまく一致しています。

12
xuanji

範囲の長さは、上の値から下の値を引いたものです。

これは次のようなものに非常に似ています。

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

cスタイルの言語で。

Rubyの範囲も好きです:

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

しかし、Rubyは端末の値を含めることを何度も望むことを認識しており、代替構文を提供しています。

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

基本的にpythonではrange(n)n回繰り返しますが、これはそれが出力されるとき最後の値を与えない排他的な性質なので、範囲内で言及される最後の値も出力することを意味します。

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 __== "__main__":
    main()
4
Ashish Dixit

コードを考えてください

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

アイデアは、あなたが長さy-xのリストを得るということです、あなたは(あなたが上で見たように)あなたが繰り返すことができる。

範囲については Pythonドキュメント を読んでください - 彼らはforループの繰り返しを主なユースケースと考えています。

4
Robert

多くの場合、推論するほうがより便利です。

基本的に、範囲はstartendの間の間隔と考えることができます。 start <= endの場合、それらの間の間隔の長さはend - startです。 lenが実際に長さとして定義されている場合は、次のようになります。

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

ただし、間隔の長さを測定する代わりに、範囲に含まれる整数を数えます。上記の特性を真に保つためには、一方の端点を含め、他方の端点を除外する必要があります。

stepパラメータを追加することは、長さの単位を導入することに似ています。その場合は、あなたが期待するだろう

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

長さのために。カウントを取得するには、単に整数除算を使用します。

1
Arseny