为什么 scheme 没有原始的 c 数据类型,如 int、float 等

Why doesn't scheme have primitive c data types like int, float etc

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

我真的试过在互联网上进行研究,很抱歉我不得不在这里问,因为我一无所获。

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

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))

虽然我们一视同仁,但对象的类型不同。前两个不使用任何额外的 space 指针,但最后一个是指向分配在堆上的实际对象的指针。

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

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

这是在 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) |
  -----------------------------------------------------------------------

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

Scheme 不使用 C 类型,因为 Scheme 实现根本不需要绑定到 C:几个编译器发出本机代码,而不通过 C。就像其他答案提到的那样,Scheme(和之前的 Lisp它)试图让程序员不必处理诸如目标机器的寄存器大小这样(通常)不重要的细节。

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

其他答案已经向你展示了一些Scheme的实现细节。我最近写了一篇关于 CHICKEN Scheme's internal data representation 的博客。 post 包含指向其他几个方案的数据表示的链接,最后您将在 Python、Ruby、Perl 和较旧的 Lisp 变体中找到对数据表示的进一步引用。

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

一样高效地实现

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

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