ฉันไม่เข้าใจแนวคิดของผลข้างเคียงอย่างชัดเจน
- ผลข้างเคียงในการเขียนโปรแกรมคืออะไร?
- มันขึ้นอยู่กับการเขียนโปรแกรมภาษา
- มีผลข้างเคียงภายนอกและภายในไหม?
โปรดยกตัวอย่างสาเหตุที่สร้างผลข้างเคียง
ฉันไม่เข้าใจแนวคิดของผลข้างเคียงอย่างชัดเจน
โปรดยกตัวอย่างสาเหตุที่สร้างผลข้างเคียง
คำตอบ:
ผลข้างเคียงจะหมายเพียงเพื่อการปรับเปลี่ยนบางชนิดของรัฐ - เช่น:
ตรงกันข้ามกับสิ่งที่บางคนพูดว่า:
ผลข้างเคียงไม่จำเป็นต้องซ่อนหรือคาดไม่ถึง (สามารถทำได้ แต่ไม่เกี่ยวข้องกับคำนิยามตามที่ใช้กับวิทยาศาสตร์คอมพิวเตอร์);
ผลข้างเคียงที่มีอะไรจะทำอย่างไรกับ Idempotency ฟังก์ชั่น idempotent สามารถมีผลข้างเคียงและฟังก์ชั่นที่ไม่ใช่ idempotent อาจไม่มีผลข้างเคียง (เช่นการรับวันที่และเวลาของระบบปัจจุบัน)
มันง่ายมากจริงๆ ผลข้างเคียง = การเปลี่ยนแปลงบางสิ่งบางอย่าง
PS ในฐานะผู้วิจารณ์เบ็นโจลชี้ให้เห็นว่าหลายคนอาจกำลังนิยามคำจำกัดความของผลข้างเคียงกับคำจำกัดความของฟังก์ชั่นที่บริสุทธิ์ซึ่งเป็นฟังก์ชั่นที่ (a) idempotent และ (b) ไม่มีผลข้างเคียง หนึ่งไม่ได้หมายถึงอื่น ๆ ในวิทยาศาสตร์คอมพิวเตอร์ทั่วไป แต่ภาษาการเขียนโปรแกรมการทำงานมักจะมีแนวโน้มที่จะบังคับใช้ข้อ จำกัด ทั้งสอง
++a
ในซี ดูไม่เหมือนงานที่มอบหมาย b = ++a;
มีผลข้างเคียงที่สอง a
ที่เห็นได้ชัดหนึ่งและการเข้ารหัสลับการโอน นั่นคือสิ่งที่เป็นผลข้างเคียงที่ต้องการ (สำหรับบางคน) แต่ได้รับการขนานนามว่าเป็นผลข้างเคียงสำหรับอาชีพการงานทั้งหมดของฉันเพื่อทำให้มันไม่ลึกซึ้ง
การดำเนินการใด ๆ ที่แก้ไขสถานะของคอมพิวเตอร์หรือการโต้ตอบกับโลกภายนอกมีการกล่าวถึงผลข้างเคียง วิกิพีเดียดูผลข้างเคียง
ตัวอย่างเช่นฟังก์ชั่นนี้ไม่มีผลข้างเคียง ผลลัพธ์ขึ้นอยู่กับอินพุตอาร์กิวเมนต์เท่านั้นและไม่มีอะไรเกี่ยวกับสถานะของโปรแกรมหรือสภาพแวดล้อมที่เปลี่ยนแปลงเมื่อเรียกว่า:
int square(int x) { return x * x; }
ในทางตรงกันข้ามการเรียกฟังก์ชั่นเหล่านี้จะให้ผลลัพธ์ที่แตกต่างกันไปขึ้นอยู่กับลำดับที่คุณโทรหาพวกเขาเพราะพวกเขาเปลี่ยนบางอย่างเกี่ยวกับสถานะของคอมพิวเตอร์:
int n = 0;
int next_n() { return n++; }
void set_n(int newN) { n = newN; }
ฟังก์ชั่นนี้มีผลข้างเคียงของการเขียนข้อมูลไปยังเอาต์พุต คุณไม่เรียกใช้ฟังก์ชันเนื่องจากคุณต้องการส่งคืนค่า คุณเรียกมันว่าเพราะคุณต้องการผลที่มันมีต่อ "โลกภายนอก":
int Write(const char* s) { return printf("Output: %s\n", s); }
Write
ตัวอย่างของคุณแสดงให้เห็นว่าการมีผลข้างเคียงไม่ได้หมายความว่าฟังก์ชั่นจะเปลี่ยนผลลัพธ์เมื่อเทียบกับอินพุตหรือแม้แต่เอาต์พุตนั้นขึ้นอยู่กับอินพุตเลย
square(x)
อาจทำให้โมดูลที่ฟังก์ชันถูกกำหนดให้โหลดจากดิสก์ ควรคำนึงถึงผลข้างเคียงหรือไม่ ท้ายที่สุดบุรุษคนนี้ที่การโทร (ครั้งแรก) ใช้เวลานานโดยไม่คาดคิดการใช้งาน RAM ก็สูงขึ้น ฯลฯ
square(x)
เพราะคุณต้องการให้สถานะคอมพิวเตอร์ภายนอกเปลี่ยนแปลงคุณอาจพิจารณาว่าเป็นผลข้างเคียง
ฉันคิดว่าคำตอบที่มีอยู่ค่อนข้างดี ฉันต้องการอธิบายรายละเอียดในบางประเด็นที่ IMO ยังไม่ได้รับการเน้นย้ำมากพอ
ในวิชาคณิตศาสตร์ฟังก์ชั่นเป็นเพียงการทำแผนที่จาก tuple ของค่าไปยังค่า ดังนั้นให้ฟังก์ชั่นf
และค่าx
, มักจะเป็นผลเดียวกันf(x)
y
คุณอาจแทนที่f(x)
ด้วยy
ทุกที่ในการแสดงออกและไม่มีอะไรจะเปลี่ยนแปลง
สิ่งที่เรียกว่าฟังก์ชั่น (หรือโพรซีเดอร์) ในภาษาการเขียนโปรแกรมจำนวนมากคือโครงสร้าง (ส่วนของโค้ด) ที่สามารถดำเนินการได้เพราะ:
ดังนั้นเอฟเฟกต์อาจเกี่ยวข้องกับสถานะ แต่ยังรวมถึงแง่มุมอื่น ๆ เช่นการยิงขีปนาวุธหรือการหยุดการทำงานชั่วคราวเป็นเวลาสองสามวินาที
คำข้างเคียงอาจฟังดูเป็นลบ แต่โดยปกติแล้วผลของการเรียกฟังก์ชั่นคือจุดประสงค์ของฟังก์ชั่นนั้น ๆ ผมคิดว่าตั้งแต่ฟังก์ชั่นในระยะแรกที่ใช้ในวิชาคณิตศาสตร์คำนวณค่าถือว่าเป็นผลกระทบหลักของการทำงานในขณะที่ผลกระทบอื่น ๆ ที่ได้รับการพิจารณาผลข้างเคียง ภาษาการเขียนโปรแกรมบางภาษาใช้โพรซีเดอร์เพื่อหลีกเลี่ยงความสับสนกับฟังก์ชันในแง่คณิตศาสตร์
สังเกตได้ว่า
sleep()
ใน Python มีประโยชน์สำหรับเอฟเฟกต์ (ด้าน) เท่านั้น สิ่งเหล่านี้มักถูกจำลองเป็นฟังก์ชันที่ส่งคืนค่าพิเศษNone
หรือunit
หรือ()
หรือ ... ซึ่งเพียงระบุว่าการคำนวณสิ้นสุดลงอย่างถูกต้องผลข้างเคียงคือเมื่อการดำเนินการมีผลกับตัวแปร / วัตถุที่อยู่นอกการใช้งานที่ต้องการ
อาจเกิดขึ้นได้เมื่อคุณทำการเรียกไปยังฟังก์ชันที่ซับซ้อนซึ่งมีผลข้างเคียงของการเปลี่ยนแปลงตัวแปรส่วนกลางบางอย่างแม้ว่าจะไม่ใช่เหตุผลที่คุณเรียกมันว่า (บางทีคุณอาจเรียกมันว่าเพื่อแยกบางอย่างออกจากฐานข้อมูล)
ฉันยอมรับว่าฉันมีปัญหาในการหาตัวอย่างง่ายๆที่ไม่ได้ดูอย่างสมบูรณ์แบบและตัวอย่างจากสิ่งที่ฉันได้ทำมานานเกินกว่าที่จะโพสต์ที่นี่ได้ (และเนื่องจากมันเกี่ยวข้องกับการทำงาน )
ตัวอย่างหนึ่งที่ฉันเคยเห็น (เมื่อครู่ก่อน) เป็นฟังก์ชั่นที่เปิดการเชื่อมต่อฐานข้อมูลถ้าการเชื่อมต่ออยู่ในสถานะปิด ปัญหาคือว่ามันควรจะปิดการเชื่อมต่อในตอนท้ายของฟังก์ชั่น แต่นักพัฒนาซอฟต์แวร์ลืมที่จะเพิ่มรหัสนั้น ดังนั้นที่นี่มีความตั้งใจผลข้างเคียง: เรียกขั้นตอนที่ควรจะเพียง แต่ทำแบบสอบถามและผลข้างเคียงคือการที่การเชื่อมต่อยังคงเปิดอยู่และถ้าฟังก์ชั่นที่เรียกว่าเป็นครั้งที่สองในแถวที่มีข้อผิดพลาดจะได้รับการเลี้ยงดูบอกว่าการเชื่อมต่อเป็น เปิดแล้ว
ตกลงดังนั้นเมื่อทุกคนให้ตัวอย่างตอนนี้ฉันคิดว่าฉันก็จะเหมือนกัน;)
/*code is PL/SQL-styled pseudo-code because that's what's on my mind right now*/
g_some_global int := 0; --define a globally accessible variable somewhere.
function do_task_x(in_a in number) is
begin
b := calculate_magic(in_a);
if b mod 2 == 0 then
g_some_global := g_some_global + b;
end if;
return (b * 2.3);
end;
ฟังก์ชันdo_task_x
มีผลกระทบหลักของการคืนผลลัพธ์ของการคำนวณบางอย่างและผลข้างเคียงของการปรับเปลี่ยนตัวแปรกลาง
แน่นอนซึ่งเป็นหลักและเป็นผลข้างเคียงที่สามารถเปิดการตีความและอาจขึ้นอยู่กับการใช้งานจริง ถ้าฉันเรียกฟังก์ชั่นนี้เพื่อวัตถุประสงค์ในการแก้ไข global และฉันจะทิ้งค่าที่ส่งคืนมากกว่าที่ฉันจะบอกว่าการแก้ไข globalนั้นเป็นผลหลัก
ในวิทยาการคอมพิวเตอร์ฟังก์ชันหรือนิพจน์ถูกกล่าวว่ามีผลข้างเคียงถ้าแก้ไขบางสถานะหรือมีปฏิสัมพันธ์ที่สังเกตได้กับฟังก์ชันการโทรหรือโลกภายนอก
ฟังก์ชั่นในแง่คณิตศาสตร์คือการแมปจากอินพุตไปยังเอาต์พุต ผลของการเรียกฟังก์ชั่นที่ตั้งใจไว้คือเพื่อจับคู่อินพุตกับเอาต์พุตที่ส่งคืน หากฟังก์ชั่นนั้นทำอะไรอย่างอื่นก็ไม่สำคัญว่าอะไร แต่ถ้ามันมีพฤติกรรมใด ๆ ที่ไม่ได้แมปอินพุตกับเอาต์พุตพฤติกรรมนั้นเป็นที่รู้กันว่าเป็นผลข้างเคียง
ในระยะทั่วไปมากขึ้น, ผลข้างเคียงคือผลกระทบใด ๆ ที่ไม่ได้เป็นผลกระทบที่ตั้งใจของนักออกแบบของการก่อสร้าง
ผลกระทบคือสิ่งใดก็ตามที่ส่งผลกระทบต่อนักแสดง ถ้าฉันเรียกฟังก์ชั่นที่ส่งข้อความแบบแยกย่อยแฟนสาวของฉันซึ่งมีผลต่อกลุ่มของนักแสดงฉันเธอเธอเครือข่าย บริษัท โทรศัพท์มือถือ ฯลฯ ผลที่ตั้งใจเพียงอย่างเดียวของการเรียกฟังก์ชั่นฟรีด้านข้างคือสำหรับฟังก์ชั่น เพื่อส่งคืนการแม็พจากอินพุตของฉัน ดังนั้นสำหรับ:
public void SendBreakupTextMessage() {
Messaging.send("I'm breaking up with you!")
}
หากสิ่งนี้ตั้งใจให้เป็นฟังก์ชันสิ่งเดียวที่ควรทำคือคืนค่าเป็นโมฆะ หากปราศจากผลข้างเคียงก็ไม่ควรส่งข้อความ
ในภาษาการเขียนโปรแกรมส่วนใหญ่ไม่มีโครงสร้างสำหรับฟังก์ชันทางคณิตศาสตร์ ไม่มีการสร้างมีวัตถุประสงค์เพื่อใช้เช่นนี้ นั่นเป็นเหตุผลที่ภาษาส่วนใหญ่บอกว่าคุณมีวิธีการหรือขั้นตอน จากการออกแบบสิ่งเหล่านี้มีจุดประสงค์เพื่อให้สามารถสร้างเอฟเฟกต์อื่น ๆ อีกมากมาย ในการเขียนโปรแกรมทั่วไปไม่มีใครสนใจเกี่ยวกับเจตนาของวิธีการหรือขั้นตอนดังนั้นเมื่อมีคนบอกว่าฟังก์ชั่นนี้มีผลข้างเคียงพวกเขาหมายถึงได้อย่างมีประสิทธิภาพโครงสร้างนี้ไม่ทำงานเหมือนฟังก์ชั่นทางคณิตศาสตร์ และเมื่อมีคนบอกว่าฟังก์ชั่นนี้ไม่มีผลข้างเคียงพวกเขาหมายถึงโครงสร้างนี้ทำงานได้อย่างมีประสิทธิภาพเหมือนกับฟังก์ชันทางคณิตศาสตร์
ฟังก์ชั่นที่บริสุทธิ์ไม่มีผลข้างเคียงเสมอโดยนิยาม ฟังก์ชั่นบริสุทธิ์เป็นวิธีที่จะพูดว่าฟังก์ชั่นนี้แม้ว่ามันจะใช้โครงสร้างที่ช่วยให้ผลกระทบมากขึ้นเพียง แต่มีผลกระทบเท่ากับหนึ่งเท่าของฟังก์ชั่นทางคณิตศาสตร์
ฉันท้าทายทุกคนที่จะบอกฉันเมื่อฟังก์ชั่นฟรีด้านข้างจะไม่บริสุทธิ์ ยกเว้นว่าผลกระทบที่ตั้งใจหลักของบริบทของประโยคที่ใช้คำว่าบริสุทธิ์และผลข้างเคียงฟรีไม่ได้เป็นของผลกระทบที่ตั้งใจคณิตศาสตร์ของฟังก์ชั่นแล้วสิ่งเหล่านั้นจะเสมอกัน
เช่นนี้บางครั้งถึงแม้ว่าจะไม่ค่อยบ่อยนักและฉันเชื่อว่านี่คือความแตกต่างที่ขาดและยังทำให้ผู้คนเข้าใจผิด (ซึ่งไม่ใช่ข้อสันนิษฐานทั่วไป) ในคำตอบที่ยอมรับ แต่บางครั้งมันก็สันนิษฐานว่าเจตนาของฟังก์ชั่นโปรแกรมคือ เพื่อแมปอินพุตกับเอาต์พุตโดยที่อินพุตไม่ถูก จำกัด กับพารามิเตอร์ที่ชัดเจนของฟังก์ชัน แต่เอาต์พุตถูก จำกัด กับค่าส่งคืนที่ชัดเจน หากคุณคิดว่านั่นเป็นผลกระทบที่ตั้งใจไว้ฟังก์ชั่นการอ่านไฟล์และส่งคืนผลลัพธ์ที่แตกต่างกันขึ้นอยู่กับสิ่งที่อยู่ในไฟล์นั้นยังคงมีผลข้างเคียงฟรีเนื่องจากคุณอนุญาตให้อินพุตมาจากที่อื่น
เหตุใดจึงสำคัญทั้งหมดนี้
มันคือทั้งหมดที่เกี่ยวกับการควบคุมและรักษามัน หากคุณเรียกใช้ฟังก์ชันและทำสิ่งอื่นแล้วส่งคืนค่ามันยากที่จะให้เหตุผลเกี่ยวกับพฤติกรรมของมัน คุณจะต้องเข้าไปดูในฟังก์ชั่นของรหัสจริงเพื่อคาดเดาสิ่งที่มันทำอยู่และยืนยันความถูกต้องของมัน สถานการณ์ในอุดมคติคือมันชัดเจนและง่ายต่อการรู้ว่าอะไรคือฟังก์ชั่นอินพุทที่ใช้และมันไม่ได้ทำอะไรอย่างอื่นแล้วส่งคืนเอาต์พุตให้มัน คุณสามารถผ่อนคลายสิ่งนี้ได้เล็กน้อยและพูดว่าการรู้ว่าการป้อนข้อมูลที่ใช้นั้นไม่ได้มีประโยชน์เท่าที่จะแน่ใจว่ามันไม่ได้ทำอะไรอย่างอื่นที่คุณอาจไม่ทราบแล้วส่งคืนค่าดังนั้นบางทีคุณอาจพอใจกับการบังคับใช้เท่านั้น ว่ามันจะไม่ทำอะไรอย่างอื่นแล้วแมปอินพุตไม่ว่าจะรับจากที่ใดไปยังเอาต์พุต
ในเกือบทุกกรณีจุดของโปรแกรมคือการมีเอฟเฟกต์อื่น ๆ จากนั้นทำการแมปสิ่งต่าง ๆ เข้ากับสิ่งที่ออกมา แนวคิดของการควบคุมผลข้างเคียงคือคุณสามารถจัดระเบียบโค้ดในแบบที่เข้าใจง่ายและมีเหตุผล หากคุณใส่ผลข้างเคียงทั้งหมดเข้าด้วยกันในสถานที่ที่ชัดเจนและเป็นศูนย์กลางคุณสามารถรู้ได้อย่างง่ายดายว่าจะมองหาและไว้วางใจว่านี่คือสิ่งที่เกิดขึ้นทั้งหมด หากคุณมีอินพุตที่ชัดเจนเช่นกันมันจะช่วยทดสอบพฤติกรรมสำหรับอินพุตที่แตกต่างกันและใช้งานง่ายขึ้นเนื่องจากคุณไม่จำเป็นต้องเปลี่ยนอินพุตในที่ต่าง ๆ มากมายซึ่งบางอย่างอาจไม่ชัดเจนเพียง เพื่อให้ได้สิ่งที่คุณต้องการ
เนื่องจากมีประโยชน์มากที่สุดที่จะเข้าใจเหตุผลและควบคุมพฤติกรรมของโปรแกรมคือการจัดกลุ่มข้อมูลทั้งหมดให้ชัดเจนและชัดเจนรวมทั้งมีผลข้างเคียงทั้งหมดที่ถูกจัดกลุ่มเข้าด้วยกันและชัดเจนนี่เป็นสิ่งที่ผู้คนพูดถึงเมื่อพวกเขาพูด ผลข้างเคียงบริสุทธิ์ ฯลฯ
เนื่องจากมีประโยชน์มากที่สุดคือการจัดกลุ่มผลข้างเคียงและความชัดเจนของพวกเขาบางครั้งผู้คนจะหมายถึงเท่านั้นและแยกแยะโดยบอกว่ามันไม่บริสุทธิ์ แต่ก็ยังฟรีผลข้างเคียง แต่ผลข้างเคียงนั้นสัมพันธ์กับ "ผลกระทบหลักที่ตั้งใจไว้" ดังนั้นจึงเป็นศัพท์ตามบริบท ฉันพบว่ามีการใช้งานน้อยกว่า แต่ก็น่าแปลกใจที่มีการพูดคุยกันมากเกี่ยวกับหัวข้อนี้
ในที่สุด idempotent หมายถึงการเรียกใช้ฟังก์ชั่นนี้หลายครั้งด้วยอินพุตเดียวกัน (ไม่สำคัญว่ามาจากไหน) จะส่งผลเหมือนกันเสมอ (ผลข้างเคียงหรือไม่)
ในการเขียนโปรแกรมผลข้างเคียงคือเมื่อโพรซีเดอร์เปลี่ยนตัวแปรจากนอกขอบเขต ผลข้างเคียงไม่ได้ขึ้นอยู่กับภาษา มีบางคลาสของภาษาที่มีเป้าหมายเพื่อกำจัดผลข้างเคียง (ภาษาที่ใช้งานได้จริง) แต่ฉันไม่แน่ใจว่ามีสิ่งใดบ้างที่ต้องการผลข้างเคียง แต่อาจผิด
เท่าที่ฉันรู้ไม่มีผลข้างเคียงภายในและภายนอก
นี่คือตัวอย่างง่ายๆ:
int _totalWrites;
void Write(string message)
{
// Invoking this function has the side effect of
// incrementing the value of _totalWrites.
_totalWrites++;
Debug.Write(message);
}
คำจำกัดความของผลข้างเคียงไม่เฉพาะเจาะจงกับการเขียนโปรแกรมดังนั้นเพียงแค่จินตนาการถึงผลข้างเคียงของยาหรือการรับประทานอาหารมากเกินไป
x++
แก้ไขตัวแปรx
นั้นโดยทั่วไปถือว่าเป็นผลข้างเคียง ค่าของนิพจน์นั่นคือค่าก่อนเพิ่มขึ้นx
; นี่เป็นส่วนที่ไม่ใช่ผลข้างเคียงของการแสดงออก
ผลข้างเคียงคือสิ่งที่เกิดขึ้นในรหัสที่ไม่ชัดเจน
ตัวอย่างเช่นสมมติว่าคุณมีคลาสนี้
public class ContrivedRandomGenerator {
public int Seed { get; set; }
public int GetRandomValue()
{
Random(Seed);
Seed++;
}
}
เมื่อคุณสร้างชั้นเรียนคุณจะให้เมล็ด
var randomGenerator = new ContrivedRandomGenerator();
randomGenerator.Seed = 15;
randomGenerator.GetRandomValue();
คุณไม่ทราบว่าภายในคุณแค่คาดหวังว่าจะได้รับค่าสุ่มและคุณคาดหวังว่า randomGenerator.Seed จะยังคงเป็น 15 ... แต่ก็ไม่ได้
การเรียกใช้ฟังก์ชันมีผลข้างเคียงของการเปลี่ยนแปลงค่า Seed