คำอธิบายที่ดีที่สุดสำหรับภาษาที่ไม่มีค่า null


225

บ่อยครั้งเมื่อโปรแกรมเมอร์กำลังบ่นเกี่ยวกับข้อผิดพลาด / ข้อยกเว้นของโมฆะบางคนถามว่าเราทำอะไรโดยไม่มีโมฆะ

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

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

11
หากคุณเพิ่มแท็กในคำถามสำหรับฟังก์ชันการเขียนโปรแกรมหรือ F # คุณจะต้องได้รับคำตอบที่ยอดเยี่ยม
Stephen Swensen

ฉันเพิ่มแท็กการเขียนโปรแกรมใช้งานได้เนื่องจากตัวเลือกประเภทมาจากโลก ml ฉันไม่ควรทำเครื่องหมายว่า F # (เจาะจงเกินไป) BTW คนที่มีอำนาจ taxonomy จำเป็นต้องเพิ่มแท็กประเภทอาจหรือประเภทตัวเลือก
Roman A. Taycher

4
ฉันไม่ต้องการแท็กเฉพาะเช่นนั้นมาก่อน แท็กส่วนใหญ่จะอนุญาตให้ผู้คนค้นหาคำถามที่เกี่ยวข้อง (ตัวอย่างเช่น "คำถามที่ฉันรู้มากและจะสามารถตอบได้" และ "ฟังก์ชั่นการใช้งานโปรแกรม" นั้นมีประโยชน์มาก แต่บางอย่างเช่น "null" หรือ " ตัวเลือกชนิด" มีมากน้อยที่มีประโยชน์ไม่กี่คนที่มีแนวโน้มที่จะตรวจสอบ. 'ตัวเลือกชนิด' แท็กมองหาคำถามที่พวกเขาสามารถตอบ;).
jalf

อย่าลืมว่าหนึ่งในเหตุผลหลักสำหรับโมฆะคือคอมพิวเตอร์มีวิวัฒนาการอย่างมากที่เชื่อมโยงกับทฤษฎีเซต Null เป็นหนึ่งในฉากสำคัญที่สุดในทฤษฎีเซตทั้งหมด อัลกอริธึมทั้งหมดจะพัง เช่น - ดำเนินการจัดเรียงผสาน เรื่องนี้เกี่ยวข้องกับการทำลายรายการในหลาย ๆ ครั้ง เกิดอะไรขึ้นถ้ารายการมีความยาว 7 รายการ? ก่อนอื่นคุณแบ่งมันเป็น 4 และ 3 จากนั้น 2, 2, 2 และ 1 จากนั้น 1, 1, 1, 1, 1, 1, 1, 1 และ .... null! Null มีจุดประสงค์เพียงจุดเดียวที่คุณไม่เห็นจริง มันมีอยู่มากกว่าสำหรับขอบเขตทางทฤษฎี
stevendesu

6
@steven_desu - ฉันไม่เห็นด้วย ในภาษา 'nullable' คุณสามารถมีการอ้างอิงไปยังรายการว่าง [] และการอ้างอิงรายการ null คำถามนี้เกี่ยวข้องกับความสับสนระหว่างคนทั้งสอง
stusmith

คำตอบ:


433

ผมคิดว่าสรุปรวบรัดว่าทำไม null เป็นที่พึงปรารถนาคือการที่รัฐไม่มีความหมายไม่ควรจะแทนได้

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

class Door
    private bool isShut
    private bool isLocked

และชัดเจนว่าจะแมปสามสถานะของฉันเป็นตัวแปรบูลีนสองตัวนี้ได้อย่างไร isShut==false && isLocked==trueแต่ใบนี้เป็นหนึ่งในสี่ของรัฐที่ไม่พึงประสงค์ที่ใช้ได้: เนื่องจากประเภทที่ฉันเลือกเป็นตัวแทนของฉันยอมรับสถานะนี้ฉันต้องใช้ความพยายามทางจิตเพื่อให้แน่ใจว่าชั้นไม่เคยเข้าสู่สถานะนี้ (บางทีโดยการเข้ารหัสค่าคงที่) ในทางตรงกันข้ามถ้าฉันใช้ภาษาที่มีประเภทข้อมูลพีชคณิตหรือการแจกแจงแบบตรวจสอบที่ให้ฉันกำหนด

type DoorState =
    | Open | ShutAndUnlocked | ShutAndLocked

จากนั้นฉันสามารถกำหนด

class Door
    private DoorState state

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

ปัญหาnullคือว่าทุกประเภทอ้างอิงได้รับสถานะพิเศษนี้ในพื้นที่ของมันที่ไม่พึงประสงค์โดยทั่วไป stringตัวแปรอาจจะมีลำดับของตัวอักษรใด ๆ หรือมันอาจจะเป็นพิเศษนี้บ้าnullค่าที่ไม่ map ในโดเมนปัญหาของฉัน TriangleวัตถุมีสามPoints ซึ่งตัวเองมีXและYค่า แต่โชคร้ายPoints หรือTriangleตัวเองอาจจะมีค่า Null นี้บ้าที่มีความหมายกับโดเมนกราฟผมทำงานใน. ฯลฯ

เมื่อคุณตั้งใจจะสร้างแบบจำลองค่าที่อาจไม่มีอยู่จริงคุณควรเลือกใช้อย่างชัดเจน หากวิธีที่ฉันตั้งใจจะทำตัวเป็นแบบคนก็คือทุกคนPersonมี a FirstNameและ a LastNameแต่มีเพียงบางคนที่มีMiddleNames แล้วฉันอยากจะพูดอะไรบางอย่างเช่น

class Person
    private string FirstName
    private Option<string> MiddleName
    private string LastName

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

ตัวอย่างเช่นเมื่อใช้ประเภทด้านบนเราสามารถเขียนฟังก์ชันโง่ ๆ นี้ได้:

let TotalNumCharsInPersonsName(p:Person) =
    let middleLen = match p.MiddleName with
                    | None -> 0
                    | Some(s) -> s.Length
    p.FirstName.Length + middleLen + p.LastName.Length

ไม่ต้องกังวล ในทางตรงกันข้ามในภาษาที่มีการอ้างอิง nullable สำหรับประเภทเช่นสตริงแล้วถือว่า

class Person
    private string FirstName
    private string MiddleName
    private string LastName

คุณจบลงด้วยการเขียนสิ่งต่าง ๆ เช่น

let TotalNumCharsInPersonsName(p:Person) =
    p.FirstName.Length + p.MiddleName.Length + p.LastName.Length

ซึ่งระเบิดขึ้นหากวัตถุบุคคลที่เข้ามาไม่มีค่าคงที่ของทุกสิ่งที่ไม่เป็นโมฆะหรือ

let TotalNumCharsInPersonsName(p:Person) =
    (if p.FirstName=null then 0 else p.FirstName.Length)
    + (if p.MiddleName=null then 0 else p.MiddleName.Length)
    + (if p.LastName=null then 0 else p.LastName.Length)

หรืออาจจะ

let TotalNumCharsInPersonsName(p:Person) =
    p.FirstName.Length
    + (if p.MiddleName=null then 0 else p.MiddleName.Length)
    + p.LastName.Length

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

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

(โปรดทราบว่ามีความซับซ้อนมากยิ่งขึ้นสำหรับตัวอย่างง่ายๆเหล่านี้แม้ว่า a FirstNameไม่สามารถทำได้nulla stringสามารถเป็นตัวแทน""(สตริงว่าง) ซึ่งอาจเป็นชื่อบุคคลที่เราตั้งใจจะทำเช่นนั้นแม้ว่าจะไม่ใช่ สตริงที่ไม่มีค่าอาจเป็นกรณีที่เราเป็น "แทนค่าที่ไม่มีความหมาย" อีกครั้งคุณสามารถเลือกที่จะต่อสู้กับสิ่งนี้ผ่านค่าคงที่และรหัสเงื่อนไขที่รันไทม์หรือโดยใช้ระบบพิมพ์ (เช่นมีNonEmptyStringประเภท) ส่วนหลังอาจไม่เหมาะสม (ประเภท "ดี" มักจะ "ปิด" เหนือชุดการดำเนินงานทั่วไปและเช่นNonEmptyStringไม่ได้ปิด.SubString(0,0)) แต่มันแสดงให้เห็นถึงจุดมากขึ้นในพื้นที่การออกแบบ ในตอนท้ายของวันในระบบประเภทใดก็ตามมีความซับซ้อนที่จะกำจัดได้ดีและความซับซ้อนอื่น ๆ ที่ยากยิ่งกว่าในการกำจัด กุญแจสำคัญสำหรับหัวข้อนี้คือในเกือบทุกระบบประเภทการเปลี่ยนจาก "การอ้างอิงแบบ nullable โดยค่าเริ่มต้น" เป็น "การอ้างอิงแบบไม่เป็นโมฆะโดยค่าเริ่มต้น" เป็นการเปลี่ยนแปลงที่เรียบง่ายซึ่งทำให้ระบบการพิมพ์มีความซับซ้อนมากขึ้น พิจารณาข้อผิดพลาดบางประเภทและสถานะที่ไม่มีความหมาย ดังนั้นมันจึงค่อนข้างบ้าที่หลายภาษาทำซ้ำข้อผิดพลาดนี้ซ้ำแล้วซ้ำอีก)


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

59
อะไรที่คุณไม่เคยล็อคประตูเปิด?
Joshua

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

67
@ Akaphenom: ขอบคุณ แต่โปรดทราบว่าไม่ใช่ทุกคนที่ดื่มเบียร์ (ฉันไม่ใช่ผู้ดื่ม) แต่ฉันขอขอบคุณที่คุณกำลังใช้แบบจำลองที่เรียบง่ายของโลกเพื่อสื่อสารความกตัญญูดังนั้นฉันจะไม่พูดเล่นเกี่ยวกับสมมติฐานที่สมบูรณ์แบบของแบบจำลองโลกของคุณ : P (ซับซ้อนมากในโลกแห่งความเป็นจริง! :))
Brian

4
น่าแปลกที่มีประตู 3 สถานะในโลกนี้! พวกเขาจะใช้ในบางโรงแรมเป็นประตูห้องน้ำ ปุ่มกดทำหน้าที่เป็นกุญแจจากภายในซึ่งล็อคประตูจากด้านนอก มันจะถูกปลดล็อคโดยอัตโนมัติทันทีที่สลักกลอนเคลื่อนที่
comonad

65

สิ่งที่ดีเกี่ยวกับประเภทตัวเลือกไม่ได้เป็นตัวเลือก มันคือการที่ทุกประเภทอื่น ๆ ไม่ได้

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

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

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

จะเป็นการดีในหลาย ๆ กรณีที่ null ไม่ได้ทำให้ความรู้สึกมันไม่ควรจะได้รับอนุญาต

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

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

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

ดังที่คนอื่น ๆ พูดถึงใน C # หรือ Java ตัวอย่างเช่น null อาจหมายถึงหนึ่งในสองสิ่ง:

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

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


และในความหมายที่สองเราต้องการให้คอมไพเลอร์เตือน (หยุด?) เราถ้าเราพยายามเข้าถึงตัวแปรดังกล่าวโดยไม่ตรวจสอบความว่างเปล่าก่อน นี่เป็นบทความที่ยอดเยี่ยมเกี่ยวกับคุณลักษณะ C # เป็นโมฆะ / ไม่เป็นโมฆะ (ในที่สุด!) blogs.msdn.microsoft.com/dotnet/2017/11/15/…
Ohad Schneider

44

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

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

มันคือทุกสิ่งที่ดี! แต่มันไม่ได้ จำกัด การใช้งานของประเภทที่สามารถ nullable / non-null อย่างชัดเจนเพื่อให้ได้ผลเดียวกัน เหตุใดตัวเลือกยังคงเป็นสิ่งที่ดี? ท้ายที่สุด Scala รองรับค่า nullable (จำเป็นต้องมีเพื่อให้สามารถทำงานกับไลบรารี Java) แต่รองรับOptionsเช่นกัน

Q.ดังนั้นประโยชน์ที่เกินความสามารถในการลบ null ออกจากภาษาทั้งหมดได้อย่างไร

A.องค์ประกอบ

หากคุณทำการแปลที่ไร้เดียงสาจากโค้ดที่ไม่รู้จัก

def fullNameLength(p:Person) = {
  val middleLen =
    if (null == p.middleName)
      p.middleName.length
    else
      0
  p.firstName.length + middleLen + p.lastName.length
}

เพื่อรหัสตัวเลือกการรับรู้

def fullNameLength(p:Person) = {
  val middleLen = p.middleName match {
    case Some(x) => x.length
    case _ => 0
  }
  p.firstName.length + middleLen + p.lastName.length
}

ไม่แตกต่างกันมาก! แต่มันก็เป็นวิธีที่แย่มากที่จะใช้ตัวเลือก ... วิธีนี้สะอาดกว่ามาก:

def fullNameLength(p:Person) = {
  val middleLen = p.middleName map {_.length} getOrElse 0
  p.firstName.length + middleLen + p.lastName.length
}

หรือแม้กระทั่ง:

def fullNameLength(p:Person) =       
  p.firstName.length +
  p.middleName.map{length}.getOrElse(0) +
  p.lastName.length

เมื่อคุณเริ่มจัดการกับ List of Options มันจะดีขึ้นกว่าเดิม ลองนึกภาพว่ารายการpeopleนั้นเป็นทางเลือก:

people flatMap(_ find (_.firstName == "joe")) map (fullNameLength)

มันทำงานอย่างไร

//convert an Option[List[Person]] to an Option[S]
//where the function f takes a List[Person] and returns an S
people map f

//find a person named "Joe" in a List[Person].
//returns Some[Person], or None if "Joe" isn't in the list
validPeopleList find (_.firstName == "joe")

//returns None if people is None
//Some(None) if people is valid but doesn't contain Joe
//Some[Some[Person]] if Joe is found
people map (_ find (_.firstName == "joe")) 

//flatten it to return None if people is None or Joe isn't found
//Some[Person] if Joe is found
people flatMap (_ find (_.firstName == "joe")) 

//return Some(length) if the list isn't None and Joe is found
//otherwise return None
people flatMap (_ find (_.firstName == "joe")) map (fullNameLength)

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


8
+1 นี่คือจุดที่ดีที่จะเน้น ภาคผนวกหนึ่ง: ใน Haskell-land flatMapจะถูกเรียก(>>=)นั่นคือผู้ดำเนินการ "ผูก" สำหรับพระ ถูกต้อง Haskellers ชอบทำflatMapสิ่งต่าง ๆ มากมายที่เราใส่ไว้ในโลโก้ภาษาของเรา
CA McCann

1
+1 หวังว่าการแสดงออกของOption<T>จะไม่เคยเป็นโมฆะ น่าเศร้าที่ Scala เป็น uhh ยังคงเชื่อมโยงกับ Java :-) (ในทางกลับกันถ้า Scala เล่นได้ไม่ดีกับ Java ใครจะใช้บ้าง Oo)

ง่ายพอที่จะทำ: 'รายการ (null) .headOption' โปรดทราบว่านี่หมายถึงสิ่งที่แตกต่างจากค่าตอบแทนเป็น 'None'
Kevin Wright

4
ฉันให้ความโปรดปรานแก่คุณเพราะฉันชอบสิ่งที่คุณพูดเกี่ยวกับการแต่งเพลงที่คนอื่นไม่พูดถึง
Roman A. Taycher

คำตอบที่ยอดเยี่ยมพร้อมตัวอย่างที่ยอดเยี่ยม!
thSoft

38

เนื่องจากคนดูเหมือนจะหายไปมัน: nullคลุมเครือ

nullอลิซวันของการเกิดเป็น มันหมายความว่าอะไร?

nullบ๊อบวันของการเสียชีวิตคือ นั่นหมายความว่าอย่างไร?

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


ปัญหาอื่น: nullเป็นกรณีขอบ

  • คือnull = nullอะไร
  • คือnan = nanอะไร
  • คือinf = infอะไร
  • คือ+0 = -0อะไร
  • คือ+0/0 = -0/0อะไร

คำตอบมักจะ "ใช่", "ไม่", "ใช่", "ใช่", "ไม่", "ใช่" ตามลำดับ "นักคณิตศาสตร์" บ้าเรียก NaN "nullity" และบอกว่ามันเปรียบเทียบเท่ากับตัวมันเอง SQL ถือว่า nulls ไม่เท่ากับอะไรเลย (ดังนั้นมันจะทำตัวเหมือน NaNs) สิ่งมหัศจรรย์หนึ่งที่เกิดขึ้นเมื่อคุณพยายามจัดเก็บ±∞, ± 0 และ NaN ลงในคอลัมน์ฐานข้อมูลเดียวกัน (มี 2 53 NaNs ครึ่งหนึ่งเป็น "ลบ")

เพื่อทำให้เรื่องแย่ลงฐานข้อมูลแตกต่างกันในการปฏิบัติต่อ NULL และส่วนใหญ่ไม่สอดคล้องกัน (ดูการจัดการ NULL ใน SQLiteสำหรับภาพรวม) มันค่อนข้างน่ากลัว


และตอนนี้สำหรับเรื่องราวบังคับ:

ฉันเพิ่งได้รับการออกแบบ (sqlite3) a NOT NULL, b, id_a, id_b NOT NULL, timestampตารางฐานข้อมูลที่มีห้าคอลัมน์ เนื่องจากเป็นสคีมาทั่วไปที่ออกแบบมาเพื่อแก้ปัญหาทั่วไปสำหรับแอพตามอำเภอใจโดยทั่วไปมีข้อ จำกัด ที่ไม่ซ้ำกันสองประการ:

UNIQUE(a, b, id_a)
UNIQUE(a, b, id_b)

id_aมีเพียงเพื่อความเข้ากันได้กับการออกแบบแอพที่มีอยู่ (ส่วนหนึ่งเป็นเพราะฉันยังไม่ได้แก้ปัญหาที่ดีกว่า) และไม่ได้ใช้ในแอพใหม่ เพราะเป็นโมฆะวิธีการทำงานใน SQL ผมสามารถแทรก(1, 2, NULL, 3, t)และ(1, 2, NULL, 4, t)และไม่ละเมิดข้อ จำกัด เอกลักษณ์แรก (เพราะ(1, 2, NULL) != (1, 2, NULL))

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


FWIW โดยไม่มีการเรียกใช้พฤติกรรมที่ไม่ได้กำหนดครั้งแรกการอ้างอิง C ++ ไม่สามารถ "ชี้ไปที่" เป็นโมฆะและเป็นไปไม่ได้ที่จะสร้างคลาสที่มีตัวแปรสมาชิกอ้างอิงที่ไม่ได้กำหนดค่าเริ่มต้น (หากมีข้อผิดพลาดเกิดขึ้น

Sidenote: บางครั้งคุณอาจต้องการคำแนะนำร่วมกัน แต่เพียงผู้เดียว (คือหนึ่งเดียวของพวกเขาอาจจะไม่ใช่ NULL) เช่นในสมมุติ type DialogState = NotShown | ShowingActionSheet UIActionSheet | ShowingAlertView UIAlertView | DismissediOS assert((bool)actionSheet + (bool)alertView == 1)แต่ผมถูกบังคับให้ทำสิ่งที่ชอบ


นักคณิตศาสตร์ที่เกิดขึ้นจริงไม่ได้ใช้แนวคิดของ "น่าน" แต่มั่นใจได้
Noldorin

@Noldorin: พวกเขาทำ แต่พวกเขาใช้คำว่า "รูปแบบไม่แน่นอน"
IJ Kennedy

@IJKennedy: นั่นเป็นวิทยาลัยที่แตกต่างกันซึ่งฉันรู้ค่อนข้างดีขอบคุณ 'NaN บางตัวอาจเป็นตัวแทนของรูปแบบที่ไม่แน่นอน แต่เนื่องจาก FPA ไม่ได้ให้เหตุผลเชิงสัญลักษณ์
Noldorin

มีอะไรผิดปกติกับassert(actionSheet ^ alertView)? หรือภาษาของคุณไม่สามารถใช้แฮคเกอร์ได้?
แมว

16

ความไม่พึงประสงค์ของการมีการอ้างอิง / พอยน์เตอร์จะเป็นโมฆะโดยค่าเริ่มต้น

ฉันไม่คิดว่านี่เป็นปัญหาหลักที่มีค่า Null ปัญหาหลักของ Null คือพวกเขาสามารถหมายถึงสองสิ่ง:

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

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

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

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

ใน F #:

//first we create the option list, and then filter out all None Option types and 
//map all Some Option types to their values.  See how type-inference shines.
let optionList = [Some(1); Some(2); None; Some(3); None]
optionList |> List.choose id //evaluates to [1;2;3]

//here is a simple pattern-matching example
//which prints "1;2;None;3;None;".
//notice how value is extracted from op during the match
optionList 
|> List.iter (function Some(value) -> printf "%i;" value | None -> printf "None;")

อย่างไรก็ตามในภาษาอย่าง Java ที่ไม่ได้รับการสนับสนุนโดยตรงสำหรับประเภทตัวเลือกเรามีบางอย่างเช่น:

//here we perform the same filter/map operation as in the F# example.
List<Option<Integer>> optionList = Arrays.asList(new Some<Integer>(1),new Some<Integer>(2),new None<Integer>(),new Some<Integer>(3),new None<Integer>());
List<Integer> filteredList = new ArrayList<Integer>();
for(Option<Integer> op : list)
    if(op instanceof Some)
        filteredList.add(((Some<Integer>)op).getValue());

ทางเลือกอื่น ๆ เช่นการกินข้อความไม่มี

"การกินข้อความที่ไม่มีข้อความ" ของ C วัตถุประสงค์ไม่ใช่วิธีการแก้ปัญหามากเท่าความพยายามที่จะแบ่งเบาอาการปวดหัวของการตรวจสอบโมฆะ โดยพื้นฐานแล้วแทนที่จะขว้างข้อยกเว้นรันไทม์เมื่อพยายามเรียกใช้เมธอดบนออบเจ็กต์ null นิพจน์จะประเมินค่าเป็นโมฆะแทนตัวเอง if (this == null) return null;การระงับการปฏิเสธศรัทธาก็เหมือนกับว่าวิธีการเช่นแต่ละเริ่มต้นด้วย แต่มีการสูญหายของข้อมูล: คุณไม่ทราบว่าวิธีการส่งคืนเป็นโมฆะเพราะมันเป็นค่าตอบแทนที่ถูกต้องหรือเพราะวัตถุเป็นโมฆะจริง มันเหมือนกับการกลืนยกเว้นและไม่มีความคืบหน้าใด ๆ ในการจัดการกับปัญหาที่มีค่าว่างก่อนหน้านี้


นี่เป็นสัตว์เลี้ยงโกรธ แต่ c # เป็นภาษา c-like แทบจะไม่
Roman A. Taycher

4
ฉันจะไปที่จาวาที่นี่เนื่องจาก C # อาจมีทางออกที่ดีกว่า ... แต่ฉันซาบซึ้งกับความโกรธของคุณสิ่งที่ผู้คนหมายถึงจริงๆคือ ฉันไปข้างหน้าและแทนที่คำสั่ง "เหมือน"
Stephen Swensen

ด้วย linq ถูกต้อง ฉันกำลังคิดถึง c # และไม่ได้สังเกตสิ่งนั้น
Roman A. Taycher

1
ใช่ด้วยไวยากรณ์ที่ได้รับแรงบันดาลใจจาก c ส่วนใหญ่ แต่ฉันคิดว่าฉันเคยได้ยินภาษาการเขียนโปรแกรมที่จำเป็นเช่น python / ruby ​​ซึ่งมีน้อยมากในทางของ c เช่นไวยากรณ์ที่เรียกว่า c-like โดยโปรแกรมเมอร์ที่ใช้งานได้
Roman A. Taycher

11

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

ในภาษาระดับสูงกว่านั้นการมีค่า Null นั้นเป็นสิ่งที่น่าอึดอัดใจเพราะมันสื่อถึงแนวคิดที่แตกต่างกันสองประการ:

  • บอกว่าสิ่งที่เป็นไม่ได้กำหนด
  • บอกว่าสิ่งที่เป็นตัวเลือก

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

กรณีที่สองคือ optionality และให้บริการที่ดีที่สุดอย่างชัดเจนเช่นกับประเภทตัวเลือก


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

ใน C เราสามารถมี:

struct PhoneNumber { ... };
struct MotorbikeLicence { ... };
struct CarLicence { ... };
struct TruckLicence { ... };

struct Driver {
  char name[32]; /* Null terminated */
  struct PhoneNumber * emergency_phone_number;
  struct MotorbikeLicence * motorbike_licence;
  struct CarLicence * car_licence;
  struct TruckLicence * truck_licence;
};

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

ใน OCaml รหัสเดียวกันจะมีลักษณะดังนี้:

type phone_number = { ... }
type motorbike_licence = { ... }
type car_licence = { ... }
type truck_licence = { ... }

type driver = {
  name: string;
  emergency_phone_number: phone_number option;
  motorbike_licence: motorbike_licence option;
  car_licence: car_licence option;
  truck_licence: truck_licence option;
}

ตอนนี้สมมติว่าเราต้องการพิมพ์ชื่อของไดรเวอร์ทั้งหมดพร้อมกับหมายเลขใบอนุญาตรถบรรทุก

ใน C:

#include <stdio.h>

void print_driver_with_truck_licence_number(struct Driver * driver) {
  /* Check may be redundant but better be safe than sorry */
  if (driver != NULL) {
    printf("driver %s has ", driver->name);
    if (driver->truck_licence != NULL) {
      printf("truck licence %04d-%04d-%08d\n",
        driver->truck_licence->area_code
        driver->truck_licence->year
        driver->truck_licence->num_in_year);
    } else {
      printf("no truck licence\n");
    }
  }
}

void print_drivers_with_truck_licence_numbers(struct Driver ** drivers, int nb) {
  if (drivers != NULL && nb >= 0) {
    int i;
    for (i = 0; i < nb; ++i) {
      struct Driver * driver = drivers[i];
      if (driver) {
        print_driver_with_truck_licence_number(driver);
      } else {
        /* Huh ? We got a null inside the array, meaning it probably got
           corrupt somehow, what do we do ? Ignore ? Assert ? */
      }
    }
  } else {
    /* Caller provided us with erroneous input, what do we do ?
       Ignore ? Assert ? */
  }
}

ใน OCaml นั่นจะเป็น:

open Printf

(* Here we are guaranteed to have a driver instance *)
let print_driver_with_truck_licence_number driver =
  printf "driver %s has " driver.name;
  match driver.truck_licence with
    | None ->
        printf "no truck licence\n"
    | Some licence ->
        (* Here we are guaranteed to have a licence *)
        printf "truck licence %04d-%04d-%08d\n"
          licence.area_code
          licence.year
          licence.num_in_year

(* Here we are guaranteed to have a valid list of drivers *)
let print_drivers_with_truck_licence_numbers drivers =
  List.iter print_driver_with_truck_licence_number drivers

อย่างที่คุณเห็นในตัวอย่างเล็กน้อยนี้ไม่มีอะไรซับซ้อนในเวอร์ชั่นที่ปลอดภัย:

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

ในขณะที่ C คุณสามารถลืมเช็คว่างและบูม ...

หมายเหตุ: ตัวอย่างโค้ดเหล่านี้ที่ไม่ได้รวบรวม แต่ฉันหวังว่าคุณจะได้ความคิด


ฉันไม่เคยลองเลยยกเว้นen.wikipedia.org/wiki/Cyclone_%28programming_language%29อ้างว่าอนุญาตพอยน์เตอร์ที่ไม่เป็นค่าว่างสำหรับค
Roman A. Taycher

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

ฉันเชื่อNULLเช่นเดียวกับใน "การอ้างอิงที่ไม่สามารถชี้ไปที่สิ่งใด" ได้ถูกคิดค้นขึ้นสำหรับภาษาอัลกอลบางภาษา (วิกิพีเดียเห็นด้วยเห็นen.wikipedia.org/wiki/Null_pointer#Null_pointer # ) แต่แน่นอนว่าเป็นไปได้ว่าโปรแกรมเมอร์แอสเซมบลีเริ่มต้นพอยน์เตอร์ของพวกเขาไปยังที่อยู่ที่ไม่ถูกต้อง (อ่าน: Null = 0)

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

2
เป็น @tc บอกว่า null ไม่เกี่ยวอะไรกับชุดประกอบ ในการชุมนุมประเภทโดยทั่วไปจะไม่เป็นโมฆะ ค่าที่โหลดเข้าสู่รีจิสเตอร์ทั่วไปอาจเป็นศูนย์หรืออาจเป็นจำนวนเต็มที่ไม่ใช่ศูนย์ แต่มันจะไม่เป็นโมฆะ แม้ว่าคุณจะโหลดที่อยู่หน่วยความจำในการลงทะเบียนบนสถาปัตยกรรมทั่วไปส่วนใหญ่จะไม่มีการแสดง "ตัวชี้โมฆะ" แยกต่างหาก นั่นเป็นแนวคิดที่นำเสนอในภาษาระดับสูงกว่าเช่น C.
jalf

5

Microsoft Research มีโครงการที่เรียกว่า intersting

spec #

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


4

มาจากพื้นหลัง. NET ฉันคิดเสมอว่า null มีประเด็นมันมีประโยชน์ จนกว่าฉันจะได้รู้โครงสร้างและวิธีการที่ง่ายในการทำงานกับพวกเขาหลีกเลี่ยงรหัสสำเร็จรูปจำนวนมาก โทนีฮอร์พูดที่ QCon ลอนดอนในปี 2009 ขอโทษสำหรับการประดิษฐ์อ้างอิงโมฆะ อ้างเขา:

ฉันเรียกมันว่าผิดพันล้านดอลลาร์ มันเป็นสิ่งประดิษฐ์ของการอ้างอิงโมฆะในปี 1965 ในเวลานั้นฉันกำลังออกแบบระบบการพิมพ์แบบครอบคลุมครั้งแรกสำหรับการอ้างอิงในภาษาเชิงวัตถุ (ALGOL W) เป้าหมายของฉันคือเพื่อให้แน่ใจว่าการใช้การอ้างอิงทั้งหมดควรจะปลอดภัยอย่างยิ่งโดยการตรวจสอบดำเนินการโดยอัตโนมัติโดยคอมไพเลอร์ แต่ฉันไม่สามารถต้านทานสิ่งล่อใจที่จะใส่ในการอ้างอิงโมฆะเพียงเพราะมันง่ายที่จะใช้ สิ่งนี้นำไปสู่ข้อผิดพลาดมากมายช่องโหว่และระบบล่มซึ่งอาจทำให้เกิดความเจ็บปวดและความเสียหายนับพันล้านดอลลาร์ในช่วงสี่สิบปีที่ผ่านมา ในช่วงไม่กี่ปีที่ผ่านมามีการใช้โปรแกรมวิเคราะห์จำนวนมากเช่น PREfix และ PREfast ใน Microsoft เพื่อตรวจสอบการอ้างอิงและให้คำเตือนหากมีความเสี่ยงที่อาจไม่เป็นโมฆะ ภาษาการเขียนโปรแกรมล่าสุดเช่น Spec # ได้แนะนำการประกาศสำหรับการอ้างอิงที่ไม่เป็นนั นี่คือทางออกซึ่งฉันปฏิเสธในปี 1965

ดูคำถามนี้ด้วยที่โปรแกรมเมอร์


3

Robert Nystrom เสนอบทความที่ดีที่นี่:

http://journal.stuffwithstuff.com/2010/08/23/void-null-maybe-and-nothing/

อธิบายกระบวนการคิดของเขาเมื่อเพิ่มการสนับสนุนสำหรับการขาดงานและความล้มเหลวในภาษาการเขียนโปรแกรมMagpieของเขา


1

ฉันมองเสมอที่ Null (หรือไม่มี) เป็นกรณีที่ไม่มีค่า

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

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

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

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


สวัสดี @ จอนมันยากมากที่จะติดตามคุณที่นี่ ในที่สุดฉันก็รู้ว่าด้วยค่า "พิเศษ / แปลก" คุณอาจหมายถึงบางสิ่งเช่น 'ไม่ได้กำหนด' ของ Javascript หรือ 'NaN' ของ IEEE แต่นอกเหนือจากนั้นคุณไม่ได้ตอบคำถามใด ๆ ที่ OP ถาม และคำสั่งที่ว่า "Null อาจเป็นความคิดที่มีประโยชน์มากที่สุดสำหรับการตรวจสอบว่ามีบางสิ่งที่ขาดหายไป" เกือบผิดแน่นอน ประเภทของตัวเลือกเป็นทางเลือกที่ปลอดภัยและปลอดภัยเป็นศูนย์
Stephen Swensen

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

0

บางครั้งภาษาเวกเตอร์สามารถหนีไปได้โดยไม่ต้องมีค่าว่าง

เวกเตอร์เปล่าทำหน้าที่เป็นโมฆะที่พิมพ์ในกรณีนี้


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

การใช้เวกเตอร์แปลงเป็นเวกเตอร์เปล่าจะทำให้เกิดเวกเตอร์ว่าง FYI, SQL ส่วนใหญ่เป็นภาษาเวกเตอร์
Joshua

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