如果没有基本数据类型,值将存储在哪里?例如,我想象这个伪类:
class Integer
{
private Integer i = 12;
public Integer getInteger
{
return this.Integer;
}
}
这将是一个递归的过程。
如果没有基本元素,如何实现编程语言?
我感激任何帮助解决我的困惑。
class Integer
{
private Integer i = 12;
public Integer getInteger
{
return this.Integer;
}
}
这将是一个递归的过程。
如果没有基本元素,如何实现编程语言?
我感激任何帮助解决我的困惑。
在幕后,计算机始终会使用原始数据类型,因为它们只是内存中的一些比特。但有些语言会把原始数据类型隐藏起来,让你只能使用对象进行操作。Java 允许同时使用对象和原始数据类型。
LISP是一种非常简单的函数式语言。最初的LISP没有原始的int
类型,因此解决整数的方法之一是将successor of successor of successor of zero
表示为3。
实际上,这种方法有一些优点,包括整数是开放式的,没有溢出,因此操作确实满足交换律、结合律等性质。还有一些不错的优化方法。当然,succ(succ(succ(zero)))
也可以以更类似于元组的方式进行编码(最好不要在LISP中这样做)。
后来,正常的LISP中,'3'将成为一个原子,123也将是这样的原子,并带有数学运算符。
然后,还有一些符号操作语言(如SNOBOL),可以对数字字符串执行运算,例如['4', '0'] * ['3']
。
因此,名称就像字符'a'或整数42一样,都是对象(原子)。
如果让你看一下采用“一切皆为对象”设计原则的语言中类似的代码,可能会有所帮助,这种语言比Java更加严格。那就是Smalltalk。想象一下,如果Java只有int
而没有Integer
,但你以前需要使用Integer
的所有内容都可以用int
实现,那么这就是Smalltalk。
以下是在Squeak 5.0中定义SmallInteger
类的代码摘录:
Integer immediateSubclass: #SmallInteger
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Kernel-Numbers'!
!SmallInteger commentStamp: 'eem 11/20/2014 08:41' prior: 0!
My instances are at least 31-bit numbers, stored in twos complement
form. The allowable range in 32-bits is approximately +- 10^9
(+- 1billion). In 64-bits my instances are 61-bit numbers,
stored in twos complement form. The allowable range is
approximately +- 10^18 (+- 1 quintillion). The actual
values are computed at start-up. See SmallInteger class startUp:,
minVal, maxVal.!
!SmallInteger methodsFor: 'arithmetic' stamp: 'di 2/1/1999 21:31'!
+ aNumber
"Primitive. Add the receiver to the argument and answer with the result
if it is a SmallInteger. Fail if the argument or the result is not a
SmallInteger.
Essential, No Lookup. See Object documentation whatIsAPrimitive."
<primitive: 1>
^ super + aNumber! !
!SmallInteger class methodsFor: 'instance creation' stamp: 'tk 4/20/1999 14:17'!
basicNew
self error: 'SmallIntegers can only be created by performing arithmetic'! !
SmallInteger
被定义为像语言中的其他所有东西一样的对象类,算术运算是像语言中的所有其他代码一样的方法。但它有点奇怪。它没有实例变量,你只能通过执行算术运算来创建实例,并且大多数方法看起来像是循环定义。<primitive: 1>
是关于此的实现提示),并将 SmallInteger
存储为仅仅是整数本身。与硬件相比,受限的范围是因为几个位被保留用于标记内存字为整数,而不是指向对象的指针(“tagged pointers”)。最终,所有的东西都会回到存储器中的位和指令。汇编语言、编译、过程式、面向对象以及其他所有的东西之间的区别在于你与位之间有多少抽象层,并且你从这种抽象中获得了多少好处(或成本)。
我会重新表述问题,以我的理解来回答。如果您认为我理解有误,请随时评论。
如果没有固有类型作为起点,基于组合和继承的类型系统如何定义任何有用的类型?除非语言实现至少知道一个固有类型作为起点,否则定义的类型注定要么是递归的,要么是空的。这是不可避免的吗?
是的,在我所知道的所有C家族语言中,这几乎是不可避免的。
如果每种类型都由其他类型组成,那么至少需要有一个固有类型来构建 - 例如,表示一个 比特 的固有类型,以便通过组合构造出 字节 类型,然后是 字 类型,然后是各种 整数 类型等等。然后,您需要定义可以对这些类型执行的操作,通过操纵构成其内部表示的位。
即使你只需要一个内在类型来构建,但这很可能效率非常低 - 你不想浪费空间或CPU周期,而且你想利用目标架构提供的各种存储位置和指令,包括FP寄存器和其他东西。
因此,在性能和“纯度”之间达成良好的折衷是,在语言中提供一些内置类型,这些类型可能会被现代CPU识别(如int32,int64,float,double等),并在它们之上构建其余的类型系统。在Java中,他们决定将这些内在类型称为primitives,并将它们与类分开。