Ачааллж байна...
Өмнөх хичээлд бид range(5)-ыг "0-ээс эхлээд 5 хүрэхгүй таван тоо үйлдвэрлэдэг багаж" гээд, гүн асуултуудыг нь энэ хичээлд амлаад орхисон. Одоо өрөө барагдуулъя — учир нь range() бол for loop-ын байнгын хань бөгөөд түүнийг гадарлаж биш, эзэмшиж байж loop таны гарт жинхэнэ уян багаж болно. Өнөөдөр гурван асуултад бүрэн хариулна: яагаад 0-ээс эхэлдэг юм бэ? Өөр тооноос эхлүүлж болох уу? Нэг нэгээр биш — хоёр хоёроор, тав таваар "үсэргэж" тоолуулж болох уу? Гурвууланд нь хариулт бий, тэгээд гурвуулаа range-ийн нэг л зарчимд багтдаг: range нь нэг, хоёр, эсвэл гурван орц авч чаддаг бөгөөд орцын тоо нь юу асууж байгааг чинь Python-д хэлж өгдөг юм.
Мөн энэ хичээлд бид программчлалын түүхэн дэх хамгийн алдартай "нэгээр зөрөх" гэдэг үзэгдэлтэй нүүр тулна. Range-тэй ажиллаж буй хүн бүр — эхлэгч ч, ахмад программист ч — амьдралдаа олон удаа "яг нэгээр дутуу" эсвэл "яг нэгээр илүү" давталт хийлгэчихсэн байдгаа олж хардаг. Энэ нь тэнэглэлийн шинж огт биш — хилийн тооллолд хүний тархи угаасаа бүдэрдэг (аравны баярын хурдны замын хажуугийн шонг тоолж үзээрэй: 10 метр тутамд шон байвал 100 метрт хэдэн шон бэ? 10 биш — 11!). Python-ы range энэ бүдрэлтийг нэг тогтсон дүрмээр намдаадаг: дүрмээ ойлгочихвол зөрөө таамаглагдахуйц, шалгагдахуйц болдог.
range()-ийг дугаарын тасалбар хэвлэдэг машин гэж төсөөлөөрэй — банк, эмнэлгийн үүдэнд байдаг тэр машин. Машиныг тохируулахдаа гурван зүйл зааж өгч болно: аль дугаараас эхлэхийг (эхлэл), аль дугаарт хүрмэгц зогсохыг (зогсоол), дугаар хооронд хэдээр үсрэхийг (алхам). For loop бол тэр машины дэргэд зогсоод тасалбар бүрийг нэг нэгээр нь авч, тасалбар бүрд биеэ нэг биелүүлдэг ажилтан: i хайрцганд тойрог бүрд дараагийн тасалбарын дугаар очдог гэсэн үг. Машины нэг онцгой зан бий, бүх будлианы эх нь энэ: зогсоолын дугаартай тасалбар хэзээ ч хэвлэгддэггүй — машин тэр дугаарт "хүрмэгц" зогсдог болохоос түүнийг "хэвлээд" зогсдоггүй. "10 хүртэл" гэж тохируулбал сүүлчийн тасалбар 9 байна. Энэ занг механик цээжлэхийн оронд ганц дүрмээр тогтооё: эхлэл — орно, зогсоол — орохгүй. Программистууд үүнийг "хагас нээлттэй завсар" гэдэг: эхлэл талдаа хаалттай (багтана), төгсгөл талдаа нээлттэй (багтахгүй).
Хамгийн их хэрэглэгддэг хэлбэрээс нь эхэлье — эхлэл ба зогсоолоо хоёуланг нь зааж өгөх:
python
for i in range(1, 6):
print(i)Үр дүн:
1
2
3
4
5Өмнөх хичээлийн "нуугдаж тоглох" жишээн дээр 1-10 хэвлэхийн тулд бид toolol + 1 гэсэн тохируулга хийж байсныг санаж байна уу — одоо тэр ажил хэрэггүй боллоо: range(1, 6) нь "1-ээс эхэл, 6-д хүрмэгц зогс" гэсэн шууд заавар. Хоёр орцыг таслалаар зааглаж өгнө: эхний орц — эхлэл, хоёр дахь — зогсоол. Дүрмээ шалгая: эхлэл (1) — орсон, зогсоол (6) — ороогүй, сүүлчийн тасалбар 5. "Тэгвэл 1-ээс 10 хэвлэе гэвэл?" — range(1, 11): зогсоолоо хүсч буй сүүлчийн тооноосоо нэгээр их тавина. Энэ "нэгээр их зогсоол" эхэндээ эвгүй санагдана — тэр нь хэвийн. Тайтгарал нь: энэ дүрэмд нэг далд бэлэг бий — давталтын тоог зогсоолоос эхлэлийг хасаад шууд гаргаж болдог: range(1, 6) нь 6 - 1 = 5 тойрог, range(1, 11) нь 10 тойрог. Хэрэв зогсоол "багтдаг" байсан бол давталтын тоо нь "хасаад нэг нэмэх" гэсэн бүдрүүлэгч томьёотой болох байсан юм — хагас нээлттэй завсрын жинхэнэ учир нь энэ мэт тооцоог цэвэрхэн болгодогт оршдог.
Одоо нэг орцтой хэлбэрийнхээ нууцыг тайлъя. range(5) гэдэг нь үнэндээ range(0, 5)-ын товчлол юм: эхлэлээ хэлэхгүй бол Python "тэгвэл 0-ээс" гэж өөрөө таамагладаг. Тэгэхээр асуулт нь "range яагаад 0-ээс эхэлдэг вэ" биш — "Python яагаад анхдагч эхлэлээ 0 гэж сонгосон бэ" юм. Хоёр шалтгаан хэлье. Нэгд — практик: 0-ээс эхэлсэн, N дээр зогссон завсар нь яг N ширхэг тоо агуулдаг (range(5) — таван тоо) тул "N удаа давт" гэсэн хамгийн түгээмэл хэрэгцээнд хамгийн богино бичиглэл нь болдог. Хоёрт — гүн: программчлалын ертөнцөд байрлалыг "хэд дэх вэ" гэж биш "эхнээс хэр зайтай вэ" гэж тоолдог уламжлал бий: эхний зүйл эхлэлээс 0 зайтай тул дугаар нь 0. Энэ санаа одоо жаахан хийсвэр сонсогдож болно — гэхдээ Бүлэг 9-д string-ийн үсгүүдийг, Бүлэг 10-д list-ийн гишүүдийг дугаарлаж эхлэхээр "эхний зүйл 0 дугаартай" дүрэм дахин гарч ирээд, тэнд бүрэн биетэй учир нь тайлбарлагдана. Өнөөдрийн хэмжээнд: range(N) = range(0, N) = N удаа давтах хамгийн богино бичиг гэж тогтооход хангалттай.
Хоёр дахь жишээ: гурав дахь орц — алхам. Тасалбарын машиныхаа "үсрэлтийг" тохируулъя. 2-ын үржвэрийн хүснэгтийг өмнөх хичээлд 2 * i гэж бодуулж байсан — одоо шууд алхаад очъё:
python
for i in range(2, 11, 2):
print(i)Үр дүн:
2
4
6
8
10Гурав дахь орц (сүүлчийн 2) бол алхам: "тасалбар бүрийн дараа хоёроор үсэр." Уншлага нь: "2-оос эхэл, 11-д хүрмэгц зогс, хоёр хоёроор яв" — 2, 4, 6, 8, 10. Зогсоолоо 11 гэж тавьснаа ажигла: 10-ыг багтаахын тулд зогсоол нь 10-аас цаана байх ёстой (11 ч бай, 12 ч бай — 10-ыг давсан л бол болно; гэхдээ "сүүлчийн хүссэн тоо дээр нэгийг нэм" гэсэн танил дадлаа баримталбал андуурал багатай). Алхам заагаагүй үед Python "тэгвэл 1-ээр" гэж таамагладаг — өөрөөр хэлбэл жирийн range(1, 6) маань үнэндээ range(1, 6, 1) юм. Ингээд гурван хэлбэрийн бүрэн зураг: нэг орц — зогсоол л заана (эхлэл 0, алхам 1); хоёр орц — эхлэл ба зогсоол (алхам 1); гурван орц — эхлэл, зогсоол, алхам. Орц нэмэгдэх тусам эрх чөлөө нэмэгддэг — гэхдээ дараалал нь үргэлж тогтмол: эхлэл, зогсоол, алхам.
Гурав дахь жишээ: ухраах алхам. Алхам сөрөг тоо ч байж болно — тэгвэл машин буурах дугаартай тасалбар хэвлэнэ. Пуужингийн хөөргөлтийн тоолол:
python
print("Хөөргөлтөд бэлдэв!")
for i in range(5, 0, -1):
print(i)
print("Хөөрлөө!")Үр дүн:
Хөөргөлтөд бэлдэв!
5
4
3
2
1
Хөөрлөө!Уншлага: "5-аас эхэл, 0-д хүрмэгц зогс, нэг нэгээр буу." Дүрэм маань ухралтад ч хүчинтэй: эхлэл (5) орсон, зогсоол (0) ороогүй — тиймээс тоолол 1 дээр дуусаад "Хөөрлөө!" гарсан нь яг санасан хэрэг. Анхаарах зүйл: ухрахдаа эхлэл нь зогсоолоосоо их байх ёстой (5-аас 0 руу) — range(0, 5, -1) гэж бичвэл машин "0-ээс доошилж 5-д хүрэх" гэсэн биелэшгүй даалгавар аваад нэг ч тасалбар хэвлэхгүй: loop-ын бие тэг удаа биелж, программ алдаагүй, чимээгүй өнгөрнө (энэ "чимээгүй хоосон" зан алдаануудын хэсэгт эргэж ирнэ).
Дөрөв дэх жишээ: хуримтлуулагчтай хамт — сондгойн нийлбэр. Өмнөх хичээлийн хуримтлуулагч хэв маягийг шинэ range-тэйгээ хослуулъя: 1-ээс 9 хүртэлх сондгой тоонуудын нийлбэр:
python
niilber = 0
for i in range(1, 10, 2):
niilber = niilber + i
print(niilber)Үр дүн:
25Мөшгье — хоёр хайрцгаа зэрэг хөтөлнө. range(1, 10, 2) ямар тасалбарууд хэвлэхийг эхлээд өөртөө хэлье: 1, 3, 5, 7, 9 (1-ээс, хоёроор үсэрч, 10-д хүрэлгүй). Тойрог 1: i = 1, niilber = 0 + 1 = 1. Тойрог 2: i = 3, niilber = 4. Тойрог 3: i = 5, niilber = 9. Тойрог 4: i = 7, niilber = 16. Тойрог 5: i = 9, niilber = 25. Тоон үйлдлүүдийн хичээлд "тэгш тоо гэдэг нь % 2 == 0" гэж үзсэнийг санаж байвал — сондгойг шүүх хоёр дахь зам энд гарч ирлээ: алхамтай range нь шүүлтийг "тоолол дотроо" шингээж чаддаг. Аль аргыг хэзээ сонгохоо цаг нь болохоор ялгаж сурна — өнөөдөртөө "нэг бодлогод олон зам байдаг" гэдгийг үзсэн нь өөрөө үнэ цэнэ.
Хүсвэл туршаад үзээрэй: өөрийн төрсөн оноос эхлээд энэ он хүртэл он оноор хэвлэдэг loop бичээрэй — range(терсен_он, 2027) маягаар (зогсоолын нэгээр ихийг бүү мартаарай). Сонирхвол ухраах тооллоо 10-аас, хоёр хоёроор буулгаж туршаад, эхлэл-зогсоол-алхам гурваа өөрөө тааруулж үзээрэй.
Алдаа 1: Нэгээр зөрөх — зогсоолыг "багтана" гэж бодох. Амласан гол зочин:
python
print("1-ээс 10 хүртэл тоолъё:")
for i in range(1, 10):
print(i)Үр дүн: 1-ээс 9 хүртэл л хэвлэгдэнэ — 10 алга. Алдааны мэдэгдэлгүй, программ "ажиллаад л" — гэхдээ амласан 10 нь ирсэнгүй: зогсоол ордоггүйг мартаж range(1, 10) гэж бичжээ, зөв нь range(1, 11). Энэ бол өмнө дурдсан off-by-one буюу "нэгээр зөрөх" алдааны range-хувилбар — дэлхийн бүх хэл дээрх, бүх түвшний программистын хамгийн олон гаргадаг алдааны нэг. Хамгаалах дадал нь механик бөгөөд үр дүнтэй: range бичсэн бүрдээ "эхний тасалбар хэд вэ, сүүлчийнх нь хэд вэ" гэж хоёр асуултаар өөрийгөө шалга — толгойдоо эхний болон сүүлчийн тойргоо нэг "эргүүлж" үзэх нь секунд зарцуулж, олон минутын эргэлзээнээс хамгаалдаг. Мөн урвуу хувилбар нь бий: "10 хүртэл" гэж бодоод range(1, 11)-ийн оронд range(11) бичвэл 0-оос эхэлчихнэ — эхлэлээ мартсан "нэгээр зөрөх"-ийн ах дүү.
Алдаа 2: Хоосон range — чимээгүй тэг тойрог. Гурав дахь жишээн дээр сөхсөн занг бүтнээр нь харъя:
python
for i in range(5, 5):
print(i)
print("Дууслаа")Үр дүн:
ДууслааLoop-ын бие нэг ч удаа биелсэнгүй: "5-аас эхэл, 5-д хүрмэгц зогс" — гэтэл эхлэл нь аль хэдийн зогсоол дээрээ байгаа тул машин ганц ч тасалбар хэвлэлгүй хаагдав. Range-ийн энэ зан алдаа биш — зарим бодлогод яг хэрэгтэй, зөв зан (жишээ нь "үлдсэн ажлын тоогоор давт" гэхэд үлдсэн нь 0 бол давтахгүй байх нь зөв). Гэхдээ санамсаргүй тохиолдвол "loop минь яагаад юу ч хийхгүй байгаа юм бэ" гэсэн чимээгүй эргэлзээ төрүүлдэг. Оношлогооны дүрэм: loop чинь "огт ажиллахгүй" мэт байвал range-ийнхээ орцуудыг хэвлэж шалга — эхлэл нь зогсоолоосоо их буюу тэнцүү (эсвэл ухралтад: бага буюу тэнцүү) болчихсон байх нь хамгийн магадлалт шалтгаан. Ялангуяа орцууд нь variable байх үед (range(a, b)) энэ байдал амархан "нуугддаг".
Алдаа 3: Орцын дарааллыг сольж тавих. Гурван орцын дараалал мартагдахаар:
python
for i in range(2, 20, 10):
print(i)Программист "10-аас эхлээд 20 хүртэл хоёроор" гэсэн санаатай байсан ч range(10, 20, 2) гэхийн оронд орцуудаа хольж range(2, 20, 10) гэж бичжээ — үр дүн нь 2 ба 12 гэсэн огт өөр хоёр тоо. Алдааны мэдэгдэл байхгүй: гурван тоо нь бүгд "хууль ёсны" тул Python дуулгавартай биелүүлнэ — зөвхөн таны санаснаас өөр даалгаврыг. Хамгаалалт нь нэг л зүйл: дарааллын тогтмол дуудлага — эхлэл, зогсоол, алхам — гурван үгээ мөр бичихдээ дотроо хэлж яв. Мөн туршилтын нэг хялбар арга: эргэлзсэн range-ээ loop-д хийхийн өмнө print(list(range(2, 20, 10))) гэж бичвэл Python тасалбаруудыг нь бүгдийг нь жагсааж үзүүлдэг (list гэдэг шинэ үг энд гүйцэтгэх үүрэг нь "жагсааж харуул" төдий — жинхэнэ танилцах ёслол нь Бүлэг 10-т) — range-ийн доторхыг "нүдээр үзэх" энэ арга таамаг таалтын оронд баримт өгнө.
range гурван хэлбэртэй: range(зогсоол) — 0-ээс; range(эхлэл, зогсоол); range(эхлэл, зогсоол, алхам) — дараалал нь үргэлж эхлэл, зогсоол, алхам.
Алтан дүрэм: эхлэл — орно, зогсоол — орохгүй; тиймээс "N хүртэл багтаана" гэвэл зогсоолоо N+1 тавь, давталтын тоо нь зогсоол хасах эхлэл.
0-ээс эхэлдэг нь товчлолын учиртай: range(N) = range(0, N) = яг N тойрог; "эхнээс хэр зайтай" гэсэн дугаарлалтын гүн учир нь Бүлэг 9, 10-т биежинэ.
Сөрөг алхам ухраана (range(5, 0, -1) — 5, 4, 3, 2, 1); эхлэл нь зогсоолдоо "хүрчихсэн" range тэг тойрог эргэдэг — алдаа биш, чимээгүй зан.
Нэгээр зөрөхөөс хамгаалах дадал: range бичих бүрдээ эхний ба сүүлчийн тасалбараа толгойдоо эргүүлж шалга; эргэлзвэл print(list(range(...))) — баримтаар үз.
Дараагийн хичээлд давталтын хоёр дахь, огт өөр зантай төрөлтэй танилцана: while loop — "N удаа давт" биш "нөхцөл биелж байх зуур давт" гэдэг хэлбэр. Тоо таах тоглоомоо "онотол нь таалгадаг" болгох түлхүүр яг тэнд бий — мөн программист бүрийн амьдралдаа нэг удаа хийдэг "төгсгөлгүй давталт" гэдэг алдартай осолтой аюулгүй орчинд танилцана.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.