ตัวชี้ / การเรียกซ้ำเป็นเรื่องยากอะไร [ปิด]


20

ในอันตรายของโรงเรียนจาวาโจเอลกล่าวถึงประสบการณ์ของเขาที่เพนน์และความยากลำบากของ "การแบ่งส่วนความผิดพลาด" เขาพูดว่า

[segfaults นั้นยากจนกระทั่งคุณ] "หายใจลึก ๆ และพยายามบังคับจิตใจของคุณให้ทำงานในระดับนามธรรมที่แตกต่างกันสองระดับพร้อมกัน"

จากรายการสาเหตุทั่วไปของsegfaults ฉันไม่เข้าใจว่าเราต้องทำงานในระดับนามธรรมได้อย่างไร

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


31
หยุดกังวลว่า Joel อาจคิดอย่างไรกับคุณ หากคุณพบว่าการเรียกซ้ำเป็นเรื่องง่ายนั่นเป็นเรื่องดี ไม่ใช่ทุกคนที่ทำ
FrustratedWithFormsDesigner

6
การเรียกซ้ำเป็นเรื่องง่ายตามคำจำกัดความ (ฟังก์ชั่นที่เรียกว่าตัวเอง) แต่การรู้ว่าจะใช้เมื่อไรและวิธีการทำให้มันใช้งานได้นั้นเป็นส่วนที่ยากลำบาก
JeffO

9
สมัครงานที่ Fog Creek และแจ้งให้เราทราบว่ามันเป็นอย่างไร เราทุกคนสนใจในการโปรโมตตนเองของคุณ
Joel Etherton

4
@ P.Brian.Mackey: เราไม่ได้เข้าใจผิด คำถามไม่ได้ถามอะไรเลย มันเป็นการส่งเสริมตนเองที่เห็นได้ชัด หากคุณต้องการทราบว่าโจเอลถามเกี่ยวกับพอยน์เตอร์ / การสอบถามซ้ำถามเขาหรือไม่: team@stackoverflow.com
Joel Etherton

19
คำถามนี้ซ้ำหรือไม่
ozz

คำตอบ:


38

ฉันแรกสังเกตเห็นว่าตัวชี้และการเรียกซ้ำยากในวิทยาลัย ฉันเรียนหลักสูตรปีแรกทั่วไปสองสามครั้ง (อันหนึ่งคือ C และ Assembler และอีกอันอยู่ใน Scheme) ทั้งสองหลักสูตรเริ่มต้นจากนักเรียนหลายร้อยคนซึ่งหลายคนมีประสบการณ์การเขียนโปรแกรมระดับมัธยมปลายเป็นเวลาหลายปี (โดยทั่วไปคือ BASIC และ Pascal ในสมัยนั้น) แต่ทันทีที่มีการนำพอยน์เตอร์ไปใช้ในหลักสูตร C และมีการแนะนำการเรียกซ้ำในหลักสูตร Scheme นักเรียนจำนวนมาก - บางทีอาจเป็นคนส่วนใหญ่ก็ล้มเหลวอย่างสิ้นเชิง เด็กเหล่านี้เคยเขียนโค้ดจำนวนมากมาก่อนและไม่มีปัญหาเลย แต่เมื่อพวกเขาไปถึงพอยน์เตอร์และการเรียกซ้ำพวกเขาก็ชนกำแพงในแง่ของความสามารถทางปัญญาของพวกเขา

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

  • ด้วยพอยน์เตอร์ "ข้อมูลนามธรรมสองระดับ" คือ "ข้อมูลที่อยู่ของข้อมูลที่อยู่ของข้อมูล ฯลฯ " หรือที่เราเรียกกันว่า "ค่าเทียบกับการอ้างอิง" เพื่อให้นักเรียนได้รับการฝึกฝนเป็นเรื่องยากมากที่จะเห็นความแตกต่างระหว่างที่อยู่ของ xและx ตัวเอง
  • ด้วยการเรียกซ้ำ "สิ่งที่เป็นนามธรรมสองระดับ" นั้นเข้าใจว่าเป็นไปได้อย่างไรที่ฟังก์ชันจะเรียกตัวมันเอง อัลกอริทึมแบบเรียกซ้ำคือบางครั้งสิ่งที่ผู้คนเรียกว่า "การเขียนโปรแกรมด้วยการคิดอย่างปรารถนา" และเป็นเรื่องแปลกประหลาดมากที่คิดว่าอัลกอริทึมในแง่ของ "กรณีฐาน + กรณีอุปนัย" แทนที่จะเป็นรายการธรรมชาติมากขึ้น ." เพื่อให้นักเรียนได้รับการฝึกฝนมองไปที่ขั้นตอนวิธี recursive อัลกอริทึมที่ดูเหมือนจะขอคำถาม

ฉันก็เต็มใจที่จะยอมรับอย่างสมบูรณ์ว่าเป็นไปได้ที่จะสอนพอยน์เตอร์และ / หรือเรียกร้องให้ทุกคน ... ฉันไม่มีหลักฐานใด ๆ ไม่ทางใดก็ทางหนึ่ง ฉันรู้ว่าสังเกตุความสามารถในการเข้าใจแนวคิดทั้งสองนี้เป็นตัวทำนายที่ดีมากของความสามารถในการเขียนโปรแกรมทั่วไปและในหลักสูตรปกติของการฝึกอบรมระดับปริญญาตรี CS แนวความคิดทั้งสองนี้เป็นอุปสรรคที่ใหญ่ที่สุด


4
"มากผิดธรรมชาติมากที่คิดว่าอัลกอริทึมในแง่ของ" case base + case inductive "" - ฉันคิดว่ามันไม่ผิดธรรมชาติเลยมันเป็นแค่เด็ก ๆ ที่ไม่ได้รับการฝึกฝนตามนั้น
Ingo

14
ถ้ามันเป็นเรื่องธรรมชาติคุณไม่จำเป็นต้องฝึกฝน : P
Joel Spolsky

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

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

2
ฉันคิดว่าการไม่สามารถเข้าใจตัวชี้และการเรียกซ้ำถูกเชื่อมโยงกับ) ระดับ IQ โดยรวมและ b) การศึกษาคณิตศาสตร์ที่ไม่ดี
quant_dev

23

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

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


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

recursion gotoร่วมกันระหว่างหลายหน้าที่เป็นหลักเช่นเดียวกับ
starblue

2
@ starblue ไม่ใช่จริง ๆ - เนื่องจากแต่ละ stackframe สร้างอินสแตนซ์ใหม่ของตัวแปรโลคัล
Charles Salvia

คุณขวาเพียงหาง recursion gotoเป็นเช่นเดียวกับ
starblue

3
@wnoise int a() { return b(); }สามารถ recursive bแต่มันขึ้นอยู่กับความหมายของ ดังนั้นมันจึงไม่ง่ายอย่างที่คิด ...
ทางเลือก

14

Java รองรับตัวชี้ (เรียกว่าการอ้างอิง) และรองรับการเรียกซ้ำ ดังนั้นบนพื้นผิวข้อโต้แย้งของเขาดูเหมือนไม่มีจุดหมาย

สิ่งที่เขาพูดถึงจริงๆคือความสามารถในการแก้ไขข้อบกพร่อง จาวาตัวชี้ (ข้อผิดพลาดอ้างอิง) รับประกันว่าจะชี้ไปยังวัตถุที่ถูกต้อง ตัวชี้ AC ไม่ใช่ และเคล็ดลับในการเขียนโปรแกรม C โดยสมมติว่าคุณไม่ได้ใช้เครื่องมือเช่นvalgrindคือการหาตำแหน่งที่คุณทำให้ตัวชี้เกิดความผิดพลาดขึ้นมา


5
ตัวชี้ต่อ se เป็นรายละเอียด การใช้การอ้างอิงใน Java นั้นไม่ซับซ้อนกว่าการใช้ตัวแปรโลคอลใน C แม้การผสมในวิธีการใช้งาน Lisp (อะตอมอาจเป็นจำนวนเต็มขนาด จำกัด หรืออักขระหรือตัวชี้) ไม่ยาก มันยากขึ้นเมื่อภาษาอนุญาตให้มีการเรียงลำดับของข้อมูลเดียวกันในท้องที่หรืออ้างอิงด้วยไวยากรณ์ที่แตกต่างกันและมีขนดกจริง ๆ เมื่อภาษาอนุญาตให้ใช้คณิตศาสตร์พอยน์เตอร์
David Thornley

@ David - อืม, สิ่งนี้เกี่ยวข้องกับการตอบสนองของฉันอย่างไร?
Anon

1
ความคิดเห็นของคุณเกี่ยวกับตัวชี้การสนับสนุน Java
David Thornley

"ที่ซึ่งคุณเมาตัวชี้ (แทบจะไม่ถึงจุดที่พบในสแต็คแท็ก)" หากคุณโชคดีพอที่จะรับสแต็คเทรซ
Omega Centauri

5
ฉันเห็นด้วยกับ David Thornley; Java ไม่สนับสนุนพอยน์เตอร์เว้นแต่ว่าฉันสามารถสร้างตัวชี้ไปยังตัวชี้ไปยังตัวชี้ไปยังตัวชี้ไปยัง int ซึ่งบางทีฉันคิดว่าฉันสามารถทำได้โดยการทำเหมือน 4-5 ชั้นที่แต่ละคนอ้างถึงอย่างอื่น แต่นั่นเป็นตัวชี้หรือว่าเป็นวิธีแก้ปัญหาที่น่าเกลียด?
ทางเลือก

12

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

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

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


ฉันจะเห็นด้วยฉันมี Fortran ปี / ทศวรรษก่อนที่จะเห็นตัวชี้ที่แท้จริงดังนั้นฉันได้ใช้วิธีการของตัวเองในการทำสิ่งเดียวกันแล้วก่อนที่จะได้รับโอกาสให้ lanquage / คอมไพเลอร์ทำเพื่อฉัน ฉันยังคิดว่าไวยากรณ์ C เกี่ยวกับพอยน์เตอร์ / ที่อยู่สับสนมากแม้ว่าแนวคิดของค่าที่เก็บไว้ที่ที่อยู่นั้นง่ายมาก
Omega Centauri

หากคุณมีลิงก์ไปยัง Quicksort ที่ใช้งานใน Fortran IV ฉันชอบที่จะเห็นมัน ไม่ได้บอกว่ามันไม่สามารถทำได้จริง ๆ แล้วฉันใช้มันใน BASIC เมื่อ 30 ปีที่แล้ว - แต่ฉันสนใจที่จะดู
Anon

ฉันไม่เคยทำงานใน Fortran IV แต่ฉันใช้อัลกอริธึมแบบวนซ้ำในการใช้งาน VAX / VMS ของ Fortran 77 (มีตะขอให้คุณบันทึกเป้าหมายของ goto เป็นตัวแปรชนิดพิเศษดังนั้นคุณสามารถเขียนได้GOTO target) . ฉันคิดว่าเราต้องสร้างกองรันไทม์ของเราเอง นานมาแล้วที่ฉันจำรายละเอียดไม่ได้อีกแล้ว
John Bode

8

พอยน์เตอร์มีปัญหาหลายอย่าง:

  1. aliasingความเป็นไปได้ของการเปลี่ยนค่าของวัตถุโดยใช้ชื่อ / ตัวแปรต่างกัน
  2. Non-ท้องที่ความเป็นไปได้ของการเปลี่ยนค่าวัตถุในบริบทที่แตกต่างจากที่มันถูกประกาศ (สิ่งนี้เกิดขึ้นกับข้อโต้แย้งที่ส่งผ่านโดยการอ้างอิง)
  3. อายุการใช้งานไม่ตรงกันอายุการใช้งานของตัวชี้อาจแตกต่างจากอายุการใช้งานของวัตถุที่ชี้ไปและอาจนำไปสู่การอ้างอิงที่ไม่ถูกต้อง (SEGFAULTS) หรือขยะ
  4. ชี้เลขคณิต ภาษาการเขียนโปรแกรมบางภาษาอนุญาตให้จัดการพอยน์เตอร์เป็นจำนวนเต็มและนั่นหมายความว่าพอยน์เตอร์สามารถชี้ได้ทุกที่ (รวมถึงสถานที่ที่ไม่คาดคิดที่สุดเมื่อมีบั๊ก) ในการใช้ตัวชี้ทางคณิตศาสตร์อย่างถูกต้องโปรแกรมเมอร์ต้องระวังขนาดหน่วยความจำของวัตถุที่ชี้ไปและนั่นเป็นสิ่งที่คิดมาก
  5. Type Castsความสามารถในการร่ายพอยน์เตอร์จากประเภทหนึ่งไปยังอีกประเภทหนึ่งช่วยให้สามารถเขียนทับหน่วยความจำของวัตถุที่แตกต่างจากที่ตั้งใจไว้ได้

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

Pair* make_pair(int a, int b)
{
    Pair p;
    p.a = a;
    p.b = b;
    return &p;
}

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

ความยากลำบากกับการทำงาน recursive ที่เกิดขึ้นเมื่อคนที่ไม่มีพื้นฐานทางคณิตศาสตร์พอ (ที่ recursiveness เป็นปกติและความรู้ที่จำเป็น) พยายามที่จะเข้าใกล้พวกเขาคิดว่าการทำงานจะทำงานแตกต่างกันขึ้นอยู่กับว่าหลายครั้งที่จะได้รับการเรียกก่อน ปัญหานั้นรุนแรงขึ้นเพราะฟังก์ชั่นวนซ้ำสามารถสร้างขึ้นในรูปแบบที่คุณต้องคิดด้วยวิธีนั้นเพื่อทำความเข้าใจกับมัน

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

มันไม่ได้เป็นที่กล่าวถึงในคำถาม แต่ปัญหาที่สำคัญอื่น ๆ ที่สามเณรมีความยากลำบากเป็นเห็นพ้องด้วย

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


การใช้ฟังก์ชั่นนั้นจะส่งกลับตัวชี้ที่ถูกต้อง แต่ตัวแปรนั้นอยู่ในขอบเขตที่สูงกว่าขอบเขตที่เรียกใช้ฟังก์ชันดังนั้นตัวชี้สามารถ (สมมติว่าจะ) ใช้งานไม่ได้เมื่อใช้ malloc
rightfold

4
@Radek S: ไม่ไม่ได้ มันจะส่งคืนพอยน์เตอร์ที่ไม่ถูกต้องซึ่งในบางสภาพแวดล้อมจะทำงานได้ชั่วขณะหนึ่งจนกว่าจะมีสิ่งอื่นเขียนทับ (ในทางปฏิบัตินี้จะเป็นสแต็คที่ไม่ได้กอง. malloc()ไม่มีจะมีโอกาสมากกว่าฟังก์ชั่นอื่น ๆ ที่จะทำเช่นนั้น.)
wnoise

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

อย่างไรก็ตาม Rust มีพอยน์เตอร์ แต่ไม่มีปัญหาเหล่านี้ (เมื่อไม่อยู่ในบริบทที่ไม่ปลอดภัย)
Sarge Borsch

2

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

โดยทั่วไปพอยน์เตอร์ต้องการแบบจำลองทางจิตที่แตกต่างจากการกำหนดตัวแปรบริสุทธิ์ เมื่อฉันมีตัวแปรพอยน์เตอร์มันเป็นเพียงแค่: ตัวชี้ไปยังวัตถุอื่นข้อมูลเดียวที่มีอยู่คือที่อยู่หน่วยความจำที่ชี้ไปที่ ตัวอย่างเช่นถ้าฉันมีตัวชี้ int32 และกำหนดค่าโดยตรงฉันไม่เปลี่ยนค่าของ int ฉันกำลังชี้ไปยังที่อยู่หน่วยความจำใหม่ (มีเทคนิคมากมายที่คุณสามารถทำได้ ) สิ่งที่น่าสนใจยิ่งกว่าคือการมีตัวชี้ไปยังตัวชี้ (นี่คือสิ่งที่เกิดขึ้นเมื่อคุณส่งตัวแปร Ref เป็นฟังก์ชันพารามิเตอร์ใน C # ฟังก์ชันสามารถกำหนดวัตถุที่แตกต่างอย่างสิ้นเชิงให้กับพารามิเตอร์และค่านั้นจะยังคงอยู่ในขอบเขตเมื่อฟังก์ชัน ทางออก

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

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


1

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

make a burger:
   put a cold burger on the grill
   wait
   flip
   wait
   hand the fried burger over to the service personel
   unless its end of shift: make a burger

แน่นอนว่าการขาดความเข้าใจก็เกี่ยวข้องกับโรงเรียนของเราเช่นกัน ที่นี่เราควรแนะนำตัวเลขธรรมชาติเช่น Peano, Dedekind และ Frege ทำดังนั้นเราจะไม่ได้มีปัญหามากในภายหลัง


6
นั่นคือการหลอมหางซึ่งเป็นการวนลูป
Michael K

6
ขออภัยกับผมวนลูปเป็น arguably recursion หาง :)
Ingo

3
@Ingo: :) ฟังก์ชั่นคลั่ง!
Michael K

1
@Michael - เฮ้จริง ๆ ! แต่ฉันคิดว่าใครสามารถทำกรณีที่เรียกซ้ำเป็นแนวคิดพื้นฐานเพิ่มเติม
Ingo

@Ingo: แน่นอน (ตัวอย่างของคุณแสดงให้เห็นว่าดี) อย่างไรก็ตามด้วยเหตุผลบางอย่างมนุษย์มีช่วงเวลาที่ยากลำบากในการเขียนโปรแกรม - เราดูเหมือนจะต้องการสิ่งนั้นgoto topด้วยเหตุผลบางอย่างสำหรับ IME
Michael K

1

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

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

การเรียกซ้ำเป็นปัญหาเนื่องจากมีสองช่วงแนวคิดในการทำความเข้าใจ สิ่งแรกคือที่ระดับเครื่องและเหมือนกับตัวชี้ที่สามารถเอาชนะได้โดยการพัฒนาความเข้าใจที่ดีของวิธีการจัดเก็บและดำเนินการโปรแกรมจริง ฉันคิดว่าอีกปัญหาของการเรียกซ้ำคือว่าคนมีแนวโน้มตามธรรมชาติที่จะพยายามแยกแยะปัญหาที่เกิดขึ้นซ้ำ ๆ ให้กลายเป็นปัญหาที่ไม่เกิดซ้ำซึ่งทำให้ความเข้าใจของฟังก์ชันเวียนเกิดขึ้น นี่อาจเป็นปัญหากับคนที่มีพื้นฐานทางคณิตศาสตร์ไม่เพียงพอหรือแบบจำลองทางจิตที่ไม่ได้ผูกทฤษฎีทางคณิตศาสตร์กับการพัฒนาโปรแกรม

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


1
  DATA    |     CODE
          |
 pointer  |   recursion    SELF REFERENTIAL
----------+---------------------------------
 objects  |   macro        SELF MODIFYING
          |
          |

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

แนวคิดของการแก้ไขข้อมูลและรหัสภายใต้คำจำกัดความของวัตถุ (เช่นข้อมูลอัจฉริยะ) และมาโครตามลำดับ ฉันพูดถึงสิ่งเหล่านี้เนื่องจากยากต่อการเข้าใจโดยเฉพาะอย่างยิ่งเมื่อคาดหวังความเข้าใจในการปฏิบัติงานของรันไทม์จากการรวมกันของแนวคิดทั้งสี่ - เช่นแมโครสร้างชุดของวัตถุที่ใช้ parser ที่เหมาะสมแบบเรียกซ้ำด้วยความช่วยเหลือของต้นไม้พอยน์เตอร์ . แทนที่จะติดตามการดำเนินการทั้งหมดของสถานะของโปรแกรมทีละขั้นตอนผ่านทุกเลเยอร์ของ abstraction พร้อมกันโปรแกรมเมอร์ที่จำเป็นต้องเรียนรู้ที่จะไว้วางใจว่าตัวแปรของพวกเขาได้รับมอบหมายเพียงครั้งเดียวภายในฟังก์ชั่นบริสุทธิ์และการเรียกซ้ำของฟังก์ชันบริสุทธิ์เดียวกัน ข้อโต้แย้งเดียวกันให้ผลลัพธ์เหมือนกันเสมอ (เช่น referential transparent) แม้ในภาษาที่รองรับฟังก์ชั่นที่ไม่บริสุทธิ์เช่น Java การวิ่งวนเป็นวงกลมหลังจากรันไทม์เป็นความพยายามที่ไร้ผล สิ่งที่เป็นนามธรรมควรทำให้ง่ายขึ้น


-1

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

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

ในทำนองเดียวกันกับการสอบถามซ้ำ มันอาจเป็นวิธีที่ทรงพลังมากในการจัดระเบียบสิ่งต่าง ๆ โดยการบรรจุความลับเข้าไปในโครงสร้างข้อมูลที่ซ่อนอยู่ (กองซ้อน)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.