57

免責事項: andの上限引数が排他的かどうか、またはこれらの関数の使用方法を尋ねているわけではありませんstopslice()range()

関数と関数の呼び出し、rangeおよびsliceスライス表記[start:stop]はすべて、整数のセットを参照します。

range([start], stop[, step])
slice([start], stop[, step])

これらすべてにおいて、stop整数は除外されます。

なぜ言語がこのように設計されているのか疑問に思っています。

が0または省略さstopれた場合、表現された整数セットの要素の数と等しくするのですか?start

それは持っていることですか:

for i in range(start, stop):

次の C コードのように見えますか?

for (i = start ; i < stop; i++) {
4

6 に答える 6

51

ドキュメントは、これがいくつかの有用なプロパティを持っていることを意味します:

word[:2]    # The first two characters
word[2:]    # Everything except the first two characters

スライス操作の便利な不変式を次に示します: s[:i] + s[i:]equals s

非負のインデックスの場合、両方が境界内にある場合、スライスの長さはインデックスの差になります。たとえば、 の長さはword[1:3]です2

一貫性のために、範囲関数は同じように機能すると想定できると思います。

于 2012-07-06T14:59:44.627 に答える
28

一部の Google+ ユーザーの意見は次のとおりです。

[…] 半開きの間隔の優雅さに心揺さぶられました。特に、2 つのスライスが隣接している場合、最初のスライスの終了インデックスが 2 番目のスライスの開始インデックスであるという不変条件は、無視するには美しすぎます。たとえば、文字列をインデックス i と j で 3 つの部分に分割するとします。部分は a[:i]、a[i:j]、a[j:] になります。

Google+ は閉鎖されているため、リンクは機能しません。ネタバレ注意:それはグイド・ヴァン・ロッサムでした。

于 2014-01-31T13:59:43.537 に答える
11

この質問には少し遅れていますが、これは理由に答えようとします-質問の部分:

その理由の 1 つは、メモリのアドレス指定時にゼロベースのインデックス作成/オフセットを使用しているためです。

最も簡単な例は配列です。「6 項目の配列」は、6 つのデータ項目を格納する場所と考えてください。この配列の開始位置がメモリ アドレス 100 にある場合、データ (たとえば 6 文字の「apple\0」) は次のように格納されます。

memory/
array      contains
location   data
 100   ->   'a'
 101   ->   'p'
 102   ->   'p'
 103   ->   'l'
 104   ->   'e'
 105   ->   '\0'

したがって、6 項目の場合、インデックスは 100 から 105 になります。アドレスはbase + offsetを使用して生成されるため、最初の項目はベース メモリ位置100 +オフセット0 (つまり、100 + 0) にあり、2 番目は 100 + 1 にあり、3 番目は100 + 2、...、100 + 5 が最後の位置になるまで。

forこれが、ゼロベースのインデックスを使用する主な理由であり、C のループなどの言語構造につながります。

for (int i = 0; i < LIMIT; i++)

またはPythonで:

for i in range(LIMIT):

ポインターをより直接的に扱う C のような言語でプログラミングする場合、またはアセンブリーをより直接的に扱う場合、この base+offset スキームはより明白になります。

上記の理由により、多くの言語構造は自動的にstartからlength-1までの範囲を使用します。

ウィキペディアのゼロベースの番号付けに関するこの記事と、Software Engineering SE からのこの質問も興味深いかもしれません。

たとえばCでは、配列があり、配列の(ベース)アドレスを取得してそれに追加することと実際に同等であるため、ar添字を付ける場合=>これにより、配列の内容を出力するこのようなコードが簡単に表示されますベース + オフセット アプローチ:ar[3]ar3*(ar+3)

for(i = 0; i < 5; i++)
   printf("%c\n", *(ar + i));

本当に同等

for(i = 0; i < 5; i++)
   printf("%c\n", ar[i]);
于 2012-07-06T18:04:41.747 に答える
9

排他的な上限がより健全なアプローチであるもう 1 つの理由を次に示します。

リスト内のアイテムのサブシーケンスに何らかの変換を適用する関数を書きたいとします。あなたが提案するように間隔が包括的な上限を使用する場合、単純に次のように記述してみてください。

def apply_range_bad(lst, transform, start, end):
     """Applies a transform on the elements of a list in the range [start, end]"""
     left = lst[0 : start-1]
     middle = lst[start : end]
     right = lst[end+1 :]
     return left + [transform(i) for i in middle] + right

一見、これは簡単で正しいように見えますが、残念ながら微妙に間違っています。

次の場合はどうなりますか。

  • start == 0
  • end == 0
  • end < 0

? 一般に、考慮すべきさらに多くの境界ケースが存在する可能性があります。そのすべてについて考えて時間を無駄にしたいのは誰ですか?(これらの問題は、包括的な下限と上限を使用することによって、空の interval を表現する固有の方法がないために発生します。)

代わりに、上限が排他的であるモデルを使用することにより、リストを個別のスライスに分割することがよりシンプルでエレガントになり、エラーが発生しにくくなります:

def apply_range_good(lst, transform, start, end):
     """Applies a transform on the elements of a list in the range [start, end)"""
     left = lst[0:start]
     middle = lst[start:end]
     right = lst[end:]
     return left + [transform(i) for i in middle] + right

apply_range_good(は変換されないことに注意してくださいlst[end]。これも排他的な上限として扱わendれます。包括的な上限を使用するようにしようとすると、前述の問題がいくつか残っています。道徳的には、包括的な上限は通常面倒です。 )

(主に、別のスクリプト言語での包括的な上限に関する私の古い投稿から改作されています。)

于 2015-01-24T21:42:34.930 に答える