เหตุใดจึงไม่มีการดำเนินการช่วงทศนิยมในไลบรารีมาตรฐาน
range()
ในฐานะที่ทำให้เห็นได้ชัดโดยข้อความทั้งหมดที่นี่ไม่มีรุ่นจุดลอยของ ที่กล่าวว่าการละเว้นนั้นเหมาะสมถ้าเราพิจารณาว่าrange()
ฟังก์ชั่นมักใช้เป็นดัชนี (และแน่นอนนั่นหมายถึงตัวสร้างaccessor ) ดังนั้นเมื่อเราโทรrange(0,40)
เราจะบอกว่าเราต้องการค่า 40 ค่าเริ่มต้นที่ 0 ถึง 40 แต่ไม่รวมค่า 40
เมื่อเราพิจารณาว่าการสร้างดัชนีนั้นมีจำนวนมากเกี่ยวกับดัชนีเท่าที่เป็นค่าของพวกเขาการใช้การดำเนินการลอยตัวของrange()
ในไลบรารีมาตรฐานทำให้รู้สึกน้อยลง ตัวอย่างเช่นถ้าเราเรียกฟังก์ชันfrange(0, 10, 0.25)
นี้เราคาดว่าจะรวมทั้ง 0 และ 10 ไว้ด้วย แต่นั่นจะทำให้ได้เวกเตอร์ที่มี 41 ค่า
ดังนั้นfrange()
ฟังก์ชั่นที่ขึ้นอยู่กับการใช้งานมักจะแสดงพฤติกรรมที่เข้าใจง่าย มันมีค่ามากเกินไปตามการรับรู้จากมุมมองของการจัดทำดัชนีหรือไม่รวมจำนวนที่ควรส่งคืนจากมุมมองทางคณิตศาสตร์อย่างสมเหตุสมผล
กรณีใช้คณิตศาสตร์
ด้วยที่กล่าวว่าตามที่กล่าวไว้numpy.linspace()
ดำเนินการสร้างด้วยมุมมองทางคณิตศาสตร์อย่าง:
numpy.linspace(0, 10, 41)
array([ 0. , 0.25, 0.5 , 0.75, 1. , 1.25, 1.5 , 1.75,
2. , 2.25, 2.5 , 2.75, 3. , 3.25, 3.5 , 3.75,
4. , 4.25, 4.5 , 4.75, 5. , 5.25, 5.5 , 5.75,
6. , 6.25, 6.5 , 6.75, 7. , 7.25, 7.5 , 7.75,
8. , 8.25, 8.5 , 8.75, 9. , 9.25, 9.5 , 9.75, 10.
])
กรณีการใช้การจัดทำดัชนี
และสำหรับมุมมองการจัดทำดัชนีฉันได้เขียนแนวทางที่แตกต่างออกไปเล็กน้อยด้วยเวทมนตร์สตริงที่ซับซ้อนซึ่งทำให้เราสามารถระบุจำนวนทศนิยมได้
# Float range function - string formatting method
def frange_S (start, stop, skip = 1.0, decimals = 2):
for i in range(int(start / skip), int(stop / skip)):
yield float(("%0." + str(decimals) + "f") % (i * skip))
ในทำนองเดียวกันเรายังสามารถใช้round
ฟังก์ชันในตัวและระบุจำนวนทศนิยม:
# Float range function - rounding method
def frange_R (start, stop, skip = 1.0, decimals = 2):
for i in range(int(start / skip), int(stop / skip)):
yield round(i * skip, ndigits = decimals)
การเปรียบเทียบอย่างรวดเร็ว & ประสิทธิภาพ
แน่นอนจากการอภิปรายข้างต้นฟังก์ชั่นเหล่านี้มีกรณีการใช้งานที่ จำกัด อย่างไรก็ตามนี่เป็นการเปรียบเทียบด่วน:
def compare_methods (start, stop, skip):
string_test = frange_S(start, stop, skip)
round_test = frange_R(start, stop, skip)
for s, r in zip(string_test, round_test):
print(s, r)
compare_methods(-2, 10, 1/3)
ผลลัพธ์เหมือนกันสำหรับแต่ละ:
-2.0 -2.0
-1.67 -1.67
-1.33 -1.33
-1.0 -1.0
-0.67 -0.67
-0.33 -0.33
0.0 0.0
...
8.0 8.0
8.33 8.33
8.67 8.67
9.0 9.0
9.33 9.33
9.67 9.67
และกำหนดเวลา:
>>> import timeit
>>> setup = """
... def frange_s (start, stop, skip = 1.0, decimals = 2):
... for i in range(int(start / skip), int(stop / skip)):
... yield float(("%0." + str(decimals) + "f") % (i * skip))
... def frange_r (start, stop, skip = 1.0, decimals = 2):
... for i in range(int(start / skip), int(stop / skip)):
... yield round(i * skip, ndigits = decimals)
... start, stop, skip = -1, 8, 1/3
... """
>>> min(timeit.Timer('string_test = frange_s(start, stop, skip); [x for x in string_test]', setup=setup).repeat(30, 1000))
0.024284090992296115
>>> min(timeit.Timer('round_test = frange_r(start, stop, skip); [x for x in round_test]', setup=setup).repeat(30, 1000))
0.025324633985292166
ดูเหมือนว่าวิธีการจัดรูปแบบสตริงชนะโดยทรงผมในระบบของฉัน
ข้อ จำกัด
และสุดท้ายเป็นการสาธิตจุดจากการสนทนาข้างบนและข้อ จำกัด สุดท้าย:
# "Missing" the last value (10.0)
for x in frange_R(0, 10, 0.25):
print(x)
0.25
0.5
0.75
1.0
...
9.0
9.25
9.5
9.75
นอกจากนี้เมื่อskip
พารามิเตอร์ไม่สามารถหารด้วยstop
ค่าอาจมีช่องว่างที่หาวได้เนื่องจากปัญหาหลัง:
# Clearly we know that 10 - 9.43 is equal to 0.57
for x in frange_R(0, 10, 3/7):
print(x)
0.0
0.43
0.86
1.29
...
8.14
8.57
9.0
9.43
มีวิธีการแก้ไขปัญหานี้ แต่ในตอนท้ายของวันวิธีที่ดีที่สุดน่าจะเป็นการใช้ Numpy