คอมพิวเตอร์ หน้าต่าง อินเทอร์เน็ต

โปรแกรมการศึกษาเกี่ยวกับการพิมพ์ในภาษาโปรแกรม ทุกภาษา - เหตุใดจึงต้องพิมพ์แบบไดนามิก การพิมพ์ที่แข็งแกร่งและอ่อนแอ

ทุกอย่างง่ายมาก มันเหมือนกับความแตกต่างระหว่างโรงแรมและอพาร์ตเมนต์ส่วนตัว

เฉพาะผู้ที่ลงทะเบียนที่นั่นเท่านั้นที่อาศัยอยู่ในอพาร์ตเมนต์ หากพูดว่าครอบครัว Sidorov อาศัยอยู่ในนั้นครอบครัว Pupkin ก็ไม่สามารถอยู่ที่นั่นตลอดชีวิตของเราได้ ในเวลาเดียวกัน Petya Sidorov สามารถอาศัยอยู่ในอพาร์ทเมนต์นี้ได้จากนั้น Grisha Sidorov ก็สามารถย้ายไปที่นั่นได้ (บางครั้งพวกเขาสามารถอาศัยอยู่ที่นั่นในเวลาเดียวกัน - นี่คืออาร์เรย์) นี่คือการพิมพ์แบบคงที่

ครอบครัว Sidorov สามารถอาศัยอยู่ในโรงแรมในคราวเดียวได้ พวกเขาไม่จำเป็นต้องลงทะเบียนที่นั่นเสมอไป จากนั้นพวกเขาจะออกไปที่นั่น และพวกพัพกินส์จะย้ายไปที่นั่น แล้วก็พวก Kuznetsov แล้วคนอื่นล่ะ. นี่คือการพิมพ์แบบไดนามิก

หากเรากลับไปที่การเขียนโปรแกรมก็จะพบกรณีแรก (การพิมพ์แบบคงที่) เช่นภาษา C, C++, C#, Java และอื่น ๆ ก่อนที่คุณจะกำหนดค่าให้กับตัวแปรเป็นครั้งแรก คุณต้องบอกว่าคุณจะเก็บอะไรไว้ที่นั่น เช่น จำนวนเต็ม ตัวเลขทศนิยม สตริง ฯลฯ ( Sidorovs จะอาศัยอยู่ในอพาร์ตเมนต์นี้). ในทางกลับกัน การพิมพ์แบบไดนามิกไม่ต้องการสิ่งนี้ เมื่อคุณกำหนดค่า คุณจะกำหนดประเภทของตัวแปรไปพร้อมๆ กัน ( ตอนนี้ Vasya Pupkin จากครอบครัว Pupkin อาศัยอยู่ในห้องพักของโรงแรมแห่งนี้). พบได้ในภาษาต่างๆ เช่น PHP, Python และ JavaScript

ทั้งสองวิธีมีข้อดีและข้อเสีย อันไหนดีกว่าหรือแย่กว่านั้นขึ้นอยู่กับปัญหาที่กำลังแก้ไข คุณสามารถอ่านรายละเอียดเพิ่มเติมได้ใน Wikipedia

ด้วยการพิมพ์แบบคงที่ คุณจะทราบประเภทของตัวแปรในขณะที่เขียนโปรแกรมและพัฒนาอัลกอริทึมอย่างแม่นยำและคำนึงถึงสิ่งนี้ด้วย เหล่านั้น. หากคุณบอกว่าตัวแปร G เป็นจำนวนเต็มสี่ไบต์ที่ไม่ได้ลงชื่อ ดังนั้นในอัลกอริทึมมันจะเป็นจำนวนเต็มสี่ไบต์ที่ไม่ได้ลงนามเสมอ (หากมีสิ่งใด คุณจะต้องแปลงมันอย่างชัดเจนหรือรู้ว่านักแปลแปลงมันในค่าใดค่าหนึ่งอย่างไร ช่วงของสถานการณ์ แต่ส่วนใหญ่หากมีประเภทไม่ตรงกันจะเป็นข้อผิดพลาดในอัลกอริทึมและอย่างน้อยคอมไพเลอร์จะเตือนคุณ) ด้วยรูปแบบคงที่คุณไม่สามารถใส่สตริง "Vasya the Fool" ลงในตัวเลขได้ และการตรวจสอบเพิ่มเติมก่อนที่จะใช้ตัวแปรเพื่อพิจารณาว่า "มีตัวเลขอยู่หรือไม่" คุณดำเนินการความถูกต้องของข้อมูลทั้งหมด ณ เวลาที่เข้าสู่โปรแกรมหรือตามที่กำหนดโดยอัลกอริทึมเอง

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

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



ประเภทคือชุดของค่าที่เป็นไปได้ จำนวนเต็มสามารถมีค่า 0, 1, 2, 3 และอื่นๆ บูลีนอาจเป็นจริงหรือเท็จก็ได้ คุณสามารถสร้างประเภทของคุณเองได้ เช่น ประเภท "High Five" ซึ่งค่าที่เป็นไปได้คือ "สูง" และ "5" และไม่มีอะไรอื่นอีก ไม่ใช่สตริงหรือตัวเลข แต่เป็นชนิดใหม่ที่แยกจากกัน


ภาษาที่พิมพ์แบบคงที่จะจำกัดประเภทของตัวแปร เช่น ภาษาการเขียนโปรแกรมอาจรู้ว่า x เป็นจำนวนเต็ม ในกรณีนี้โปรแกรมเมอร์ห้ามทำ x = true ซึ่งจะเป็นโค้ดที่ไม่ถูกต้อง คอมไพเลอร์จะปฏิเสธที่จะคอมไพล์ ดังนั้นเราจะไม่สามารถรันโค้ดได้ ภาษาที่พิมพ์แบบคงที่อีกภาษาหนึ่งอาจมีความสามารถในการแสดงออกที่แตกต่างกัน และไม่มีระบบการพิมพ์ที่ได้รับความนิยมใดที่สามารถแสดงประเภท HighFive ของเราได้ (แต่หลายคนสามารถแสดงแนวคิดอื่นๆ ที่มีความซับซ้อนมากกว่าได้)


ภาษาที่พิมพ์แบบไดนามิกทำเครื่องหมายค่าด้วยประเภท: ภาษารู้ว่า 1 เป็นจำนวนเต็ม 2 เป็นจำนวนเต็ม แต่ไม่สามารถรู้ได้ว่าตัวแปร x มีจำนวนเต็มเสมอ


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

ภาษาที่พิมพ์แบบคงที่

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


ภาษาที่พิมพ์แบบคงที่จำนวนมากจำเป็นต้องมีการระบุประเภท ฟังก์ชั่น Java public int add(int x, int y) รับจำนวนเต็มสองตัวและส่งกลับจำนวนเต็มตัวที่สาม ภาษาที่พิมพ์แบบคงที่อื่นๆ อาจอนุมานประเภทโดยอัตโนมัติ ฟังก์ชั่นการบวกแบบเดียวกันใน Haskell มีลักษณะดังนี้: เพิ่ม x y = x + y เราไม่ได้บอกประเภทภาษา แต่สามารถคิดออกเองได้เพราะรู้ว่า + ใช้ได้กับตัวเลขเท่านั้น ดังนั้น x และ y ต้องเป็นตัวเลข ดังนั้นฟังก์ชัน add จะใช้ตัวเลขสองตัวเป็นอาร์กิวเมนต์


สิ่งนี้ไม่ได้ลดลักษณะ "คงที่" ของระบบประเภท ระบบประเภทของ Haskell มีชื่อเสียงในด้านความคงที่ เข้มงวด และทรงพลัง และ Haskell ก็นำหน้า Java ในทุกด้าน

ภาษาที่พิมพ์แบบไดนามิก

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


def f(x, y): กลับ x + y

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

การเปรียบเทียบ

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


ภาษาคงที่และไดนามิกสร้างขึ้นจากแนวคิดพื้นฐานที่แตกต่างกันเกี่ยวกับความถูกต้องของโปรแกรม ในภาษาไดนามิก "a" + 1 เป็นโปรแกรมที่ถูกต้อง: รหัสจะทำงานและข้อผิดพลาดจะปรากฏขึ้นในสภาพแวดล้อมรันไทม์ อย่างไรก็ตามในภาษาที่พิมพ์แบบคงที่ส่วนใหญ่นิพจน์ "a" + 1 คือ ไม่ใช่โปรแกรม: มันจะไม่คอมไพล์และจะไม่ทำงาน นี่เป็นรหัสที่ไม่ถูกต้อง เช่นเดียวกับชุดอักขระแบบสุ่ม!&%^@*&%^@* เป็นรหัสที่ไม่ถูกต้อง แนวคิดเพิ่มเติมเกี่ยวกับความถูกต้องและไม่ถูกต้องไม่มีความเทียบเท่าในภาษาไดนามิก

การพิมพ์ที่แข็งแกร่งและอ่อนแอ

แนวคิดเรื่อง "แข็งแกร่ง" และ "อ่อนแอ" นั้นคลุมเครือมาก นี่คือตัวอย่างการใช้งาน:

    บางครั้ง "แข็งแกร่ง" หมายถึง "คงที่"
    ง่ายๆ แต่ใช้คำว่า "คงที่" ดีกว่า เพราะคนส่วนใหญ่ใช้และเข้าใจ

    บางครั้ง "strong" หมายถึง "ไม่ทำการแปลงประเภทโดยนัย"
    ตัวอย่างเช่น JavaScript อนุญาตให้คุณเขียน "a" + 1 ซึ่งอาจเรียกว่า "การพิมพ์ที่อ่อนแอ" แต่เกือบทุกภาษามีการแปลงโดยนัยในระดับหนึ่งซึ่งช่วยให้คุณสามารถแปลงจากจำนวนเต็มเป็นตัวเลขทศนิยมเช่น 1 + 1.1 ได้โดยอัตโนมัติ ในความเป็นจริง คนส่วนใหญ่ใช้คำว่า "เข้มแข็ง" เพื่อกำหนดขอบเขตระหว่างการเปลี่ยนใจเลื่อมใสที่ยอมรับได้และที่ยอมรับไม่ได้ ไม่มีขอบเขตที่ยอมรับโดยทั่วไป ขอบเขตทั้งหมดไม่ชัดเจนและขึ้นอยู่กับความคิดเห็นของบุคคลใดบุคคลหนึ่ง

    บางครั้ง "แรง" หมายความว่าเป็นไปไม่ได้ที่จะข้ามกฎการพิมพ์ที่เข้มงวดของภาษา

  • บางครั้ง "แข็งแกร่ง" หมายถึงปลอดภัยต่อหน่วยความจำ
    C เป็นตัวอย่างของภาษาที่ไม่ปลอดภัยต่อหน่วยความจำ หาก xs เป็นอาร์เรย์ของตัวเลขสี่ตัว ดังนั้น C จะดำเนินการอย่างมีความสุข xs หรือ xs โดยส่งคืนค่าบางส่วนจากหน่วยความจำที่อยู่ด้านหลัง xs

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



("เมื่อเป็น" ในคอลัมน์ "Conversion โดยนัย" หมายความว่าการแบ่งระหว่างมากและน้อยขึ้นอยู่กับ Conversion ที่เราพิจารณาว่ายอมรับได้)


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


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



การพิมพ์ดีเด่น: ระบบการพิมพ์ที่ฉันชอบและสบายใจ

การพิมพ์ที่อ่อนแอ: ระบบการพิมพ์ที่รบกวนจิตใจฉันหรือฉันไม่สบายใจ

การพิมพ์แบบค่อยเป็นค่อยไป

เป็นไปได้หรือไม่ที่จะเพิ่มประเภทคงที่ให้กับภาษาไดนามิก? ในบางกรณี - ใช่ ในบางเรื่องมันยากหรือเป็นไปไม่ได้ ปัญหาที่ชัดเจนที่สุดคือการประเมินและคุณลักษณะอื่นๆ ที่คล้ายกันของภาษาไดนามิก การทำ 1 + eval("2") ใน Python ให้ผลตอบแทน 3 แต่ 1 + eval(read_from_the_network()) ให้ผลตอบแทนอะไร ขึ้นอยู่กับสิ่งที่ออนไลน์อยู่ ณ เวลาที่ดำเนินการ หากเราได้รับตัวเลขแสดงว่านิพจน์นั้นถูกต้อง ถ้าเป็นเส้นคงไม่ใช่ ไม่มีทางรู้ก่อนรัน ดังนั้นจึงไม่สามารถแยกวิเคราะห์ประเภทแบบคงที่ได้


วิธีแก้ปัญหาที่ไม่น่าพอใจในทางปฏิบัติคือการตั้งค่านิพจน์ eval() ให้พิมพ์ Any ซึ่งคล้ายกับ Object ในภาษาโปรแกรมเชิงวัตถุบางภาษาหรือ interface() ใน Go: เป็นประเภทที่ค่าใดๆ ก็สามารถพึงพอใจได้


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


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


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

รวบรวมรหัสพิมพ์แบบคงที่

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


คอมไพเลอร์สำหรับภาษาคงที่มักจะสามารถสร้างโค้ดได้เร็วกว่าคอมไพเลอร์สำหรับภาษาไดนามิก ตัวอย่างเช่น หากคอมไพเลอร์รู้ว่าฟังก์ชัน add ยอมรับจำนวนเต็ม ก็สามารถใช้คำสั่ง ADD ดั้งเดิมของ CPU ได้ ภาษาไดนามิกจะตรวจสอบประเภทในขณะรันไทม์ โดยเลือกจากฟังก์ชันเพิ่มที่หลากหลาย ขึ้นอยู่กับประเภท (เพิ่มจำนวนเต็มหรือจำนวนลอย หรือต่อสตริงหรืออาจเป็นรายการ) หรือจำเป็นต้องตัดสินใจว่ามีข้อผิดพลาดเกิดขึ้นและประเภทไม่ตรงกัน . การตรวจสอบทั้งหมดนี้ต้องใช้เวลา ภาษาไดนามิกใช้เทคนิคต่างๆ ในการปรับให้เหมาะสม เช่น การคอมไพล์ JIT (ทันเวลาพอดี) โดยที่โค้ดจะถูกคอมไพล์ใหม่ขณะรันไทม์หลังจากได้รับข้อมูลประเภทที่จำเป็นทั้งหมดแล้ว อย่างไรก็ตาม ไม่มีภาษาไดนามิกใดที่สามารถเทียบได้กับความเร็วของโค้ดคงที่ที่เขียนอย่างประณีตในภาษาอย่าง Rust

อาร์กิวเมนต์สำหรับประเภทคงที่และไดนามิก

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


ผู้เสนอภาษาไดนามิกชี้ให้เห็นว่าภาษาดังกล่าวดูเหมือนจะเขียนโค้ดได้ง่ายกว่า นี่เป็นเรื่องจริงอย่างแน่นอนสำหรับโค้ดบางประเภทที่เราเขียนเป็นครั้งคราว เช่น โค้ด eval นี่เป็นวิธีแก้ปัญหาที่ขัดแย้งกันสำหรับงานประจำและที่นี่ควรจำคำที่คลุมเครือว่า "ง่าย" Rich Hickey ทำได้ดีมากโดยพูดถึงคำว่า "ง่าย" และความเชื่อมโยงกับคำว่า "เรียบง่าย" ดูรายงานนี้แล้วจะเข้าใจว่าการใช้คำว่า “ง่าย” ให้ถูกต้องไม่ใช่เรื่องง่าย ระวัง "ง่าย"


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


JavaScript พยายามดำเนินการต่อแม้ว่าจะหมายถึงการแปลงที่ไม่มีความหมายก็ตาม (เช่น "a" + 1 ส่งผลให้เป็น "a1") ในทางกลับกัน Python พยายามที่จะอนุรักษ์นิยมและมักจะส่งคืนข้อผิดพลาด เช่นในกรณีของ "a" + 1


มีแนวทางที่แตกต่างกันโดยมีระดับความปลอดภัยต่างกัน แต่ Python และ JavaScript เป็นภาษาที่พิมพ์แบบไดนามิก



Haskell จะไม่อนุญาตให้คุณเพิ่มจำนวนเต็มและจำนวนทศนิยมโดยไม่มีการแปลงอย่างชัดเจนก่อน C และ Haskell ต่างก็พิมพ์แบบคงที่ แม้ว่าจะมีความแตกต่างกันมากก็ตาม


ภาษาแบบไดนามิกและแบบคงที่มีหลากหลายรูปแบบ ข้อความแบบครอบคลุมใดๆ เช่น "ภาษาแบบคงที่จะดีกว่าภาษาไดนามิกเมื่อพูดถึง X" เกือบจะรับประกันได้ว่าเป็นเรื่องไร้สาระ สิ่งนี้อาจเป็นจริงในกรณีของภาษาใดภาษาหนึ่ง แต่จะดีกว่าถ้าพูดว่า "Haskell ดีกว่า Python เมื่อพูดถึง X"

ระบบการพิมพ์แบบคงที่ที่หลากหลาย

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


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


Go ยังไม่มีฟีเจอร์อื่นๆ มากมายที่พบในภาษาที่พิมพ์แบบคงที่สมัยใหม่ (หรือแม้แต่บางระบบจากปี 1970) ผู้สร้าง Go มีเหตุผลในการตัดสินใจเหล่านี้ แต่ความคิดเห็นจากบุคคลภายนอกเกี่ยวกับเรื่องนี้บางครั้งอาจฟังดูรุนแรง


ทีนี้มาเปรียบเทียบกับ Haskell ซึ่งมีระบบการพิมพ์ที่ทรงพลังมาก หากคุณตั้งค่าประเภทเป็น MyList ประเภทของ "รายการตัวเลข" จะเป็นเพียงแค่ MyList Integer Haskell จะป้องกันไม่ให้เราเพิ่มสตริงลงในรายการโดยไม่ตั้งใจ และจะตรวจสอบให้แน่ใจว่าเราไม่ได้ใส่องค์ประกอบจากรายการลงในตัวแปรสตริง


Haskell สามารถแสดงความคิดเห็นที่ซับซ้อนมากขึ้นได้โดยตรงกับประเภทต่างๆ ตัวอย่างเช่น Num a => MyList a หมายถึง "MyList ของค่าที่เป็นของตัวเลขประเภทเดียวกัน" อาจเป็นรายการจำนวนเต็ม ทศนิยม หรือทศนิยมที่มีความแม่นยำคงที่ แต่จะไม่เป็นรายการสตริงซึ่งจะถูกตรวจสอบ ณ เวลาคอมไพล์อย่างแน่นอน


คุณสามารถเขียนฟังก์ชันเพิ่มที่ใช้ได้กับตัวเลขประเภทใดก็ได้ ฟังก์ชั่นนี้จะมีประเภท Num a => (a -> a -> a) มันหมายความว่า:

  • a สามารถเป็นตัวเลขประเภทใดก็ได้ (Num a =>)
  • ฟังก์ชันรับอาร์กิวเมนต์ประเภท a สองตัวและส่งกลับประเภท a (a -> a -> a)

ตัวอย่างสุดท้าย หากประเภทฟังก์ชันคือ String -> String แสดงว่ายอมรับสตริงและส่งกลับสตริง แต่ถ้าเป็น String -> IO String มันก็ทำ I/O บ้างเช่นกัน นี่อาจเป็นการเข้าถึงดิสก์ การเข้าถึงเครือข่าย การอ่านจากเทอร์มินัล และอื่นๆ


หากฟังก์ชันมีประเภท เลขที่ IO เรารู้ว่ามันไม่ได้ดำเนินการ I/O ใดๆ ตัวอย่างเช่น ในเว็บแอปพลิเคชัน คุณสามารถบอกได้ว่าฟังก์ชันแก้ไขฐานข้อมูลเพียงแค่ดูที่ประเภทของฟังก์ชันเท่านั้น ไม่มีภาษาไดนามิกและแทบไม่มีภาษาคงที่ใดที่สามารถทำได้ นี่คือคุณสมบัติของภาษาที่มีระบบการพิมพ์ที่ทรงพลังที่สุด


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


เปรียบเทียบพลังนี้กับ Go ซึ่งไม่สามารถแสดงแนวคิดง่ายๆ ของ MyList นับประสาอะไรกับ "ฟังก์ชันที่รับสองอาร์กิวเมนต์ ทั้งที่เป็นตัวเลขและเป็นประเภทเดียวกัน และนั่นรับ I/O"


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


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


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

ตัวอย่างเฉพาะของความแตกต่างในความสามารถของระบบการพิมพ์

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


ใน Go คุณสามารถพูดว่า "ฟังก์ชัน add รับจำนวนเต็มสองตัวและส่งกลับจำนวนเต็ม":


func เพิ่ม (x int, y int) int ( กลับ x + y )

ใน Haskell คุณสามารถพูดว่า "a function makes ใดๆประเภทตัวเลขและส่งกลับตัวเลขประเภทเดียวกัน":


f:: หมายเลข a => a -> a -> a เพิ่ม x y = x + y

ใน Idris คุณสามารถพูดว่า "ฟังก์ชันรับจำนวนเต็มสองตัวและส่งกลับจำนวนเต็ม แต่อาร์กิวเมนต์แรกต้องน้อยกว่าอาร์กิวเมนต์ที่สอง":


เพิ่ม: (x: Nat) -> (y: Nat) -> (เล็กลงอัตโนมัติ: LT x y) -> Nat เพิ่ม x y = x + y

หากคุณพยายามเรียกใช้ฟังก์ชันเพิ่ม 2 1 โดยที่อาร์กิวเมนต์แรกมากกว่าอาร์กิวเมนต์ที่สอง คอมไพลเลอร์จะปฏิเสธโปรแกรม ในเวลารวบรวม. เป็นไปไม่ได้ที่จะเขียนโปรแกรมโดยที่อาร์กิวเมนต์แรกมากกว่าอาร์กิวเมนต์ที่สอง ภาษาไม่ค่อยมีความสามารถนี้ ในภาษาส่วนใหญ่ การตรวจสอบนี้เกิดขึ้นเมื่อรันไทม์: เราจะเขียนบางอย่างเช่น if x >= y: Raise SomeError()


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

ระบบการพิมพ์ของภาษาคงที่บางภาษา

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

  • ซี (1972), โก (2009): ระบบเหล่านี้ไม่มีประสิทธิภาพเลย หากไม่มีการสนับสนุนประเภททั่วไป ไม่สามารถกำหนดประเภท MyList ที่จะหมายถึง "รายการจำนวนเต็ม", "รายการสตริง" ฯลฯ คุณจะต้องสร้าง "รายการค่าที่ไม่ได้กำหนด" แทน โปรแกรมเมอร์ต้องรายงาน "นี่คือรายการสตริง" ด้วยตนเองทุกครั้งที่ดึงสตริงจากรายการ และอาจส่งผลให้เกิดข้อผิดพลาดในการดำเนินการ
  • ชวา (1995), C# (2000): ทั้งสองภาษารองรับประเภททั่วไปดังนั้นคุณจึงสามารถพูด MyList ได้ และรับรายการสตริงที่คอมไพเลอร์รู้และสามารถบังคับใช้กฎประเภทได้ องค์ประกอบในรายการจะเป็นประเภท String และคอมไพเลอร์จะบังคับใช้กฎการคอมไพล์ตามปกติ ดังนั้นข้อผิดพลาดรันไทม์จึงมีโอกาสน้อยลง
  • ฮาสเคลล์ (1990), รัสต์ (2010), สวิฟต์ (2014): ภาษาเหล่านี้ทั้งหมดมีคุณสมบัติขั้นสูงหลายประการ รวมถึงประเภททั่วไป ประเภทข้อมูลพีชคณิต (ADT) และคลาสประเภทหรืออะไรที่คล้ายกัน (ประเภทคลาส ลักษณะ และโปรโตคอล ตามลำดับ) Rust และ Swift ได้รับความนิยมมากกว่า Haskell และได้รับการสนับสนุนจากองค์กรขนาดใหญ่ (Mozilla และ Apple ตามลำดับ)
  • อักดา (2550), ไอดริส (2554): ภาษาเหล่านี้รองรับประเภทที่ขึ้นต่อกันทำให้คุณสามารถสร้างประเภทเช่น "ฟังก์ชันที่รับจำนวนเต็มสองตัว x และ y โดยที่ y มากกว่า x" แม้แต่ข้อจำกัด "y มากกว่า x" ก็ถูกบังคับในระหว่างการคอมไพล์ เมื่อดำเนินการแล้ว y จะไม่น้อยกว่าหรือเท่ากับ x ไม่ว่าจะเกิดอะไรขึ้น คุณสมบัติที่ละเอียดอ่อนแต่สำคัญมากของระบบสามารถตรวจสอบได้ในภาษาเหล่านี้ มีโปรแกรมเมอร์เพียงไม่กี่คนที่ศึกษาพวกเขา แต่ภาษาเหล่านี้กระตุ้นความกระตือรือร้นอย่างมากในหมู่พวกเขา

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


กลุ่มที่สอง (Java และ C#) เป็นภาษากระแสหลัก เติบโตเต็มที่และใช้กันอย่างแพร่หลาย


กลุ่มที่สามกำลังเข้าสู่กระแสหลัก โดยได้รับการสนับสนุนอย่างดีจาก Mozilla (Rust) และ Apple (Swift)


กลุ่มที่ 4 (ไอดริสและอักดา) ยังห่างไกลจากกระแสหลัก แต่อาจมีการเปลี่ยนแปลงเมื่อเวลาผ่านไป ภาษากลุ่มสามยังห่างไกลจากกระแสหลักเมื่อสิบปีที่แล้ว

เพื่ออธิบายเทคโนโลยีที่แตกต่างกันอย่างสิ้นเชิงสองเทคโนโลยีให้ง่ายที่สุดเท่าที่จะเป็นไปได้ เรามาเริ่มกันตั้งแต่ต้นเลย สิ่งแรกที่โปรแกรมเมอร์พบเมื่อเขียนโค้ดคือการประกาศตัวแปร คุณอาจสังเกตเห็นว่า ตัวอย่างเช่น ในภาษาการเขียนโปรแกรม C++ คุณต้องระบุประเภทของตัวแปร นั่นคือ หากคุณประกาศตัวแปร x คุณจะต้องเพิ่ม int - สำหรับจัดเก็บข้อมูลจำนวนเต็ม, float - สำหรับจัดเก็บข้อมูลจุดลอยตัว, char - สำหรับข้อมูลอักขระ และประเภทอื่นๆ ที่มีอยู่ ดังนั้น C++ จึงใช้การพิมพ์แบบคงที่ เช่นเดียวกับ C รุ่นก่อน

การพิมพ์แบบคงที่ทำงานอย่างไร

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

ลองดูตัวอย่างเล็กๆ น้อยๆ เมื่อเริ่มต้นตัวแปร x (int x;) เราจะระบุตัวระบุ int - นี่เป็นคำย่อที่เก็บเฉพาะจำนวนเต็มในช่วงตั้งแต่ - 2,147,483,648 ถึง 2,147,483,647 ด้วยวิธีนี้คอมไพเลอร์รู้ว่ามันสามารถดำเนินการค่าทางคณิตศาสตร์ในสิ่งนี้ ตัวแปร - ผลรวม ผลต่าง การคูณ และการหาร แต่ตัวอย่างเช่น ฟังก์ชัน strcat() ซึ่งเชื่อมต่อค่าอักขระสองตัว ไม่สามารถใช้กับ x ได้ ท้ายที่สุดหากคุณลบข้อ จำกัด และพยายามเชื่อมต่อค่า int สองค่าโดยใช้วิธีสัญลักษณ์ก็จะเกิดข้อผิดพลาดขึ้น

เหตุใดจึงต้องมีภาษาที่พิมพ์แบบไดนามิก

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

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

พิมพ์แบบไหนดีกว่ากัน?

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

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

แบ่งเป็นประเภท "แข็งแกร่ง" และ "อ่อนแอ"

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

คุณสมบัติไดนามิก

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

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

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

สถาปัตยกรรมที่อยู่ติดกันเป็นไปได้หรือไม่?

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

แต่อย่างไรก็ตาม ในบางภาษา คุณสามารถเปลี่ยนการพิมพ์ได้โดยใช้เฟรมเวิร์กเพิ่มเติม

  • ในภาษาการเขียนโปรแกรม Delphi - ระบบย่อย Variant
  • ในภาษาการเขียนโปรแกรม AliceML มีแพ็คเกจเพิ่มเติม
  • ในภาษาการเขียนโปรแกรม Haskell - ไลบรารี Data.Dynamic

การพิมพ์ที่แข็งแกร่งจะดีกว่าการพิมพ์แบบไดนามิกเมื่อใด

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

ประโยชน์ของการพิมพ์แบบไดนามิก

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

เรียนรู้เพิ่มเติมเกี่ยวกับภาษาการเขียนโปรแกรมแบบคงที่

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

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

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

ข้อมูลเพิ่มเติมเกี่ยวกับภาษาโปรแกรมด้วยการพิมพ์แบบไดนามิก

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

  • PHP เป็นภาษาสำหรับสร้างสคริปต์ มีการใช้กันอย่างแพร่หลายในการพัฒนาเว็บ โดยจัดให้มีการโต้ตอบกับฐานข้อมูลเพื่อสร้างเว็บเพจไดนามิกเชิงโต้ตอบ การพิมพ์แบบไดนามิกทำให้การทำงานกับฐานข้อมูลง่ายขึ้นมาก

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

ประเภทการพิมพ์แบบไดนามิก - ข้อเสีย

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

สรุป

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

ข้อกำหนดเบื้องต้น

การพิมพ์ที่เข้มงวดหมายถึงการปฏิบัติตามเงื่อนไขบังคับต่อไปนี้:

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

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

การพิมพ์ในภาษาโปรแกรม

ลิงค์

ดูสิ่งนี้ด้วย


มูลนิธิวิกิมีเดีย 2010.

ดูว่า "การพิมพ์ที่หนักแน่น" ในพจนานุกรมอื่น ๆ คืออะไร:

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

    การพิมพ์ข้อมูล ประเภทความปลอดภัย การอนุมานประเภท การพิมพ์แบบไดนามิก การพิมพ์แบบคงที่ การพิมพ์แบบเข้ม การพิมพ์แบบอ่อน การพิมพ์แบบเป็ด บทความหลัก: การพิมพ์แบบเข้ม การพิมพ์แบบไดนามิกเป็นเทคนิคที่ใช้กันอย่างแพร่หลาย... ... วิกิพีเดีย

    การพิมพ์ข้อมูล ประเภทความปลอดภัย การอนุมานประเภท การพิมพ์แบบไดนามิก การพิมพ์แบบคงที่ การพิมพ์แบบเข้ม การพิมพ์แบบอ่อน การพิมพ์แบบเป็ด บทความหลัก: การพิมพ์แบบเข้ม การพิมพ์แบบคงที่เป็นเทคนิคที่ใช้กันอย่างแพร่หลาย... ... Wikipedia

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

    การพิมพ์ข้อมูล ความปลอดภัยประเภท การอนุมานประเภท การพิมพ์แบบไดนามิก การพิมพ์แบบคงที่ การพิมพ์ที่แข็งแกร่ง การพิมพ์แบบอ่อน การพิมพ์แบบพึ่งพา การพิมพ์แบบเป็ด การอนุมานประเภทในการเขียนโปรแกรมเป็นคุณสมบัติคอมไพเลอร์... ... Wikipedia

    การพิมพ์ข้อมูล ประเภทความปลอดภัย การอนุมานประเภท การพิมพ์แบบไดนามิก การพิมพ์แบบคงที่ การพิมพ์ที่รุนแรง การพิมพ์แบบ Soft การพิมพ์แบบพึ่งพา การพิมพ์แบบ Duck การพิมพ์แบบพึ่งพาในวิทยาการคอมพิวเตอร์และตรรกะ ซึ่งเป็นประเภทที่ขึ้นอยู่กับค่า ผู้อยู่ในความอุปการะ... ... วิกิพีเดีย

    - (พบคำว่าประเภทข้อมูลด้วย) เป็นแนวคิดพื้นฐานของทฤษฎีการเขียนโปรแกรม ชนิดข้อมูลกำหนดชุดของค่า ชุดของการดำเนินการที่สามารถนำไปใช้กับค่าดังกล่าว และอาจเป็นวิธีการใช้การจัดเก็บค่าและ... ... Wikipedia

    ประเภทข้อมูล เนื้อหา 1 ประวัติ 2 คำจำกัดความ 3 ความจำเป็นในการใช้ประเภทข้อมูล ... Wikipedia

    คำนี้มีความหมายอื่น ดูที่ ม.ล. (ความหมาย) ML Semantics: หลายกระบวนทัศน์: การทำงาน, ความจำเป็น, แบบแยกส่วน ปรากฏใน: 1973 ผู้แต่ง: Robin Milner และคณะ มหาวิทยาลัยเอดินบะระ ... Wikipedia

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

เวอร์ชันเต็มประกอบด้วยคำอธิบายโดยละเอียดเกี่ยวกับการพิมพ์ทุกประเภท พร้อมตัวอย่างโค้ด ลิงก์ไปยังภาษาโปรแกรมยอดนิยม และรูปภาพประกอบ

ฉันแนะนำให้อ่านบทความฉบับสั้นก่อน จากนั้นจึงอ่านฉบับเต็มหากต้องการ

เวอร์ชั่นสั้น

ขึ้นอยู่กับการพิมพ์ ภาษาโปรแกรมมักจะแบ่งออกเป็นสองค่ายใหญ่ - พิมพ์และไม่ได้พิมพ์ (typeless) กลุ่มแรกประกอบด้วย เช่น C, Python, Scala, PHP และ Lua และกลุ่มที่สองประกอบด้วยภาษาแอสเซมบลี Forth และ Brainfuck

เนื่องจากโดยพื้นฐานแล้ว "การพิมพ์แบบไร้พิมพ์" นั้นเรียบง่ายเหมือนกับปลั๊ก จึงไม่ได้แบ่งออกเป็นประเภทอื่นอีกต่อไป แต่ภาษาที่พิมพ์จะแบ่งออกเป็นหลายประเภทที่ทับซ้อนกัน:

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

    ตัวอย่าง:
    คงที่: C, Java, C#;
    ไดนามิก: Python, JavaScript, Ruby

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

    ตัวอย่าง:
    แข็งแกร่ง: Java, Python, Haskell, Lisp;
    จุดอ่อน: C, JavaScript, Visual Basic, PHP

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

    ตัวอย่าง:
    ชัดเจน: C++, D, C#
    โดยนัย: PHP, Lua, JavaScript

ควรสังเกตด้วยว่าหมวดหมู่ทั้งหมดเหล่านี้ทับซ้อนกัน เช่น ภาษา C มีการพิมพ์ที่ชัดเจนแบบคงที่ที่ไม่รุนแรง และภาษา Python มีการพิมพ์โดยปริยายที่รุนแรงแบบไดนามิก

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

เวอร์ชันโดยละเอียด

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

การพิมพ์แบบไม่มีการพิมพ์

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

การพิมพ์แบบไร้พิมพ์มักมีอยู่ในภาษาระดับต่ำ (ภาษาแอสเซมบลี, Forth) และภาษาลึกลับ (Brainfuck, HQ9, Piet) อย่างไรก็ตาม นอกจากข้อเสียแล้ว ยังมีข้อดีบางประการอีกด้วย

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

ใช่ สิ่งนี้มีอยู่จริง ตัวอย่างเช่น ในภาษาแอสเซมบลี (สำหรับสถาปัตยกรรม x86/x86-64 ฉันไม่รู้จักภาษาอื่น) คุณไม่สามารถประกอบโปรแกรมได้หากคุณพยายามโหลดข้อมูลจาก rax register (64 บิต) ลงใน cx register (16 บิต) .

mov cx, อีเอ็กซ์ ; ข้อผิดพลาดเวลาในการประกอบ

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

การพิมพ์แบบคงที่และไดนามิก

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

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

ลองคิดดูสิ

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

การเขียนโปรแกรมทั่วไป

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

เราจะแก้ปัญหาอย่างไร? มาแก้ปัญหากันใน 3 ภาษา: ภาษาหนึ่งมีการพิมพ์แบบไดนามิกและอีกสองภาษามีการพิมพ์แบบคงที่

ฉันจะใช้หนึ่งในอัลกอริธึมการค้นหาที่ง่ายที่สุด - กำลังดุร้าย ฟังก์ชันจะได้รับองค์ประกอบที่ต้องการค้นหา อาร์เรย์ (หรือรายการ) เอง และส่งคืนดัชนีขององค์ประกอบ หรือหากไม่พบองค์ประกอบ - (-1)

โซลูชันแบบไดนามิก (Python):

Def find(required_element, list): for (index, element) in enumerate(list): if element == required_element: return index return (-1)

อย่างที่คุณเห็น ทุกอย่างเรียบง่ายและไม่มีปัญหากับความจริงที่ว่ารายการสามารถประกอบด้วยตัวเลข รายการ หรืออาร์เรย์อื่นๆ ดีมาก. ไปต่อดีกว่า - แก้ไขปัญหาเดียวกันใน C!

สารละลายแบบคงที่ (C):

int find_int ที่ไม่ได้ลงนาม (int required_element, int array, ขนาด int ที่ไม่ได้ลงนาม) ( สำหรับ (int ที่ไม่ได้ลงนาม i = 0; i< size; ++i) if (required_element == array[i]) return i; return (-1); } unsigned int find_float(float required_element, float array, unsigned int size) { for (unsigned int i = 0; i < size; ++i) if (required_element == array[i]) return i; return (-1); } unsigned int find_char(char required_element, char array, unsigned int size) { for (unsigned int i = 0; i < size; ++i) if (required_element == array[i]) return i; return (-1); }

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

ใช่และไม่. มีเทคนิคการเขียนโปรแกรมหลายประการ ซึ่งหนึ่งในนั้นเราจะพิจารณากัน เรียกว่าการเขียนโปรแกรมทั่วไปและภาษา C++ รองรับได้ค่อนข้างดี มาดูเวอร์ชั่นใหม่กันดีกว่า:

โซลูชันแบบคงที่ (การเขียนโปรแกรมทั่วไป, C++):

แม่แบบ int find ที่ไม่ได้ลงนาม (T required_element, std::vector อาร์เรย์) ( สำหรับ (ไม่ได้ลงนาม int i = 0; i< array.size(); ++i) if (required_element == array[i]) return i; return (-1); }

ดี! มันไม่ได้ดูซับซ้อนกว่าเวอร์ชัน Python มากนัก และไม่ต้องเขียนอะไรมาก นอกจากนี้เรายังมีการใช้งานสำหรับอาร์เรย์ทั้งหมด ไม่ใช่แค่ 3 ที่จำเป็นในการแก้ปัญหา!

เวอร์ชันนี้ดูเหมือนจะตรงกับที่เราต้องการ เราได้รับทั้งข้อดีของการพิมพ์แบบคงที่และข้อดีบางประการของการพิมพ์แบบไดนามิก

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

สถิตยศาสตร์ในพลศาสตร์

ควรกล่าวถึงด้วยว่าภาษาคงที่หลายภาษาอนุญาตให้พิมพ์แบบไดนามิกได้เช่น:

  • C# รองรับประเภทหลอกแบบไดนามิก
  • F# รองรับการใช้วากยสัมพันธ์ในรูปแบบของ ? โอเปอเรเตอร์ บนพื้นฐานของการเลียนแบบการพิมพ์แบบไดนามิกที่สามารถนำมาใช้ได้
  • Haskell - การพิมพ์แบบไดนามิกจัดทำโดยโมดูล Data.Dynamic
  • Delphi - ผ่านประเภท Variant พิเศษ

นอกจากนี้ ภาษาที่พิมพ์แบบไดนามิกบางภาษายังช่วยให้คุณใช้ประโยชน์จากการพิมพ์แบบคงที่:

  • Common Lisp - การประกาศประเภท
  • Perl - ตั้งแต่เวอร์ชัน 5.6 ค่อนข้างจำกัด

การพิมพ์ที่แข็งแกร่งและอ่อนแอ

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

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

การพิมพ์ที่อ่อนแอมักสับสนกับการพิมพ์แบบไดนามิกซึ่งผิดโดยสิ้นเชิง ภาษาที่พิมพ์แบบไดนามิกสามารถพิมพ์ได้น้อยหรือรุนแรงก็ได้

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

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

การพิมพ์แบบอ่อนไม่มีข้อดีเลยเหรอ? อาจมีลักษณะเช่นนี้ แต่แม้ว่าฉันจะเป็นผู้สนับสนุนการพิมพ์ที่แข็งแกร่งอย่างกระตือรือร้น แต่ฉันต้องยอมรับว่าการพิมพ์ที่ไม่รุนแรงก็มีข้อดีเช่นกัน

อยากรู้ว่าอันไหน?

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

โอเค เราเข้าใจแล้ว ปรากฎว่าการพิมพ์ที่อ่อนแอก็มีข้อดีเช่นกัน! มีวิธีการถ่ายโอนข้อดีของการพิมพ์ที่ไม่รุนแรงไปสู่การพิมพ์ที่ดีหรือไม่?

ปรากฎว่ามีถึงสองคนด้วยซ้ำ

การหล่อแบบโดยนัย ในสถานการณ์ที่ไม่คลุมเครือ และไม่มีการสูญเสียข้อมูล

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

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

การสูญเสียข้อมูลนั้นง่ายยิ่งขึ้น หากเราแปลงจำนวนจริง 3.5 เป็นจำนวนเต็ม เราจะสูญเสียข้อมูลบางส่วน (อันที่จริง การดำเนินการนี้ยังคลุมเครือ - จะปัดเศษอย่างไร ขึ้นหรือลง ละทิ้งส่วนที่เป็นเศษส่วน)

การแปลงในสถานการณ์ที่ไม่ชัดเจนและการแปลงที่มีการสูญเสียข้อมูลนั้นแย่มาก ไม่มีอะไรเลวร้ายไปกว่านี้ในการเขียนโปรแกรม

หากคุณไม่เชื่อฉัน ให้ศึกษาภาษา PL/I หรือแม้แต่ค้นหาข้อมูลจำเพาะของมัน มีกฎสำหรับการแปลงระหว่างข้อมูลทุกประเภท! นี่เป็นเพียงนรก!

เอาล่ะ จำไว้เกี่ยวกับการแปลงโดยนัยที่จำกัด มีภาษาดังกล่าวหรือไม่? ใช่ ตัวอย่างเช่น ในภาษาปาสคาล คุณสามารถแปลงจำนวนเต็มให้เป็นจำนวนจริงได้ แต่จะแปลงกลับกันไม่ได้ นอกจากนี้ยังมีกลไกที่คล้ายกันใน C#, Groovy และ Common Lisp

โอเค ฉันบอกว่ายังมีวิธีรับข้อดีสองสามประการของการพิมพ์ที่ไม่รุนแรงในภาษาที่รุนแรง ใช่ มันมีอยู่จริง และถูกเรียกว่า Constructor Polymorphism

ฉันจะอธิบายโดยใช้ตัวอย่างภาษา Haskell ที่ยอดเยี่ยม

ตัวสร้างโพลีมอร์ฟิกเกิดขึ้นจากการสังเกตว่าการแปลงโดยนัยอย่างปลอดภัยมักจำเป็นเมื่อใช้ตัวอักษรตัวเลข

ตัวอย่างเช่น ในนิพจน์ pi + 1 คุณไม่ต้องการเขียน pi + 1.0 หรือ pi + float(1) ฉันแค่อยากจะเขียน pi + 1!

และสิ่งนี้เสร็จสิ้นใน Haskell เนื่องจากตัวอักษร 1 ไม่มีประเภทที่เป็นรูปธรรม มันไม่ใช่ทั้งหมด ไม่ใช่ของจริง หรือซับซ้อน มันเป็นเพียงตัวเลข!

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

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

ดังนั้น เราสามารถพูดได้ว่าทางออกที่ดีที่สุดคือการรักษาสมดุลระหว่างการพิมพ์ที่แรงและการพิมพ์ที่อ่อนแอ แต่ยังไม่มีภาษาใดที่สมดุลได้อย่างสมบูรณ์แบบ ดังนั้นฉันจึงเน้นไปที่ภาษาที่พิมพ์ยาก (เช่น Haskell, Java, C#, Python) มากกว่าภาษาที่พิมพ์ไม่รุนแรง (เช่น C, JavaScript, Lua, PHP)

การพิมพ์ที่ชัดเจนและโดยนัย

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

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

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

มีข้อดีสำหรับแต่ละประเภทหรือไม่ และอีกครั้ง มีการผสมผสานกันและมีภาษาที่รองรับทั้งสองวิธีหรือไม่?

ประโยชน์ของการพิมพ์ที่ชัดเจน
  • การมีแต่ละฟังก์ชันมีลายเซ็น (เช่น int add(int, int)) ทำให้ง่ายต่อการพิจารณาว่าฟังก์ชันนี้ทำอะไร
  • โปรแกรมเมอร์จะจดบันทึกประเภทค่าที่สามารถเก็บไว้ในตัวแปรเฉพาะได้ทันทีโดยไม่จำเป็นต้องจดจำ
ประโยชน์ของการพิมพ์โดยปริยาย
  • สัญกรณ์ชวเลข - def add(x, y) สั้นกว่า int add(int x, int y) อย่างชัดเจน
  • ความต้านทานต่อการเปลี่ยนแปลง ตัวอย่างเช่น หากในฟังก์ชัน ตัวแปรชั่วคราวเป็นประเภทเดียวกันกับอาร์กิวเมนต์อินพุต ดังนั้นในภาษาที่พิมพ์อย่างชัดเจน เมื่อเปลี่ยนประเภทของอาร์กิวเมนต์อินพุต คุณจะต้องเปลี่ยนประเภทของตัวแปรชั่วคราวด้วย

โอเค เป็นที่ชัดเจนแล้วว่าทั้งสองวิธีมีทั้งข้อดีและข้อเสีย (ใครจะคาดหวังอะไรอย่างอื่นอีก) ดังนั้นเรามาดูวิธีรวมสองวิธีนี้เข้าด้วยกัน

การพิมพ์ที่ชัดเจนตามตัวเลือก

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

โดยไม่ต้องเพิ่มข้อกำหนดประเภทที่ชัดเจน (x, y) = x + y -- เพิ่มข้อกำหนดประเภทที่ชัดเจน:: (จำนวนเต็ม, จำนวนเต็ม) -> เพิ่มจำนวนเต็ม (x, y) = x + y

หมายเหตุ: ฉันตั้งใจใช้ฟังก์ชันที่ไม่ได้รับการแก้ไขและจงใจเขียนลายเซ็นส่วนตัวแทนการเพิ่มทั่วไป:: (Num a) -> a -> a -> a เพราะ ฉันต้องการแสดงแนวคิดโดยไม่ต้องอธิบายไวยากรณ์ของ Haskell

อืม อย่างที่เราเห็นมันสวยงามและสั้นมาก การเขียนฟังก์ชันใช้เวลาเพียง 18 ตัวอักษรในหนึ่งบรรทัดรวมช่องว่างแล้ว!

อย่างไรก็ตาม การอนุมานประเภทอัตโนมัตินั้นค่อนข้างจะซับซ้อน และแม้แต่ในภาษาที่เจ๋งอย่าง Haskell ก็บางครั้งก็ล้มเหลว (ตัวอย่างคือข้อจำกัด monomorphism)

มีภาษาที่มีการพิมพ์อย่างชัดเจนโดยค่าเริ่มต้นและการพิมพ์โดยนัยหากจำเป็นหรือไม่? คอน
แน่นอน.

การพิมพ์โดยนัยตามตัวเลือก

มาตรฐานภาษา C++ ใหม่ที่เรียกว่า C++11 (ก่อนหน้านี้เรียกว่า C++0x) ได้แนะนำคีย์เวิร์ด auto ซึ่งช่วยให้คอมไพเลอร์สามารถอนุมานประเภทตามบริบท:

มาเปรียบเทียบกัน: // ระบุประเภท unsigned int a = 5 ด้วยตนเอง; int b = a + 3 ที่ไม่ได้ลงนาม; // เอาต์พุตอัตโนมัติประเภท unsigned int a = 5; อัตโนมัติ b = a + 3;

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

// ระบุประเภท std::vector ด้วยตนเอง vec = สุ่มเวกเตอร์ (30); สำหรับ (std::vector::const_iterator it = vec.cbegin(); ...) ( ... ) // การอนุมานประเภทอัตโนมัติ auto vec = RandomVector (สามสิบ); สำหรับ (อัตโนมัติ = vec.cbegin(); ...) ( ... )

ว้าว! นี่คือตัวย่อ โอเค แต่เป็นไปได้ไหมที่จะทำบางอย่างเช่น Haskell โดยที่ประเภทการส่งคืนขึ้นอยู่กับประเภทของอาร์กิวเมนต์

และอีกครั้งคำตอบคือใช่ ต้องขอบคุณคีย์เวิร์ด decltype ร่วมกับ auto:

// ประเภทคู่มือ int หาร(int x, int y) ( ... ) // การอนุมานประเภทอัตโนมัติ แบ่งอัตโนมัติ(int x, int y) -> decltype(x / y) ( ... )

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

ภาษาโปรแกรมบางภาษาตามการจำแนกประเภทนี้

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

JavaScript - ไดนามิก / อ่อนแอ / โดยนัย Ruby - ไดนามิก / แข็งแกร่ง / โดยนัย Python - ไดนามิก / แข็งแกร่ง / โดยนัย Java - คงที่ / แข็งแกร่ง / ชัดเจน PHP - ไดนามิก / อ่อนแอ / โดยนัย C - คงที่ / อ่อนแอ / ชัดเจน C++ - คงที่ / กึ่งแข็งแกร่ง / Perl ที่ชัดเจน - ไดนามิก / อ่อนแอ / โดยนัย Objective-C - คงที่ / อ่อนแอ / ชัดเจน C# - คงที่ / แข็งแกร่ง / ชัดเจน Haskell - คงที่ / แข็งแกร่ง / โดยนัย เสียงกระเพื่อมทั่วไป - ไดนามิก / แข็งแกร่ง / โดยนัย

บางทีฉันอาจทำผิดพลาดที่ไหนสักแห่ง โดยเฉพาะกับ CL, PHP และ Obj-C หากคุณมีความคิดเห็นที่แตกต่างในบางภาษา โปรดเขียนความคิดเห็นไว้

บทสรุป

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