第二章 数据类型
数据类型是一组相关的值信息集。各种数据类型互相联系,而且它们通常是具有层次关系。Scheme拥有丰富的数据类型:有一些是简单的类型,还有一些复合类型由其它的类型组合而成。
2.1 简单数据类型
Scheme中的简单包含 booleans (布尔类型) , numbers (数字类型), characters(字符类型) 和 symbols(标识符类型)。
2.1.1 Booleans
Scheme中的booleans类型用 #t、#f来分别表示true和false。Scheme拥有一个叫boolean?的过程,可以用来检测它的参数是否为boolean类型。
(boolean? #t) => #t
(boolean? "Hello, World!") => #f
而not过程则直接取其参数的相反值做为boolean类型结果。
(not #f) => #t
(not #t) => #f
(not "Hello, World!") => #f
最后一个表达式清晰的显示出了Scheme的一个便捷性:在一个需要boolean类型的上下文中,Scheme会将任何非 #f的值看成true。
2.1.2 Numbers
Scheme的numbers类型可以是integers(整型,例如42),rationals(有理数,例如22/7),reals(实数,例如3.14159),或complex(复数,2+3i)。一个整数是一个有理数,一个有理数是一个实数,一个实数是一个复数,一个复数是一个数字。Scheme中有可供各种数字进行类型判断的过程:
(number? 42) => #t
(number? #t) => #f
(complex? 2+3i) => #t
(real? 2+3i) => #f
(real? 3.1416) => #t
(real? 22/7) => #t
(real? 42) => #t
(rational? 2+3i) => #f
(rational? 3.1416) => #t
(rational? 22/7) => #t
(integer? 22/7) => #f
(integer? 42) => #t
Scheme的integers(整型)不需要一定是10进制格式。可以通过在数字前加前缀 #b 来规定实现2进制。这样 #b1100就是10进制数字12了。实现8进制和16进制格式的前缀分别是 #o 和 #x。(decimal前缀 #d是可选项)
我们可以使用通用相等判断过程 eqv? 来检测数字的相等性。(eqv ?有点类似引用的相等判断ReferenceEquals)
(eqv? 42 42) => #t
(eqv? 42 #f) => #f
(eqv? 42 42.0) => #f
不过,如果你知道参与比较的参数全是数字,选择专门用来进行数字相等判断的 = 会更合适些。(= 号运算时会根据需要对参数做类型转换,如 (= 42 “42”) 运算结果是 #t)
(= 42 42) => #t
(= 42 #f) -->ERROR!!!
(= 42 42.0) => #t
其它的数字比较还包括 <, <=, >, >=
(< 3 2) => #f
(>= 4.5 3) => #t
+, -, *, /, expt等数学运算过程具有我们期待的功能。
(+ 1 2 3) => 6
(- 5.3 2) => 3.3
(- 5 2 1) => 2
(* 1 2 3) => 6
(/ 6 3) => 2
(/ 22 7) => 22/7
(expt 2 3) => 8
(expt 4 1/2) => 2.0
对于一个参数的情况,- 和 / 过程会分别得到反数和倒数的结果。
max和min 过程会分别返回提供给它们的参数的最大值和最小值。它们可以支持任何的数字。
(max 1 3 4 2 3) => 4
(min 1 3 4 2 3) => 1
abs过程会返回提供给它参数的绝对值。
(abs 3) => 3
(abs -4) => 4
这些还只是冰山一角。Scheme提供一整套丰富数学和三角运算过程。比如 atan, exp, 和 sqrt等过程分别返回参数的余切、自然反对数和开方值。其它更具体的数学运算过程信息请参阅Revised^5 Report on the Algorithmic Language Scheme
2.1.3 Characters
Scheme中字符型数据通过在字符前加 #\前缀来表示。像 #\c就表示字符 c。那些非可视字符会有更多的描述名称,例如,#\newline, #\tab。空格字符可以写成 #\ ,或者可读性更好一些的#\space。
字符类型判断过程是char? :
(char? #\c) => #t
(char? 1) => #f
(char? #\;) => #t
需要注意的是数据的分号字符不会引发注释。
字符类型数据有自己的比较判断过程:char=?, char<?, char<=?, char>?, char>=?
(char=? #\a #\a) => #t
(char<? #\a #\b) => #t
(char>=? #\a #\b) => #f
要实现忽略大小写的比较,得使用 char-ci 过程代替 char过程:
(char-ci=? #\a #\A) => #t
(char-ci<? #\a #\B) => #t
而类型转换过程分别是 char-downcase 和char-upcase:
(char-downcase #\A) => #\a
(char-upcase #\a) => #\A
2.1.4 Symbols
前面我们所见到的简单数据类型都是自运算的。也就是如果你在命令提示符后输入了任何这些类型的数据,运算后会返回和你输入内容是一样的结果。
#t => #t
42 => 42
#\c => #\c
Symbols并没有相同的表现方式。这是因为symbols通常在Scheme程序中被用来当做变量的标识,这样可以运算出变量所承载的值。然而symbols是一种简单数据类型,而且就像characers、numbers以及其它类型数据一样,是Scheme中可以传递的有效值类型。
创建一个单纯的symbol而非变量时,你需要使用quote过程:
(quote xyz)
=> xyz
因为在Scheme中经常要引用这种类型,我们有一种更简便的方式。表达式 'E和 (quote E) 在Scheme中是等价的。
Scheme中symbols由一个字符串来命令。在命名时不要和其它类型数据发生冲突,比如characters 、booleans、numbers 或复合类型。像 this-is-a-symbol,i18n, <=>,和 $!#*都是symbols,而 16,1+2i,#t,”this-is-a-string”和’(“hello” “world”) 都不是symbols类型数据(’(“hello” “world”) 是一个只包含两个字符串的List)。用来检查symbols类型数据的过程是symbol?
(symbol? 'xyz) => #t
(symbol? 42) => #f
Scheme的symbols类型通常都是不区分大小写的。因此Calorie 和calorie是等价的
(eqv? 'Calorie 'calorie)
=> #t
我们还可以使用 define 将symbol 类型的数据 如xyz当成一个全局的变量来使用:
(define xyz 9)
这样可以就创建了一个值为9的变量xyz.。如果现在直接在Scheme命令提示符后输入xyz,这样会将xyz中的值做为运算结果。
xyz
=> 9
如果想改变xyz中的值可以用set!来实现:
(set! xyz #\c)
现在xyz中的值就是字符 #\c了。
xyz
=> #\c