为啥方案没有原始 c 数据类型,如 int、float 等

Posted

技术标签:

【中文标题】为啥方案没有原始 c 数据类型,如 int、float 等【英文标题】:Why doesn't scheme have primitive c data types like int, float etc为什么方案没有原始 c 数据类型,如 int、float 等 【发布时间】:2016-01-08 23:43:37 【问题描述】:

而且,它是如何从内存池中分配内存的?符号、数字有多少字节以及它如何处理类型转换,因为它没有用于转换的 int 和 float 类型

我真的尝试在互联网上进行研究,很抱歉我不得不在这里问,因为我什么也没找到。

【问题讨论】:

请不要只在标题中出现问题,否则会错过。也将其添加到文本中。 通常 SO 问题每个帖子有 1 个问题。另请注意,方案有多种实现,因此每个可以使用不同的字节数等。 这是因为 Lisp 的创建者具有数学背景,并没有以特定平台的限制为灵感。 Scheme 最初在 MacLisp 中解释并继承了类型和原语。 【参考方案1】:

与其他动态类型语言一样,Scheme 也有类型,但它们与 values 相关联,而不是与 variables 相关联。这意味着您可以在某个时间点将布尔值分配给变量,在另一个时间点将数字分配给数字。

Scheme 不使用 C 类型,因为 Scheme 实现根本不一定与 C 相关:一些编译器会发出本机代码,而无需经过 C。就像其他答案提到的那样,Scheme(和之前的 Lisp)试图将程序员从必须处理诸如目标机器的寄存器大小之类(通常)不重要的细节中解放出来。

数字类型在 Lisp 变体中特别复杂。 Scheme 有所谓的numeric tower,它抽象出表示的细节。与 Go、Python 和 Ruby 等许多“较新”语言非常相似,Scheme 将表示机器寄存器中的小整数(称为“fixnums”)或内存中的单词。这意味着它会像在 C 中一样快,但是一旦整数超过该大小,它会自动切换到不同的表示,因此可以表示任意大的数字,而无需任何特殊配置。

其他答案已经向您展示了一些Scheme的实现细节。我最近写了一篇关于CHICKEN Scheme's internal data representation 的博客。这篇文章包含指向其他几个方案的数据表示的链接,最后你会发现更多关于 Python、Ruby、Perl 和旧 Lisp 变体中的数据表示的参考。

Lisp 和 Scheme 的美妙之处在于它们都是如此古老的语言,但它们仍然包含“新思想”,这些思想现在才被添加到其他语言中。垃圾收集几乎必须发明才能让 Lisp 工作,它长期支持数字塔,在很早的时候就添加了面向对象,匿名程序从一开始就在那里我认为,当它的作者 proved 可以像 goto 一样高效地实现 lambda 时,Scheme 引入了闭包。

所有这些都是在 1950 年代和 1980 年代之间发明的。同时,甚至垃圾收集也花了很长时间才被主流接受(基本上是 Java,大约 45 年),并且对闭包/匿名过程的普遍支持仅在最近 5 年左右才流行起来。大多数语言都没有实现尾调用优化。 javascript 程序员现在才发现它。还有多少“现代”语言仍然需要程序员使用一组单独的运算符并作为一种特殊类型来处理任意大的整数?

请注意,很多这些想法(包括您询问的数字类型转换)都会引入额外的开销,但可以通过巧妙的实现技术来减少开销。最后,大多数都是净赢,因为它们可以提高程序员的生产力。而且,如果您在代码的选定部分需要 C 或汇编性能,大多数实现都允许您通过各种技巧降低到金属的水平,因此这不会对您关闭。缺点是它不是标准化的(尽管有 is cffi 用于 Common Lisp),但就像我说的,Scheme 不依赖于 C,所以如果规范是非常粗鲁的将 C 外部函数接口强制用于非 C 实现。

【讨论】:

您能否进一步解释一下 Scheme 如何自动切换到不同的表示形式? 对数字类型的原始操作是为了检测整数溢出如果传递了两个固定数字,当发生这种情况时它们将切换到一个大数字类型。这取决于具体的操作和实现。有时更容易简单地将 fixnums 转换为 bignums,执行操作,然后在合适的情况下尝试减少回 fixnum。【参考方案2】:

这个问题的答案取决于实现。

这是在 Scheme 编译器研讨会中的完成方式。 编译器为 32 位 Sparc 机器生成机器代码。

见http://www.cs.indiana.edu/eip/compile/back.html

Data Formats

All of our data are represented by 32-bit words, with the lower three bits as a kind of type-tag. While this would normally only allow us eight types, we cheat a little bit: Booleans, empty-lists and characters can be represented in (much) less than 32 bits, so we steal a few of their data bits for an ``extended'' type tag.

Numbers:                                                                 
--------------------------------------                                   
| 29-bit 2's complement integer  000 |                                   
--------------------------------------                                   

Booleans:                                                                
      -------------------       -------------------                        
  #t: | ... 1 00000 001 |   #f: | ... 0 00000 001 |                        
      -------------------       -------------------                        

Empty lists:
-----------------                                                         
| ... 00001 001 |                                                         
-----------------                                                         

Characters:                                                              
---------------------------------------                                   
| ... 8-bit character data  00010 001 |                                   
---------------------------------------                                   
Pairs, strings, symbols, vectors and closures maintain a 3-bit type tag, but devote the rest of their 32 bits to an address into the heap where the actual value is stored:

Pairs:                                                                   
---------------       -------------                                      
| address 010 |   --> | car | cdr |                                      
-----\---------  /    -------------                                      
      -----------                                                        

Strings:                                                                 
---------------       -------------------------------------------------  
| address 011 |   --> | length | string data (may span many words)... |  
-----\---------  /    -------------------------------------------------  
      -----------                                                        

Symbols:                                                                 
---------------       --------------------------                         
| address 100 |   --> | symbol name (a string) |                         
-----\---------  /    --------------------------                         
      -----------                                                        

Vectors:                                                                 
---------------                                                          
| address 101 |                                                          
-----|---------                                                          
     v                                                                   
  -----------------------------------------------------------            
  | length | (v-ref 0) | (v-ref 1) | ... | (v-ref length-1) |            
  -----------------------------------------------------------            

Closures:                                                                
---------------                                                          
| address 110 |                                                          
-----|---------                                                          
     v                                                                   
  -----------------------------------------------------------------------
  | length | code pointer | (free 0) | (free 1) | ... | (free length-1) |
  -----------------------------------------------------------------------

【讨论】:

【参考方案3】:

简短的回答是它具有原始数据类型,但作为程序员的您无需担心。

Lisp 的设计者具有数学背景,并没有将特定平台的限制作为灵感。在数学中,数字不是 32 位,但我们确实区分了精确数字和不精确数字。

Scheme 最初是在 MacLisp 中解释的,并继承了 MacLisp 的类型和原语。 MacLisp 基于 Lisp 1.5。

变量没有类型,大多数实现都有一个机器指针作为它的数据类型。字符、符号和小整数等基元通过将最后一个有效位作为类型标志来直接存储在地址中,因为机器将内存中的对象与寄存器宽度对齐,所以对于实际对象来说,该标志始终为零。

如果添加两个大于大小的整数,结果是不同的类型。在 C 中它会溢出。

;; This is Common Lisp, but the same happens in Scheme
(type-of 1)  ; ==> BIT
(type-of 10) ; ==> (INTEGER 0 281474976710655)
(type-of 10000000000000000) ; ==> (INTEGER (281474976710655))

对象的类型是不同的,即使我们对待它们是一样的。前两个不使用除指针之外的任何额外空间,但最后一个是指向在堆上分配的实际对象的指针。

所有这些都取决于实现。 Scheme 标准并没有规定它是如何完成的,但许多人就是这样做的。你can read the standard 它没有说明如何建模数字,只有行为。您可以创建一个 R6RS 方案,将所有内容存储在字节数组中。

【讨论】:

以上是关于为啥方案没有原始 c 数据类型,如 int、float 等的主要内容,如果未能解决你的问题,请参考以下文章

为啥没有定义 Java 的布尔原始大小?

JVM调优

C ++中的原始类型与C中的原始类型[重复]

在C语言中能否直接给指针指向的数据赋值?为啥?

使用原始类型进行模乘的方法

如何为记录类型创建布尔类型的字段?