การประเมินความเกียจคร้าน

Nov 28 2022
กระตือรือร้น vs เข้มงวด vs ไม่เข้มงวด vs ขี้เกียจ — ทั้งหมดหมายความว่าอย่างไร? แทะ: อาหารชิ้นเล็ก ๆ ที่ถูกกัด ในการคำนวณ: ข้อมูลครึ่งไบต์

กระตือรือร้น vs เข้มงวด vs ไม่เข้มงวด vs ขี้เกียจ — ทั้งหมดหมายความว่าอย่างไร?

ฉันอิจฉาโคอาล่าสำหรับความสามารถของพวกเขาที่จะสบายอย่างที่สุดในสถานที่ที่น่าอึดอัดที่สุด ภาพถ่ายโดย David Clode บน Unsplash

แทะ: อาหารชิ้นเล็ก ๆ ที่ถูกกัด ในการคำนวณ: ข้อมูลครึ่งไบต์ ทุกคำที่แทะจะอธิบายแนวคิดด้านวิทยาการคอมพิวเตอร์หรือวิศวกรรมซอฟต์แวร์ได้ภายในห้านาที

ทุกภาษาโปรแกรมจำเป็นต้องเลือกว่าจะประเมินนิพจน์ในลำดับใด เกือบทั้งหมดใช้การประเมินอย่างเข้มงวด : ก่อนประเมินนิพจน์ นิพจน์ย่อยทั้งหมดจะได้รับการประเมินก่อน ตัวอย่างเช่น อาร์กิวเมนต์ของฟังก์ชันจะถูกประเมินก่อนฟังก์ชัน หลายคนใช้การประเมินความกระตือรือร้นเป็นคำพ้องความหมายสำหรับการประเมินอย่างเข้มงวด

ส่วนน้อยที่เด่นชัดที่สุดคือ Haskell ใช้ การประเมิน แบบไม่เข้มงวด การประเมินแบบไม่เข้มงวดถูกกำหนดโดยสิ่งที่ไม่ใช่: กลยุทธ์การประเมินใดๆ ที่ไม่ประเมินนิพจน์ย่อยทั้งหมดก่อนหรือไม่ประเมินเลย ถือว่าไม่เข้มงวด การประเมินแบบขี้เกียจเป็นกลยุทธ์เฉพาะของการประเมินแบบไม่เข้มงวด

มาลองขจัดความสับสนด้วยการประเมินแบบขี้เกียจ

การประเมินผลที่เข้มงวด

เช่นเดียวกับภาษาส่วนใหญ่ Python ใช้การประเมินอย่างเข้มงวดสำหรับแอปพลิเคชันฟังก์ชัน:

def log(value):
  if log_level == INFO:
    print(value)

log(42 + 33)

วิธีง่ายๆ ในการระบุการประเมินที่เข้มงวดคือการจินตนาการว่าจะเกิดอะไรขึ้นหากนิพจน์ย่อยหนึ่งเข้าสู่การวนซ้ำไม่สิ้นสุด หากนิพจน์ระดับบนสุดไม่เคยได้รับการประเมิน แสดงว่าการประเมินนั้นเข้มงวด

การประเมินแบบเข้มงวดไม่ได้จำกัดว่านิพจน์ย่อยของลำดับใดจะได้รับการประเมิน เพียงแต่ว่าทั้งหมดจะได้รับการประเมินก่อนนิพจน์ระดับบนสุด สามารถเรียงจากซ้ายไปขวาได้เหมือนใน Python ขวาไปซ้ายเหมือนใน OCaml หรือไม่ได้กำหนดเช่นใน C.

การประเมินผลที่ไม่เข้มงวด

การประเมินแบบไม่เคร่งครัดไม่ใช่เรื่องผิดปกติอย่างที่อาจปรากฏในตอนแรกif...else...ตัวอย่างเช่น การประเมินแบบไม่เข้มงวด

ลองใช้เคล็ดลับของเราเพื่อค้นหาการประเมินที่เข้มงวด โปรแกรม Python ต่อไปนี้ยุติลงแม้ว่าelseสาขาจะวนซ้ำตลอดไป:

if True:
  print("I am True")
else:
  while True:
    print("I never terminate")

การดำเนินการบูลีนลัดวงจรก็ไม่เข้มงวดเช่นกัน orนิพจน์ต่อไปนี้ สิ้นสุดลง

True or infinite_loop()

นั่นเป็นสิ่งที่ดีสำหรับบิวด์อิน แต่ถ้าคุณต้องการบางสิ่งที่ถูกต้อง...

เราสามารถจำลองการประเมินที่ไม่เข้มงวดโดยใช้thunks :ฟังก์ชันโดยไม่มีข้อโต้แย้ง Thunks เลื่อนการประเมินจนกว่าจะจำเป็น

def log(value_thunk):
  if log_level == INFO:
    print(value_thunk())

log(lambda: 42 + 33)

ขี้เกียจประเมิน

ปัญหาที่น่ารำคาญของ thunks คือพวกมันถูกประเมินใหม่ทุกครั้งที่ถูกเรียก

def log(value_thunk):
  if log_level == INFO:
    print(value_thunk())
    send_to_log_aggregator(value_thunk())

  • ไม่เคยทำงานที่ไม่จำเป็น โดยเลื่อนการประเมินจนกว่าจะจำเป็น
  • มันไม่เคยทำงานซ้ำ โดยแคชผลลัพธ์แรก

ภาษาโปรแกรมเชิงฟังก์ชัน Haskell หลีกหนีจากการใช้การประเมินแบบขี้เกียจโดยค่าเริ่มต้นเพราะมันบริสุทธิ์: ฟังก์ชันของ Haskell ไม่มีผลข้างเคียง

ภาษาที่เข้มงวดเช่น OCaml, F# และ C# รองรับการประเมินแบบ Lazy โดยใช้lazyคีย์เวิร์ดหรือLazyวัตถุ

นี่คือตัวอย่างการใช้งานLazyวัตถุใน Python:

class Lazy:
    def __init__(self, thunk):
        self._thunk = thunk
        self._is_cached = False
        self._value = None

    @property
    def value(self):
        if not self._is_cached:
            self._value = self._thunk()
            self._is_cached = True
        return self._value

การประเมินอย่างเข้มงวดเป็นเรื่องปกติมากกว่าเพราะเข้าใจได้ง่ายกว่าและแก้ไขจุดบกพร่องได้ง่ายกว่า ที่กล่าวว่าการประเมินที่ไม่เข้มงวดนั้นขาดไม่ได้ในบางสถานการณ์

Power Seriousของ Doug McIlroy แสดงให้เห็นสิ่งนี้อย่างสวยงาม: การใช้งาน 10 บรรทัดที่สมบูรณ์ของชุดพลังงานที่ไม่มีที่สิ้นสุดใน Haskell

นี่คือหนึ่งบรรทัดจากนั้น:

series f = f : repeat 0

แสดง ถึงรายการ ที่series 13ไม่มีที่สิ้นสุด [13, 0, 0, 0,...]แต่เนื่องจากมันขี้เกียจ องค์ประกอบจึงถูกคำนวณเมื่อจำเป็นเท่านั้น วิธีหนึ่งในการขอองค์ประกอบคือการใช้ฟังก์ชันtake nซึ่งรับnองค์ประกอบจากด้านหน้าของรายการ กล่าวอีกนัยหนึ่งให้take 4 (series 13)ผลตอบแทน[13, 0, 0, 0]

ณ จุดนี้ คุณอาจคิดว่ารายการขี้เกียจเป็นเพียงตัววนซ้ำ เช่นเดียวกับรายการขี้เกียจ ตัววนซ้ำจะถูกคำนวณตามความต้องการและสามารถใช้เพื่อแสดงโครงสร้างข้อมูลที่ไม่สิ้นสุด นี่คือseriesตัวสร้าง Python:

def series(f: int) -> Iterable[int]:
    yield f
    while True:
        yield 0

myList = series 10 --list thunk is created
some = take 5 myList --eval first 5 elements
somemore = take 20 myList --eval 15 more elements

กลับสู่พลังที่จริงจัง Doug McIlroy ใช้รายการขี้เกียจเพื่อแสดงค่าสัมประสิทธิ์ของอนุกรมกำลังที่ไม่มีที่สิ้นสุด กล่าวอีกนัยหนึ่ง รายการ[1, 2, 3, 4, 0, ...]แสดงถึงอนุกรมกำลัง 1 + 2 + 3² + 4³ + ...

นี่คือการเพิ่มสองชุดพลังงาน:

(f:ft) + (g:gt) = f+g : ft+gt

เดี๋ยวก่อน — นิยามแบบเรียกซ้ำโดยไม่มีกรณีฐาน? นั่นเป็นไปได้ด้วยการประเมินแบบขี้เกียจ เมื่อต้องการองค์ประกอบแรกของผลรวมของสองชุด นิพจน์f+gจะต้องการfและgซึ่งทริกเกอร์การประเมินองค์ประกอบแรกของรายการซ้ายและขวาผ่านการจับคู่รูปแบบขี้เกียจ(f:ft)และ (g:gt)การประเมินเพิ่มเติมft+gtจะล่าช้าออกไปจนกว่าจะต้องมีองค์ประกอบเพิ่มเติม กระบวนการนี้จะเกิดขึ้นซ้ำเมื่อมีการขอองค์ประกอบที่สอง เป็นต้น

รายการที่ไม่สิ้นสุดแบบ Lazy ทำให้ทุกอย่างง่ายขึ้น: ไม่จำเป็นต้องตรวจสอบว่ารายการสิ้นสุดเมื่อใดเพราะไม่ได้ตรวจสอบ และไม่จำเป็นต้องใช้กรณีฐานแบบเรียกซ้ำเนื่องจากเราจะเรียกซ้ำเมื่อจำเป็นเท่านั้น

สรุป

แทะนี้กล่าวถึงคำศัพท์มากมาย

ฉันแบ่งกลยุทธ์การประเมิน ออก เป็นสองประเภท: แบบเข้มงวดและไม่เข้มงวด

ในการประเมินอย่างเข้มงวด นิพจน์ย่อยของนิพจน์ทั้งหมดจะได้รับการประเมินก่อน การประเมินแบบไม่เคร่งครัดครอบคลุมกลยุทธ์ทั้งหมดที่ทำอย่างอื่น รวมทั้งไม่ประเมินข้อโต้แย้งบางข้อเลย

การประเมินแบบเข้มงวดบางครั้งเรียกว่าการประเมินแบบกระตือรือร้น ซึ่งฟังดูคล้ายกับการประเมินแบบขี้เกียจ อย่างไรก็ตาม การประเมินแบบขี้เกียจเป็นเพียงหนึ่งในกลยุทธ์ที่ไม่เข้มงวดที่เป็นไปได้มากมาย การประเมินแบบ Lazy โดดเด่นกว่าเพราะมันแคชผลลัพธ์เพิ่มเติม แต่จะเป็นไปได้ก็ต่อเมื่อนิพจน์ไม่มีผลข้างเคียง

สุดท้าย การประเมินแบบเข้มงวดและไม่เข้มงวดมักจะใช้ร่วมกันในภาษาโปรแกรมเดียว และแม้แต่ในนิพจน์เดียวกัน ดังนั้นจุดตัดในภาพ ตัวอย่างคือif...else...: การประเมินเงื่อนไขนั้นเข้มงวด แต่การประเมินทั้งสองอนุประโยคนั้นไม่เข้มงวด

ขอบคุณที่อ่าน! ฉันตั้งใจจะเขียนหนึ่งแทะทุกเดือน สำหรับข้อมูลเพิ่มเติมสมัครรับจดหมายข่าวของฉันและ ติดตามฉัน บนTwitter

เผยแพร่ครั้งแรกที่https://getcode.substack.com