มีสิ่งอำนวยความสะดวกด้านภาษาของเครื่องกำเนิดไฟฟ้าเช่น `yield 'เป็นความคิดที่ดีหรือไม่?


9

PHP, C #, Python และภาษาอื่น ๆ สองสามภาษามีyieldคำสำคัญที่ใช้ในการสร้างฟังก์ชั่นเครื่องกำเนิดไฟฟ้า

ใน PHP: http://php.net/manual/en/language.generators.syntax.php

ใน Python: https://www.pythoncentral.io/python-generators-and-yield-keyword/

ใน C #: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/yield

ฉันกังวลว่าในฐานะคุณสมบัติทางภาษา / สิ่งอำนวยความสะดวกyieldแบ่งการประชุมบางส่วน หนึ่งในนั้นคือสิ่งที่ฉันจะอ้างถึงคือ "ความแน่นอน" มันเป็นวิธีการที่ส่งกลับผลลัพธ์ที่แตกต่างกันทุกครั้งที่คุณเรียกมัน ด้วยฟังก์ชั่นที่ไม่ใช่เครื่องกำเนิดไฟฟ้าปกติคุณสามารถเรียกมันได้และถ้ามันได้รับอินพุตเดียวกันมันก็จะส่งคืนเอาต์พุตเดียวกัน ด้วยผลผลิตจะส่งคืนเอาต์พุตที่ต่างกันตามสถานะภายใน ดังนั้นหากคุณสุ่มเรียกใช้ฟังก์ชันการสร้างโดยไม่ทราบสถานะก่อนหน้าคุณจะไม่สามารถคาดหวังว่าจะให้ผลลัพธ์ที่แน่นอน

ฟังก์ชั่นเช่นนี้เหมาะสมกับกระบวนทัศน์ทางภาษาอย่างไร จริง ๆ แล้วมันเป็นการทำลายอนุสัญญาใด ๆ เป็นความคิดที่ดีที่จะมีและใช้คุณสมบัตินี้หรือไม่? (เพื่อเป็นตัวอย่างของสิ่งที่ดีและสิ่งที่ไม่ดีgotoเคยเป็นคุณสมบัติของหลายภาษาและยังคงเป็น แต่มันก็ถือว่าเป็นอันตรายและถูกกำจัดให้หมดไปจากบางภาษาเช่น Java) คอมไพเลอร์ภาษาโปรแกรม / ล่ามต้องแยกออกจากอนุสัญญาใด ๆ ที่จะใช้คุณลักษณะดังกล่าวตัวอย่างเช่นภาษาต้องใช้หลายเธรดสำหรับคุณลักษณะนี้ในการทำงานหรือสามารถทำได้โดยไม่ต้องเทคโนโลยีเกลียว?


4
yieldเป็นหลักเครื่องยนต์รัฐ ไม่ได้หมายถึงการส่งคืนผลลัพธ์เดียวกันทุกครั้ง สิ่งที่จะทำอย่างไรกับความแน่นอนแน่นอนคือคืนค่ารายการถัดไปในจำนวนที่นับได้ทุกครั้งที่เรียกใช้ ไม่จำเป็นต้องมีเธรด คุณต้องปิด (มากหรือน้อย) เพื่อรักษาสถานะปัจจุบัน
Robert Harvey

1
สำหรับคุณภาพของ "ความแน่นอน" ให้พิจารณาว่าตามลำดับอินพุตเดียวกันชุดของการเรียกไปยังตัววนซ้ำจะให้ผลลัพธ์ที่เหมือนกันในลำดับเดียวกัน
Robert Harvey

4
ฉันไม่แน่ใจว่าคำถามของคุณมาจากไหนเนื่องจาก C ++ ไม่มีyield คำหลักเหมือนกับที่ Python ทำ มันมีวิธีการคงที่std::this_thread::yield()แต่นั่นไม่ใช่คำหลัก ดังนั้นการเติมthis_threadจะเป็นการเรียกเกือบทุกครั้งทำให้เห็นได้ชัดว่ามันเป็นคุณลักษณะของไลบรารีที่ใช้สำหรับการประมวลผลเธรดไม่ใช่คุณลักษณะภาษาเกี่ยวกับการยอมให้โฟลว์ควบคุมโดยทั่วไป
Ixrec

ลิงก์อัปเดตเป็น C #, หนึ่งรายการสำหรับ C ++ ที่นำออก
Dennis

คำตอบ:


16

Caveats ก่อน - C # เป็นภาษาที่ฉันรู้จักดีที่สุดและในขณะที่มันมีลักษณะyieldที่คล้ายกับภาษาอื่น ๆ ' yieldแต่อาจมีความแตกต่างเล็กน้อยที่ฉันไม่รู้

ฉันกังวลว่าในฐานะที่เป็นคุณสมบัติทางภาษา / สิ่งอำนวยความสะดวกให้ผลตอบแทนแบ่งการประชุมบางอย่าง หนึ่งในนั้นคือสิ่งที่ฉันจะอ้างถึงคือ "ความแน่นอน" มันเป็นวิธีการที่ส่งกลับผลลัพธ์ที่แตกต่างกันทุกครั้งที่คุณเรียกมัน

เรื่องเหลวไหล. คุณจริงๆคาดหวังRandom.NextหรือConsole.ReadLine จะกลับผลเดียวกันทุกครั้งที่คุณเรียกพวกเขา? ส่วนที่เหลือโทร? การตรวจสอบ? นำไอเท็มออกจากคอลเล็กชันหรือไม่ มีฟังก์ชั่น (ดีมีประโยชน์) ทุกประเภทที่ไม่บริสุทธิ์

ฟังก์ชั่นเช่นนี้เหมาะสมกับกระบวนทัศน์ทางภาษาอย่างไร จริง ๆ แล้วมันเป็นการทำลายอนุสัญญาใด ๆ

ใช่yieldเล่นได้แย่มากtry/catch/finallyและไม่ได้รับอนุญาต ( https://blogs.msdn.microsoft.com/ericlippert/2009/07/16/iterator-blocks-part-three-why-no-yield-in-finally/สำหรับ ข้อมูลเพิ่มเติม).

ควรใช้คุณสมบัตินี้หรือไม่?

เป็นความคิดที่ดีที่จะมีคุณสมบัตินี้ สิ่งที่ต้องการ C # 's LINQ คือจริงๆดี - ซมประเมินคอลเลกชันยังมีผลประโยชน์ขนาดใหญ่และyieldช่วยให้การจัดเรียงของสิ่งที่จะทำในส่วนของรหัสที่มีส่วนของข้อบกพร่องที่มือรีด iterator จะ

ที่กล่าวว่าไม่มีการใช้งานมากมายสำหรับyieldการประมวลผลคอลเลกชันสไตล์ LINQ นอก ฉันใช้มันเพื่อการตรวจสอบความถูกต้องการสร้างตารางการสุ่มและอื่น ๆ แต่ฉันคาดหวังว่านักพัฒนาส่วนใหญ่ไม่เคยใช้มัน (หรือใช้ผิดวัตถุประสงค์)

คอมไพเลอร์ภาษาโปรแกรม / ล่ามต้องแยกออกจากอนุสัญญาใด ๆ ที่จะใช้คุณลักษณะดังกล่าวตัวอย่างเช่นภาษาต้องใช้หลายเธรดสำหรับคุณลักษณะนี้ในการทำงานหรือสามารถทำได้โดยไม่ต้องเทคโนโลยีเกลียว?

ไม่แน่นอน คอมไพเลอร์สร้างตัวทำซ้ำเครื่องสถานะที่ติดตามตำแหน่งที่จะหยุดเพื่อให้สามารถเริ่มต้นที่นั่นอีกครั้งในครั้งต่อไปที่มันถูกเรียก กระบวนการสำหรับการสร้างรหัสทำสิ่งที่คล้ายกับสไตล์การส่งต่ออย่างต่อเนื่องโดยที่โค้ดหลังจากที่yieldถูกดึงเข้าสู่บล็อกของตัวเอง (และหากมีyields, บล็อกย่อยอื่นและอื่น ๆ ) นั่นเป็นวิธีที่รู้จักกันดีซึ่งใช้บ่อยในการเขียนโปรแกรมเชิงหน้าที่และยังแสดงในการคอมไพล์แบบ async / await ของ C #

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

โดยสรุปแล้วyieldเป็นคุณลักษณะที่มีผลกระทบค่อนข้างต่ำซึ่งช่วยได้จริงกับชุดย่อยของปัญหาที่เฉพาะเจาะจง


ฉันไม่เคยใช้ C # อย่างจริงจัง แต่yieldคำหลักนี้คล้ายกับ coroutines, ใช่, หรืออะไรที่ต่างออกไป? ถ้าฉันต้องการฉันมีหนึ่งใน C! ฉันสามารถคิดอย่างน้อยบางส่วนของรหัสที่ดีที่จะได้ง่ายขึ้นมากในการเขียนด้วยคุณสมบัติภาษาดังกล่าว

2
@DunkunkCoder - คล้ายกัน แต่มีข้อ จำกัด บางอย่างที่ฉันเข้าใจ
Telastyn

1
คุณไม่ต้องการเห็นอัตราผลตอบแทนในทางที่ผิด ยิ่งมีคุณลักษณะภาษามากเท่าใดคุณก็จะยิ่งมีโอกาสเขียนโปรแกรมได้ไม่ดีในภาษานั้น ฉันไม่แน่ใจว่าวิธีการที่ถูกต้องในการเขียนภาษาที่เข้าถึงได้คือการขว้างปาใส่คุณแล้วดูว่ามีอะไรติด
Neil

1
@DrunkCoder: เป็นรุ่นกึ่ง จำกัด ในจำนวน จำกัด ที่จริงแล้วมันถูกใช้เป็นรูปแบบวากยสัมพันธ์โดยคอมไพเลอร์ซึ่งได้รับการขยายเป็นชุดของการเรียกเมธอดคลาสและวัตถุ (โดยทั่วไปคอมไพเลอร์สร้างวัตถุต่อเนื่องที่รวบรวมบริบทปัจจุบันในเขตข้อมูล) การใช้งานเริ่มต้นสำหรับคอลเลกชันเป็นกึ่ง coroutine แต่โดยการบรรทุกเกินพิกัด "มายากล" วิธีการรวบรวมใช้คุณสามารถปรับแต่งพฤติกรรมจริง ตัวอย่างเช่นก่อนasync/ ถูกบันทึกอยู่ในภาษาที่มีคนนำมาใช้โดยใช้await yield
Jörg W Mittag

1
@Neil โดยทั่วไปเป็นไปได้ที่จะใช้คุณสมบัติการเขียนโปรแกรมภาษาใด ๆ หากสิ่งที่คุณพูดนั้นเป็นความจริงมันจะยากกว่าที่จะเขียนโปรแกรมอย่างไม่ดีโดยใช้ C มากกว่า Python หรือ C # แต่นี่ไม่ใช่กรณีที่ภาษาเหล่านั้นมีเครื่องมือมากมายที่ป้องกันโปรแกรมเมอร์จากความผิดพลาดซึ่งง่ายมาก เพื่อให้กับ C ในความเป็นจริงสาเหตุของโปรแกรมที่ไม่ดีคือโปรแกรมเมอร์ที่ไม่ดี - มันค่อนข้างเป็นปัญหาที่ไม่เชื่อเรื่องภาษา
Ben Cottrell

12

มีสิ่งอำนวยความสะดวกภาษากำเนิดเช่นyieldความคิดที่ดี?

ฉันต้องการที่จะตอบคำถามนี้จากมุมมองของงูหลามกับหนักแน่นใช่มันเป็นความคิดที่ดี

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

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

นี่เป็นเท็จ วิธีการเกี่ยวกับวัตถุสามารถคิดเป็นหน้าที่ตัวเองด้วยสถานะภายในของตนเอง ใน Python เนื่องจากทุกอย่างเป็นวัตถุคุณสามารถได้รับวิธีการจากวัตถุและส่งผ่านวิธีการนั้น (ซึ่งถูกผูกไว้กับวัตถุที่มาจากดังนั้นจึงจดจำสถานะของมัน)

ตัวอย่างอื่น ๆ รวมถึงฟังก์ชั่นการสุ่มอย่างจงใจรวมถึงวิธีการป้อนข้อมูลเช่นเครือข่ายระบบไฟล์และเทอร์มินัล

ฟังก์ชั่นเช่นนี้เหมาะสมกับกระบวนทัศน์ทางภาษาอย่างไร

หากกระบวนทัศน์ทางภาษารองรับสิ่งต่าง ๆ เช่นฟังก์ชั่นชั้นหนึ่งและเครื่องกำเนิดไฟฟ้ารองรับคุณสมบัติภาษาอื่น ๆ เช่นโปรโตคอล Iterable แล้วพวกเขาก็เข้ากันได้อย่างลงตัว

จริง ๆ แล้วมันเป็นการทำลายอนุสัญญาใด ๆ

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

การคอมไพล์เลอร์ / ล่ามภาษาโปรแกรมต้องแยกออกจากอนุสัญญาใด ๆ เพื่อใช้คุณลักษณะดังกล่าว

เช่นเดียวกับคุณสมบัติอื่น ๆ คอมไพเลอร์ก็ต้องได้รับการออกแบบเพื่อรองรับคุณสมบัติ ในกรณีของ Python ฟังก์ชั่นเป็นวัตถุที่มีสถานะอยู่แล้ว (เช่นอาร์กิวเมนต์เริ่มต้นและหมายเหตุประกอบฟังก์ชัน)

ภาษาต้องใช้หลายเธรดเพื่อให้คุณสมบัตินี้ทำงานหรือสามารถทำได้โดยไม่ต้องใช้เธรดเทคโนโลยี?

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


หมายเหตุ: ตัวอย่างอยู่ใน Python 3

เกินกว่าอัตราผลตอบแทน

ในขณะที่yieldคำสำคัญสามารถใช้ในฟังก์ชั่นใด ๆ ที่จะทำให้มันกลายเป็นเครื่องกำเนิดไฟฟ้ามันไม่ได้เป็นวิธีเดียวที่จะทำให้มัน Python มีเครื่องมือสร้างนิพจน์ซึ่งเป็นวิธีที่มีประสิทธิภาพในการแสดงเครื่องกำเนิดไฟฟ้าอย่างชัดเจนในแง่ของการทำซ้ำอื่น (รวมถึงเครื่องกำเนิดไฟฟ้าอื่น ๆ )

>>> pairs = ((x,y) for x in range(10) for y in range(10) if y >= x)
>>> pairs
<generator object <genexpr> at 0x0311DC90>
>>> sum(x*y for x,y in pairs)
1155

อย่างที่คุณเห็นไม่เพียง แต่เป็นไวยากรณ์ที่สะอาดและอ่านได้ แต่มีฟังก์ชันในตัวเช่นsumaccept generators

กับ

ตรวจสอบข้อเสนอการเพิ่มประสิทธิภาพของงูหลามสำหรับคำสั่งด้วย มันแตกต่างจากที่คุณคาดหวังจากคำสั่ง With ในภาษาอื่น ๆ ด้วยความช่วยเหลือเล็กน้อยจากห้องสมุดมาตรฐานผู้สร้าง Python ทำงานอย่างสวยงามในฐานะผู้จัดการบริบทสำหรับพวกเขา

>>> from contextlib import contextmanager
>>> @contextmanager
def debugWith(arg):
        print("preprocessing", arg)
        yield arg
        print("postprocessing", arg)


>>> with debugWith("foobar") as s:
        print(s[::-1])


preprocessing foobar
raboof
postprocessing foobar

แน่นอนว่าการพิมพ์สิ่งต่าง ๆ เป็นเรื่องที่น่าเบื่อที่สุดที่คุณสามารถทำได้ที่นี่ แต่มันแสดงผลลัพธ์ที่มองเห็นได้ ตัวเลือกที่น่าสนใจ ได้แก่ การจัดการทรัพยากรอัตโนมัติ (การเปิดและปิดไฟล์ / สตรีม / การเชื่อมต่อเครือข่าย), ล็อคการทำงานพร้อมกัน, การปิดหรือแทนที่ฟังก์ชั่นชั่วคราว, และคลายการบีบอัดข้อมูล ถ้าฟังก์ชั่นการโทรนั้นเหมือนการฉีดโค้ดเข้าไปในโค้ดของคุณดังนั้นข้อความสั่งจะเหมือนกับการตัดส่วนของโค้ดของคุณในโค้ดอื่น อย่างไรก็ตามคุณใช้มันเป็นตัวอย่างที่ชัดเจนของการเชื่อมต่อที่ง่ายเข้ากับโครงสร้างภาษา เครื่องมือสร้างผลตอบแทนไม่ใช่วิธีเดียวในการสร้างผู้จัดการบริบท แต่แน่นอนว่าเป็นวิธีที่สะดวก

สำหรับและอ่อนเพลียบางส่วน

สำหรับลูปใน Python ทำงานในวิธีที่น่าสนใจ พวกเขามีรูปแบบต่อไปนี้:

for <name> in <iterable>:
    ...

ก่อนอื่นนิพจน์ที่ฉันเรียก<iterable>นั้นจะถูกประเมินเพื่อให้ได้วัตถุที่ทำซ้ำได้ ประการที่สอง iterable __iter__เรียกใช้และ iterator ที่ได้จะถูกจัดเก็บไว้เบื้องหลัง Subsequenty, __next__ถูกเรียกบน iterator <name>ที่จะได้รับค่าเชื่อมโยงกับชื่อที่คุณใส่ใน ขั้นตอนนี้ซ้ำจนกว่าการเรียกร้องให้พ่น__next__ StopIterationข้อยกเว้นถูกกลืนโดย for for loop และการดำเนินการต่อจากตรงนั้น

กลับมาหาเครื่องปั่นไฟ: เมื่อคุณโทรหา__iter__เครื่องกำเนิดไฟฟ้ามันจะกลับมาเอง

>>> x = (a for a in "boring generator")
>>> id(x)
51502272
>>> id(x.__iter__())
51502272

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

>>> generator = (x for x in 'more boring stuff')
>>> for letter in generator:
        print(ord(letter))
        if letter > 'p':
                break


109
111
114
>>> for letter in generator:
        print(letter)


e

b
o
r
i
n
g

s
t
u
f
f

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

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

>>> import sys
>>> sys.getsizeof([x for x in range(10000)])
43816
>>> sys.getsizeof(range(10000000000))
24
>>> sys.getsizeof([x for x in range(10000000000)])
Traceback (most recent call last):
  File "<pyshell#10>", line 1, in <module>
    sys.getsizeof([x for x in range(10000000000)])
  File "<pyshell#10>", line 1, in <listcomp>
    sys.getsizeof([x for x in range(10000000000)])
MemoryError

เครื่องกำเนิดไฟฟ้าสามารถถูกล่ามโซ่อย่างเกียจคร้าน

logfile = open("logs.txt")
lastcolumn = (line.split()[-1] for line in logfile)
numericcolumn = (float(x) for x in lastcolumn)
print(sum(numericcolumn))

บรรทัดแรกที่สองและที่สามกำหนดตัวกำเนิดแต่ละตัว แต่ไม่ได้ทำงานจริง เมื่อเรียกบรรทัดสุดท้าย sum จะถามตัวเลขสำหรับค่า numericcolumn ต้องการค่าจาก lastcolumn และ lastcolumn จะขอค่าจาก logfile ซึ่งจริงๆแล้วจะอ่านบรรทัดจากไฟล์ สแต็กนี้จะผ่อนคลายจนกว่าผลรวมจะได้รับจำนวนเต็มแรก จากนั้นกระบวนการจะเกิดขึ้นอีกครั้งสำหรับบรรทัดที่สอง ณ จุดนี้ผลรวมมีจำนวนเต็มสองจำนวนและรวมเข้าด้วยกัน โปรดทราบว่าบรรทัดที่สามยังไม่ได้อ่านจากไฟล์ จากนั้น Sum จะดำเนินการขอค่าจาก numericcolumn (ลบเลือนโดยสิ้นเชิงไปยังส่วนอื่น ๆ ของ chain) และเพิ่มเข้าไปจนกระทั่งตัวเลขตัวเลขนั้นหมดไป

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

ข้อสรุป

นี่ไม่ใช่การตรวจสอบที่สมบูรณ์ของการใช้งานของเครื่องกำเนิดไฟฟ้าทั้งหมดใน Python โดยเฉพาะอย่างยิ่งฉันข้ามเครื่องกำเนิดไฟฟ้าไม่ จำกัด เครื่องสถานะการส่งผ่านค่ากลับและความสัมพันธ์ของพวกเขากับ coroutines

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


6

หากคุณคุ้นเคยกับภาษา OOP แบบคลาสสิกเครื่องกำเนิดไฟฟ้าและyieldอาจดูสั่นสะเทือนเนื่องจากสถานะที่ไม่แน่นอนถูกจับในระดับฟังก์ชั่นมากกว่าระดับวัตถุ

คำถามของ "ความแน่นอน" คือปลาเฮอริ่งแดง มันมักจะเรียกว่าreferential transparentและโดยทั่วไปหมายความว่าฟังก์ชันจะส่งคืนผลลัพธ์เดียวกันสำหรับอาร์กิวเมนต์เดียวกัน ทันทีที่คุณมีสถานะที่ไม่แน่นอนคุณจะสูญเสียความโปร่งใสในการอ้างอิง ใน OOP วัตถุมักจะมีสถานะที่ไม่แน่นอนซึ่งหมายความว่าผลลัพธ์ของการเรียกใช้เมธอดไม่เพียง แต่ขึ้นอยู่กับข้อโต้แย้ง แต่ยังรวมถึงสถานะภายในของวัตถุด้วย

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

function getCounter() {
   var cnt = 1;
   return function(){ return cnt++; }
}
var counter = getCounter();
counter() --> 1
counter() --> 2

กล่าวโดยย่อyieldเป็นธรรมชาติในภาษาที่รองรับการปิด แต่จะอยู่นอกสถานที่ในภาษาเช่น Java เวอร์ชันเก่าที่มีสถานะที่ไม่แน่นอนเท่านั้นที่ระดับวัตถุ


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

0

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

นี่ไม่ใช่แค่ความคิดเห็นของฉัน แม้แต่กุยโดในกระดานข่าว PEP ที่เขาปฏิบัติตามในเรื่องนี้ยอมรับว่าฟังก์ชั่นเครื่องกำเนิดไฟฟ้านั้นไม่ใช่เครื่องกำเนิดไฟฟ้า แต่เป็น "โรงงานเครื่องกำเนิดไฟฟ้า"

นั่นเป็นเรื่องสำคัญคุณไม่คิดเหรอ? แต่การอ่านเอกสาร 99% ออกมาคุณจะได้รับความประทับใจว่าฟังก์ชันตัวกำเนิดเป็นตัวกำเนิดที่แท้จริง

Guido พิจารณาแทนที่ "def" สำหรับ "gen" สำหรับฟังก์ชั่นเหล่านี้และกล่าวว่าไม่ แต่ฉันก็เถียงว่าคงไม่เพียงพอ มันควรจะเป็น:

def make_gen(args)
    def_gen foo
        # Put in "yield" and other beahvior
    return_gen foo
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.