null的目的是什么?
我在编译器类,我们负责创build我们自己的语言,从头开始。 目前我们的困境是是否包含“空”types。 null提供了什么目的? 我们的一些团队认为这不是绝对必要的,而另外一些则是为了提供额外的灵活性而无效。
你有什么想法,尤其是针对null吗? 你有没有创build需要null的function?
空:十亿美元的错误 。 Tony Hoare:
我把它称为我十亿美元的错误。 这是1965年空引用的发明。当时,我正在devise面向对象语言(ALGOL W)的第一个全面的引用types系统。 我的目标是确保所有引用的使用都是绝对安全的,编译器会自动执行检查。 但是,我忍不住引入空引用的诱惑,仅仅因为它很容易实现。 这导致了无数的错误,漏洞和系统崩溃,在过去的四十年里可能造成了十亿美元的痛苦和损失。 近年来,许多像PREfix和PREfast这样的微软程序分析器都被用来检查引用,如果存在非空的风险,则会发出警告。 更近期的编程语言如Spec#引入了非空引用的声明。 这是我于1965年拒绝的解决办法。
null
是一个标记值,它不是一个整数,不是一个string,也不是一个布尔值 – 除了要保存的东西,并且是一个“不存在”的值。 不要把它当作或者期望它是一个0,或者一个空string或者一个空的列表。 这些都是有效的值,在很多情况下都可以是有效的价值 – 空值的概念意味着在那里没有价值。
也许这有点像一个抛出exception而不是返回值的函数。 除了制造和返回具有特殊含义的普通价值之外,它还返回一个特殊的价值,这个价值已经有了特殊的含义。 如果一个语言期望你使用null
,那么你不能真的忽略它。
哦,不,我觉得主要是出于我的哲学….
NULL的概念来自集合论中空集的概念。 几乎每个人都同意空集不等于零。 几十年来,math家和哲学家一直在讨论集合论的价值。
在编程语言中,我认为理解不引用任何内存中的对象引用是非常有帮助的。 谷歌关于集合论,你会看到集合理论家使用的forms符号系统(符号)和我们在许多计算机语言中使用的符号之间的相似之处。
问候,山姆
你问什么是空的?
好,
没有。
我通常在'内存地址0'的C / C ++方面考虑'null'。 这不是严格需要的,但如果不存在,那么人们只会使用别的东西(如果myNumber == -1,或者如果myString ==“”)。
我所知道的是,我想不到我花了一天的时间来编写“null”这个词,所以我觉得这很重要。
在.NET世界中,MS最近添加了int,long等可以为空的types,所以我想它们也是非常重要的。
如果我正在devise一个语言,我会保留它。 但是,我不会避免使用一种没有null的语言。 这也只是需要使用一点。
零概念不是完全必要的,完全一样,零概念不是绝对必要的。
我认为在整个语言devise的背景之外谈论空值是没有帮助的。 第一点混淆:空types是否为空,还是包含一个单独的区分值(通常称为“nil”)? 一个完全空的types并不是非常有用—尽pipeC使用了空的返回typesvoid
来标记一个只为副作用执行的过程,但是为了这个目的,很多其他的语言使用了单例types(通常是空的元组)。
我发现在dynamictypes语言中最有效地使用了一个零值。 在Smalltalk中,当你需要一个值,但是你没有任何信息的时候,它就是你使用的值。 在Lua中,它被更有效地使用:nil值是唯一不能成为Lua表中的键或值的值。 在Lua中,nil也被用作缺失参数或结果的值。
总的来说,我会说在一个dynamictypes的设置中,一个零值是有用的,但是在一个静态types的设置中,一个空types仅用于讨论为了副作用而执行的函数(或者过程或者方法)。
不惜一切代价,避免在C和Java中使用NULL
指针 。 这些是指针和对象实现中的固有构件,在devise良好的情况下,它们不应该被允许。 通过一切手段给你的用户一个方法来扩展一个现有的types的空值,但要明确地做到这一点 – 不要强迫每一个types有一个意外。 (作为显式使用的一个例子,我最近在Haskell中实现了Bentley和Sedgewick的三元search树,我需要扩展字符types的一个附加值,意思是“不是一个字符”。为此,Haskell提供了Maybe
types。
最后,如果你正在编写一个编译器,最好记住要编译的语言中最简单的部分,以及导致最lessbug的部分,这些部分是不存在的:-)
有一种方法可以指示一个引用或指针,当前不指向任何东西,无论你把它叫做null,nil,None,等等。如果没有其他的理由让人们知道他们什么时候要坠落closures链表的末尾。
在C NULL中是(void *(0)),所以它是一个types的值(?)。 但是,这不适用于C ++模板,所以C ++使NULL 0,它降低了types,并成为一个纯粹的价值。
然而,它被发现有一个特定的NULLtypes会更好,所以他们(C + +委员会)决定NULL将再次成为一种types(在C ++ 0x)。
除了C ++之外,几乎所有的语言都有NULL作为一个types,或者是一个等价的唯一值,它与0不一样(它可能等于或不可能,但不是相同的值)。
所以,现在即使C ++也会使用NULL作为types,基本上closures了这个问题的讨论,因为现在每个人(几乎)都会有一个NULLtypes
编辑:思考它Haskell的可能是另一种解决scheme的NULLtypes,但它不容易掌握或实施。
您可以将任何types与一组操作一起考虑为一个集合。 有很多情况下,有一个价值是不是一个“正常”的价值是方便的; 例如,考虑一个“EOF”值。 为C的getline()
。 你可以通过以下几种方式之一来处理:你可以在集合之外有一个NULL值,你可以区分一个特定的值为null(在C中, ((void *)0)
可以用于这个目的),或者你可以有一个方法创build一个新types,所以对于typesT ,你创build一个typesT' = def {T∪NULL} ,Haskell就是这么做的(一个“Maybe”types)。
哪一个更好呢?
空不是一个错误。 空意味着“我还不知道”
对于原语,你并不需要一个null(我不得不说,string(在.NET中)不应该得到它恕我直言)
但是对于复合实体来说,它肯定是有用的。
空仅在有未赋值的variables的情况下有用。 如果每个variables都有一个值,则不需要空值。
空值是一个定点值。 这是一个不可能是真实数据的值,而是提供有关正在使用的variables的元数据。
空分配给一个指针指示该指针是未初始化的。 这使您能够通过检测空值指针的解除引用来检测未初始化指针的滥用情况。 如果你保留一个指针的值等于内存中发生的任何事情,那么你会有疯狂的不规则的程序行为,这将是更难以debugging。
此外,C样式可变长度string中的空字符用于标记string的末尾。
以这些方式使用null,特别是对于指针值,已经变得非常stream行,隐喻已经被导入到其他系统中,甚至当“空”哨兵值被完全不同地执行并且与数字0没有关系时。
空不是问题 – 每个人都在处理,而不同的解释是问题。
我喜欢null。 如果没有null,那么null将被其他方式replace为代码说:“我不知道,老兄! (有些人会写“我不知道,男人!”,或者“我没有线索,老豆!”等,所以我们也会有同样的问题)。
我总结,我知道。
例如,考虑一下C和Java的例子。 在C中,惯例是空指针是数字值零。 当然,这实际上只是一个惯例,没有任何关于这个语言把这个价值当成任何特别的东西。 然而,在Java中, null
是一个明显的概念,你可以检测和知道,这是事实上是一个不好的参考,我不应该试图打开那扇门,看看对方是什么。
即便如此,我讨厌nulls几乎比其他任何事情都更糟糕。
基于评论的CLARIFICATION:我讨厌事实上的空指针值零比我讨厌null
更糟糕。
任何时候我看到任务都是空的,我想,“好的,有人刚刚在代码里放了一个地雷,有一天,我们要走在相关的执行path和BOOM !NullPointerException!
我更喜欢的是有人指定一个有用的默认或NullObject,让我知道“这个参数没有被设置为任何有用的。 一个秃头本身就是麻烦等待发生。
这就是说,它还是比零零散散的原始零。
null的一个实际例子是当你问是/否的问题,没有得到回应。 你不想默认为“否”,因为在答案非常重要的情况下,如果知道这个问题没有得到回答,这一点很重要。
这个决定取决于编程语言的目标。
你在为谁devise编程语言? 你在为那些熟悉c语言的人devise吗? 如果是这样,那么你应该添加对null的支持。
总的来说,我认为你应该避免违背人们的期望,除非它有特定的目的。
以C#中的开关块为例。 C#中的所有案例标签必须在每个分支中都有一个明确的控制streamexpression式。 也就是说,他们必须以“rest”声明或明确的转让结束。 这意味着虽然此代码是合法的:
switch(x) { case 1: case 2: foo; break; }
这个代码不合法:
switch (x) { case 1: foo(); case 2: bar(); break; }
为了创build一个从案例1到案例2的“通过”,需要插入一个goto,如下所示:
switch (x) { case 1: foo(); goto case 2; case 2: bar(); break; }
这可以说是违反了C#程序员的期望。 但是,增加限制是有目的的。 它消除了整个类的常见C ++错误的可能性。 它稍微增加了语言的学习曲线,但结果是程序员的净效益。
如果您的目标是devise针对C ++程序员的语言,那么删除null可能会违反他们的期望。 这会造成混乱,使你的语言更难以学习。 关键问题是,“他们有什么好处”? 或者,“这是什么原因”?
如果你只是想devise一个可以在一个学期内实施的“超级小语言”,那么故事是不一样的。 在这种情况下,你的目标不是build立一个针对特定人群的有用语言。 相反,这只是为了学习如何创build一个编译器。 在这种情况下,使用较小的语言是一个很大的好处,所以值得消除空值。
所以,回顾一下,我会说你应该:
- 在创build语言时确定你的目标。 谁devise的语言是什么,他们的需求是什么。
- 根据帮助目标用户以最佳方式实现目标的方式做出决定。
通常这会使预期的结果非常清楚。
当然,如果你没有明确expression自己的devise目标,或者你不能就自己的devise达成一致,那么你仍然会争论。 不过,在这种情况下,你几乎注定要失败。
另一种看null的方式是性能问题。 如果你有一个包含其他复杂对象的复杂对象等等,那么允许所有的属性初始化为空而不是创build某种空的对象将是更有效的,这些空对象不会是没有用的,而且很快就会被replace。
这只是一个我以前看不到的angular度。
null提供了什么目的?
我相信在这里工作有两个概念。
第一个(逻辑指示符无效)是一种传统的程序语言机制,它在程序逻辑中提供了非初始化存储器引用的运行时指示。
第二个值(null值)是一个基本数据值,可用于逻辑expression式中检测逻辑空指示符(以前的定义)并在程序代码中作出逻辑决定。
你有什么想法,尤其是针对null吗?
虽然null已经成为许多程序员的祸根,多年来许多应用程序错误的来源,但null概念是有效的。 如果您和您的团队创build的语言使用的内存引用可能会因为引用未初始化而被滥用,您可能需要一种机制来检测这种可能性。 创build替代scheme始终是一种select,但null是众所周知的替代scheme。
底线,这一切都取决于你的语言的目标:
- 目标受众群体
- 稳健性
- 性能
- 等等…
如果您的优先级列表中的健壮性和程序正确性较高,并且允许使用程序化内存引用,则您将需要考虑为空。
BB
如果你正在创build一个静态types的语言,我认为null会给你的编译器增加很多复杂性。
如果你正在创build一个dynamictypes的语言,NULL可以非常方便,因为它只是另一个“types”没有任何变化。
Null是一个占位符,意味着可以为该variables分配任何值(为静态types语言附加“正确types”)。
这里有认知失调。 我听到别的地方说人类不能理解否定,因为他们必须假设一个价值,然后想象它的不合适。
我对你们团队的build议是:拿出一些需要用你的语言编写的例子程序,看看如果你遗漏了null
,看看它们是什么样子的。
使用空对象模式!
如果你的语言是面向对象的,让它有一个UndefinedValue
类,其中只有一个单例实例存在。 然后在任何使用null
地方使用这个实例。 这有一个好处,你的null
将响应消息,如#toString
和#equals
。 您将永远不会遇到Java中的空指针exception。 (当然,这需要你的语言是dynamicinput的)。
Null为程序员提供了一个简单的方法,他们没有完全想到程序所需要的逻辑和领域,或者将来使用一个基本上没有明确定义的值的维护。
一开始看起来很明显,这意味着“没有价值”,但实际意义取决于上下文。 如果,例如LastName === null,是否意味着这个人没有姓,或者我们不知道他们的姓是什么,或者它还没有被input到系统中呢? 它是否等于自身,或者不是? 在SQL中,它不。 它在很多语言中都有。 但是,如果我们不知道personA.lastName或personB.lastName的价值,我们怎么能知道personA.lastName === personB.lastName,呃? 如果结果是错误的,或..。 空值?
这取决于你在做什么,这就是为什么有某种系统的价值可以用于任何forms的“什么都不是”的情况是危险和愚蠢的,因为你的程序的其他部分和外部库或模块不能真正依赖于正确地解释你“null”的含义。
明确定义lastName可能值的域,确切说明每个可能的值实际上意味着什么,而不是依赖于一些模糊的全系统概念null,这可能与您正在做的事情没有任何关系,这取决于您使用的是哪种语言,以及您正在尝试做什么。 实际上,当您开始处理数据时,其值可能会错误地performance出来。
空对象是什么0是数字。