Skill脚本语言学习笔记
常用工具
cdsFinder
- 模糊查找 Skill 函数,查看简单介绍
在Cadence目录下的bin目录中运行./cdsFinder &
cdnshelp
- 查看更加详细的内容,软件的使用手册
在Cadence目录下的bin目录中运行./cdnshelp &
Skill Version
- 查看 Skill 版本
#终端里运行
skill -V
#skill (procps version 3.2.8)
- 启动Virtuoso在CIW中执行
getSkillVersion()
"SKILL37.00"
基础语法
注释
- 单行注释
; 单行注释
- 多行注释
/*
多行注释
*/
代码风格
语法风格
Skill语言基于LISP,支持函数表示法与前缀表示法
- 函数表示法—类似C
func(arg1 arg2)
- 前缀表示法—类似TCL
(func arg1 arg2)
命名规范
函数和变量的命名风格,Skill 中一般是使用 驼峰命名法 ( Camel-Case ),命名以小写开头,之后的每一个单词首字母大写
- 驼峰命名法
geGetEditCellView()
dbOpenCellViewByType(libName cellName viewName nil mode)
- 蛇形命名法
ge_get_edit_cell_view()
db_open_cell_view_by_type(lib_name cell_name view_name nil mode)
常用运算
数学运算
| 操作符 | 符号 | 运算 | 函数名 |
|---|---|---|---|
| 加 | + | a + b | plus(a b) |
| 减 | - | a - b | difference(a b) |
| 乘 | * | a * b | times(a b) |
| 除 | / | a / b | quotient(a b) |
| 整数求余 | remainder(a b) | ||
| 浮点数求余 | modf(a b) | ||
| 乘方 | ** | a ** b | expt(a b) |
| 开方 | sqrt | sqrt(a b) |
赋值运算
| 操作/运算 | 符号 | 运算 | 函数名 |
|---|---|---|---|
| 赋值/赋值 | = | a = 1 | setq(a 1) |
| 自增 | += | a+= 1 | |
| 自增(+1) | ++ | ++a | add1(a) |
| a++ | postincrement(a) | ||
| 自减 | -= | a-= 1 | |
| 自减(-1) | -- | --a | sub1(a) |
| a-- | postdecrement(a) |
Tips 🍉:
- 假设 a = 1
++a会将 a 加一后的值返回,返回值是 2a++会先返回 a 的值后加一,返回值是 1add1( a )会返回 2,但不改变 a 的值postincrement( a )会返回 1,a 的值会加一变成 2- 自减同理
比较运算
| 操作/运算 | 符号 | 运算 | 函数名 |
|---|---|---|---|
| 相等 | == | a == b | equal( a b ) |
| 不相等 | != | a != b | nequal( a b ) |
| 小于 | < | a < b | lessp( a b ) |
| 小于等于 | <= | a <= b | leqp( a b ) |
| 大于 | > | a > b | greaterp( a b ) |
| 大于等于 | >= | a >= b | geqp( a b ) |
| 几乎相等 | nearlyEqual( a b ) |
逻辑运算
| 操作/运算 | 符号 | 运算 | 函数名 |
|---|---|---|---|
| 与 | && | a && b | and( a b ) |
| 或 | || | a | b | or( a b ) |
| 非 | ! | !a | not( a ) |
条件判断
if
-
真/假 情况运行的都是单一的语句
if( 条件 当条件成立时运行 当条件不成立时运行 )例如:当 a > b 成立时,打印 "Yes";不成立时,打印时 "No"
if( a > b println( "Yes" ) println( "No" ) ) -
真/假 情况需要运行多条语句
if( 条件 then 当条件成立时运行 1 当条件成立时运行 2 else 当条件不成立时运行 3 当条件不成立时运行 4 )例如:当 a > b 成立时,c 赋值 1 然后打印 "Yes";不成立时,c 赋值为 0 然后打印时 "No"
if( a > b then c = 1 println( "Yes" ) else c = 0 println( "No" ) ) -
多层嵌套
Skill 不能用
if-elsif-else这种简化的写法,但是逻辑是一样的,后级的if需要在上一级的else中-
单条运行语句
if( 条件 A 当条件 A 成立时运行 if( 条件 B 否则 当条件 B 成立时运行 以上条件都不成立时运行 ) ) -
多条运行语句
if( 条件 A then 当条件 A 成立时运行 1 当条件 A 成立时运行 2 else if( 条件 B then 否则 当条件 B 成立时运行 3 否则 当条件 B 成立时运行 4 else 以上条件都不成立时运行 5 以上条件都不成立时运行 6 ) )
-
例如:
- 当 a > b 成立时,打印 "Yes";
- 否则当 a > c 成立时,打印 "Yes";
- 都不成立时,打印时 "No"
if( a > b
println( "Yes" )
if( a > c
println( "Yes" )
println( "No" )
)
)
第二个 if 虽然不是 单一语句,但可以将整个 if( a > c ... ) 看做一个整体,所以也可以忽略 then/else。
这还有一个问题,当需要判断多个条件的时候 if 的写法就太不好看了,这时就可以使用 case 或者 cond
when/unless
when / unless 只当给定的条件为 真/假 的时候才运行给定的语句
-
when条件为真才运行when( a > b println( "Yes" ) ) -
unless条件为假才运行unless( a > b println( "No" ) )
case/``cond`
case/cond 可以用来优化多条件下的 if
-
case当所有的条件都是对一个变量做是否相等的判断的时候,可以使用
case例如,现在有一个变量 arg:
- 当 arg 等于 "a" 时,打印 "It is a"
- 否则当 arg 等于 "b" 或 "c" 时,打印 "It is b or c"
- 否则当 arg 等于 "d" 时,打印 "It is d"
- 全都不成立时,打印 "Unmatch"
case( arg ( "a" println( "It is a" ) ) (( "b" "c" ) println( "It is b or c" ) ) ( "d" println( "It is d" ) ) ( t println( "Unmatch" ) ) )用``if`来实现
if( arg == "a" println( "It is a" ) if( arg == "b" || arg == "c" println( "It is b or c" ) if( arg == "d" println( "It is d" ) println( "Unmatch" ) ) ) ) -
condcase的使用情景比较单一,当条件多且判断的对象或者逻辑不唯一的时候可以cond例如,现在需要对变量 a 做几个判断:
- 当 a > b 时,打印 "Bigger than b"
- 否则当 a > c 时,打印 "Bigger than c"
- 都不成立时,打印 "Smallest"
cond( ( a > b println( "Bigger than b" ) ) ( a > c println( "Bigger than c" ) ) ( t println( "Smallest" ) ) )
循环控制
for
指定一个起始整数(initial)和终止整数(final),依次遍历从 initial 到 final 组成的 list 的每一个元素,间隔为 1。
循环包括首尾数字
下面用 for 打印从 0 到 2:
for( x 0 2
println( x )
)
; 0
; 1
; 2
foreach
指定一个 list ,依此遍历每一个元素。
下面用 foreach 打印从 0 到 2:
foreach( x list( 0 1 2 )
println( x )
)
; 0
; 1
; 2
也可以同时遍历多个 list
foreach(( x y z ) list( 0 1 2 ) list( 3 4 5 ) list( 6 7 8 )
printf( "%d %d %d\n" x y z )
)
; 0 3 6
; 1 4 7
; 2 5 8
foreach 的返回值是第一个 list ( 0 1 2 )
while
指定一个条件,当条件为真时才会运行,当条件为假时跳出 while 循环。
下面用 while 打印从 0 到 2:
a = 0
while( a < 3
println( a )
a++
)
; 0
; 1
; 2
数据类型
可以用函数 type 查看一个数据的类型标识。
type( 'GALAC ) ; symbol
type( "GALAC" ) ; string
type( list( "GALAC" )) ; list
用 println 打印一个数据的内容,同时也能够从打印结果观察到数据类型。
println( 'GALAC ) ; GALAC
println( "GALAC" ) ; "GALAC"
println( list( "GALAC" )) ; ( GALAC )
逻辑型变量(boolean)
Skill 中用 t 来表示真,用 nil 来表示假。
其实参与判断中的值,除了 nil 和空列表(list)以外都可以认为是真,例如:
- 判断
t
when( t
println( "True" )
)
; t 为真,打印 "True"
- 判断数字
when( 10
println( "True" )
)
; 10 为真,打印 "True"
- 判断字符串
when( "GALAC"
println( "True" )
)
; "YEUNGCHIE" 为真,打印 "True"
- 判断
nil
when( nil
println( "True" )
)
; nil 为假,不运行 println 语句
字符串变量(string)
- 定义方式
字符串用双引号括起来
test = "Hello"
; "Hello"
-
打印输出
-
print
print( test ) ; "Hello" -
println(自动换行)
println( test ) ; "Hello"
-
数值型变量(number)
整数(int)
14
也可以直接使用二进制 ( 0b 前缀 )、八进制 ( 0 前缀 )、十六进制 ( 0x 前缀 ),但默认会输出成 十进制
0b10010 ; 18
024 ; 20
0xFE ; 254
浮点数(float)
3.1415
浮点数也可以使用 科学计数法 和 单位后缀 来表示
1e-06 ; 0.000001
1u ; 0.000001
列表(list)
列表本质不是一种数据类型,而是一种数据的存储结构
定义方式
-
listlist( arg1 arg2 list( arg3 arg4 ) ... )listA = list( 1 2 ) ; ( 1 2 ) -
''( "value1" sym1 (1 2) ... )这种表达方式需要注意,它不适用于变量元素,例如上面
sym1并不会带入变量值,而是 symbol 类型'sym1listB = '( 3 4 ) ; ( 3 4 ) -
:arg1 : arg2仅在只有两个元素时使用,通常用来表达一个坐标点
point = 1 : 2 ; ( 1 2 )
符号(symbol)
symbol 的写法是用一个单引号开头,例如:'a、'b、'c,这个类型比较抽象,刚接触知道有这么个东西就行,重点是需要用到的时候知道如何去使用
symbol 实际上是一种指针,每个 symbol 都存在以下几个组成部分(slot):
- Print name ( name )
- Value
- Function binding
- Property list
只有 name 是必要的,其他的部分都可以为空,当一个 文本引用 产生时,会创建一个 symbol ,且 Value 会默认为 'unbound
官方原文:
The system creates a symbol whenever it encounters a text reference to the symbol for the first time. When the system creates a new symbol, the value of the symbol is set to unbound.
当我们创建一个变量会自动生成与之对应的一个 symbol ,默认值为 'unbound,这个过程是非显式的
这样,如果需要将一个变量设置为 未定义/未绑定 的状态,则可以将它赋值为 'unbound
举个例子 ( 重点是这里 ):
-
给 arg 赋值为 12,然后打印出来
arg = 12 println( arg ) ; 终端会打印 12 -
给 arg 再赋值为
'unbound,然后尝试 print 一下arg = 'unbound println( arg ) ; 终端会提示 *Error* eval: unbound variable - arg
当一个变量没有被定义过的时候,引用它但是不赋值时运行会报 Error ,但如果只是想判断某个变量是否被定义了,可以使用函数 boundp 去检测目标变量的 symbol name
例如,检测一下变量 arg 是否被定义:
-
arg 已经被赋值为
'unbound现在是未定义的状态boundp( 'arg ) ; 结果是 nil -
再给它赋值一个值。
arg = 1 boundp( 'arg ) ; 结果是 t
判断一个函数是否存在的时候也需要利用到 symbol
例如,判断一下函数 dbCreateRect 是否存在:
dbCreateRect 是 Virtuoso 自带函数
fboundp( 'dbCreateRect )
; 结果是 lambda:dbCreateRect
判断一下,函数 QWE123 是否存在:
fboundp( 'QWE123 )
; 不存在,结果是 nil
对照表 / 字典(table)
Table 是 key / value 对的集合。类似于 Python 中的字典,但又更加宽泛,Python 字典的 key 只能是不可变数据类型,无法将列表、字典、对象等作为 key。
而在 Skill 中,Table 的 key 和 value 都可以是任意的数据类型:string、symbol、number、list、table、id 等都可以。
-
定义
makeTableHASH = makeTable( "Skill HASH" ) -
赋值
HASH[ 1 ] = "ONE" HASH[ "2" ] = 2 HASH[ cvId ] = "cellName" HASH[ 'myName ] = "Name" -
访问
-
通过 key 查看
HASH[ 1 ] ; "ONE" HASH[ "2" ] ; 2 -
如果 key 是 symbol
这时你还可以使用箭头符号
~>来访问,非常舒服。HASH~>myName ; "Name"也可以使用函数
get,不过这个一般不用。get( HASH "myName" ) ; "Name" -
查看一个哈希的所有 key / value
HASH~>? ; ( myName "2" 1 db:0x21cfda1a ) HASH~>?? ; ( myName "Name" "2" 2 1 "ONE" db:0x21cfda1a "cellName" ) -
遍历一个哈希所有元素
foreach( key HASH printf( "key: %A , value: %A\n" key HASH[ key ] ) ) ; key: db:0x21cfda1a , value: "cellName" ; key: 1 , value: "ONE" ; key: "2" , value: 2
-
数组 / 向量(array/vector)
数组 和 向量 不常用
数组 (array)
-
定义
declare
declare( ARRAY[10] ) -
赋值
ARRAY[2] = 4 ARRAY[3] = 5 -
访问
println( ARRAY[2] * ARRAY[3] ) ; 20 println( ARRAY[0] ) ; unbound println( ARRAY[10] ) ; *Error* arrayref: array index out of bounds - ARRAY[10]
向量 (vector)
-
定义
makeVector
VECTOR = makeVector( 10 ) -
赋值
VECTOR[2] = 4 VECTOR[3] = 5 -
访问
println( VECTOR[2] * VECTOR[3] ) ; 20 println( VECTOR[0] ) ; unbound println( VECTOR[10] ) ; *Error* arrayref: array index out of bounds - VECTOR[10]
子程序
定义函数
- 子程序代码块以
procedure函数来声明 - 一个子程序由三个部分组成:名称、参数、代码块
- 代码块中以最后一段语句的运算结果作为子程序的返回值
例如:
procedure( myAdd( a b )
a + b
); myAdd
这个子程序用来实现输入变量 a 和 b ,返回 a + b 的值
myAdd就是子程序的名称a和b是定义需要两个参数,这两个参数会作为子程序的局部变量a + b是代码块部分,仅有一行也作为最后一行,因此会返回两个变量相加之后的值
调用示例:
myAmyAdd( 1 2 )
; 返回 3dd( 1 2 )
; 返回 3
参数不是必须的,也可以提供一个空列表(括号里空着不写)作为参数的定义,意味着这个子程序不需要参数
procedure( myAddOneAndTwo()
3
); 这个子程序直接返回 3
myAddOneAndTwo()
; 3
局部变量
局部变量只在其定义的函数或程序块里面起作用,程序块外是无法访问局部变量,不会因为变量重名导致程序出错
全局变量则正好相反,所有的程序都可以使用这个变量,如果不同的程序重复定义了全局变量,程序执行的过程中就会产生错误,应该尽量少用全局变量
prog和let函数结构本身包含了定义局部变量的部分。之前提到的for,foreach中使用的自变量也是局部变量,作用域在for和foreach范围之内
let
let用来定义局部变量,定义变量的改动不会影响到代码块外的同名变量- 也是代码块中以最后一段语句的运算结果作为返回值
用法:
a = 5
let(( a )
a = 7
println( a )
)
println( a )
返回的结果:
7
5
如果想给 let 中的变量赋值一个默认值,出了在开头写一个赋值语句,上面的 let 还可以这样简化:
let(( a( 7 ) )
println( a )
)
prog
prog相对于let增加了return和go函数的支持。prog的默认返回值是nil
return
由于 prog默认的返回值是 nil,因此需要一个方法能够指定返回值是什么。而 return 就能够实现在一个 prog 中的任意位置跳出,并指定返回值
示例:
prog(( a )
a = 1
when( a < 5
return( t )
)
return() ; 当 return 不指定的返回值的时候等同于 return( nil )
)
上面的程序由于 a 为 1 ,小于 5,因此执行 when 中的语句,跳出 prog 并返回 t
运用 prog + return 可以更加灵活的控制 while 、 foreach 等循环结构
例如下面两段相似的代码中,prog 的位置不同对程序运行的影响:
-
满足条件提前跳出循环
a = 0 prog(( ) while( a <= 3 a++ when( a == 2 return()) print( a ) ); while ); prog当前 a == 2 时,跳出
prog,由于prog在while循环外部,因此整个循环会结束结果打印
1 -
满足条件直接运行下一个循环
a = 0 while( a <= 3 prog(( ) a++ when( a == 2 return()) print( a ) ); prog ); while当前 a == 2 时,跳出
prog,由于prog在while循环内部,因此当前循环结束,不执行print直接进入下一次循环。结果打印
134
go
go 用来实现在一个 prog 内部,跳转到指定的 标签
示例:
prog(( a )
a = 0
LABEL ; 打个标签
print( a++ )
when( a <= 3
go( LABEL ) ; 跳转到标签的位置
)
); prog
上面的例子实现一个循环,判断 ++a 的值小于等于 3 时回到 LABEL 标记的位置重复运行,结果打印 0123
go 的使用存在一些限制,不能在多个 prog 之间跳转,不能往循环内跳转:
- 可以从循环内往循环外跳转
- 不能从循环外往循环内跳转
- 不能从循环内往循环内跳转(除非循坏内部再加一个
prog)
输入类型限制
上面已经定义了一个 myAdd 函数,由于执行的过程是做加法,因此它有一个隐含的要求是:输入的两个变量都必须是数字,否则运行会报错
can't handle
可以在子程序的定义中加入这个变量类型的判断:
procedure( myAdd( a b )
unless( numberp( a ) && numberp( b )
error("myAdd: Argument should be number.")
)
a + b
); myAdd
运行上面的函数:
myAdd( "1" "2" )
; *Error* myAdd: Argument should be number.
Skill 已经提供了更简单的方法,不需要这么麻烦去直接写每个参数的判断
procedure( myAdd( a b "nn")
a + b
); myAdd
运行:
myAdd( "1" "2" )
; *Error* myAdd: argument #1 should be a number (type template = "nn") - "1"
可以看到仅仅是在参数定义之后追加了 "n" 就可以起到效果,第一个 n 声明第一个参数需要为数字(number 缩写成 n),第二个 n 同理声明第二个参数。
不过这个写法还能简化:... ( a b "n") ... ,像这样只写一个 n 就代表所有的参数都必须为数字类型
常见数据类型缩写
| 编码 | 内部命令 | 数据类型 |
|---|---|---|
| d | dbobject | id ,Cadence 数据对象 |
| x | integer | 整数 |
| f | flonum | 浮点数 |
| n | number | 整数 或者 浮点数 |
| g | general | 通用的,任何数据类型 |
| l | list | 列表 |
| p | port | I / O 口 |
| t | string | 字符串 |
| s | symbol | symbol (符号) |
| S | stringSymbol | symbol 或者 字符串 |
| u | function | 函数对象,函数 或者 lambda 函数 |
| ... | ... | ... |
可选输入参数
定义输入参数时候,可以使用一些 修饰 符号来做到类似 Getopt 的效果,常用的如下:
-
@rest
定义 不限数量 的输入
-
@optional
定义 可有可无 的输入,需要按顺序
-
@key
定义 可有可无 的输入,需要指定参数名
注意 🍉:语法上 @optional 和 @key 不能同时使用,功能上 @rest 和 @optional 同时使用会存在矛盾。
rest
用上面的子程序 myAdd 来举例
场景:现在这个程序,只能接受两个参数做加法,如果输入是 3 个或者更多怎么办? 我不知道有多少个参数需要一次性输入。这时候就需要用到 @rest
优化一下 myAdd:
procedure( myAdd( @rest args ) ; 修饰符号写在被修饰参数的前面
prog(( result )
result = 0
foreach( num args
printf( "myAdd: %n + %n\n" result num )
result += num
)
return( result )
)
); myAdd
运行:
myAdd( 1 2 3 )
; myAdd: 0 + 1
; myAdd: 1 + 2
; myAdd: 3 + 3
; => 6
例子中只定义了一个输入参数,被声明 @rest 后,args 会变成一个 list 参与子程序内部运行,接着遍历所有元素加起来就行了
optional
myAdd现在只能做加法,如果我想自定义运算类型,且要求不指定运算类型的时候默认做加法怎么办?这时候就需要用到 @optional
下面的例子为了避免矛盾,就不使用 @rest 了,将 args 用一个 list 来输入
procedure( myCalc( args @optional opt("+") ) ; 参数后面的括号内写上默认值,也可以写成 ( opt "+" )
prog(( result )
result = car( args )
args = cdr( args )
foreach( num args
printf( "myCalc: %n %s %n ; " result opt num )
case(opt
("+" result += num )
("-" result -= num )
("*" result *= num )
("/" result /= num )
); 按照不同的 opt 执行不同的操作
)
return( result )
)
); myCalc
运行:
myCalc( list( 1 2 3 ))
; myCalc: 1 + 2 ; myCalc: 3 + 3 ;
; => 6
myCalc( list( 1 2 3 ) "-")
; myCalc: 1 - 2 ; myCalc: -1 - 3 ;
; => -4
myCalc( list( 1 2 3 ) "*")
; myCalc: 1 * 2 ; myCalc: 2 * 3 ;
; => 6
myCalc( list( 1.0 2 3 ) "/")
; myCalc: 1.000000 / 2 ; myCalc: 0.500000 / 3 ;
; => 0.1666667
key
上面 @optional 的要求是输入的参数必须按顺序,即 先
args后opt,我不想固定这个顺序怎么办?这时候就需要用到 @key
使用格式:
-
定义
procedure( function( @key key1 key2 ) ... )不指定默认值的时候,默认值就是 nil
-
运行
function( ?key1 arg1 ?key2 arg2 ... )
在上面 myCalc 的基础上改一下:
procedure( myCalc( @key args opt("+") )
; prog ... 过程完全一致,这里就不写了
); myCalc
运行:
- 这里没有指定参数名称( keyword ),会报错
myCalc( list( 1 2 3 ) "+")
Error myCalc: extra arguments or keyword missing - ((1 2 3) "+")
-
正确用法
myCalc( ?args list( 1 2 3 ) ?opt "+") ; myCalc: 1 + 2 ; myCalc: 3 + 3 ; ; => 6 -
输入参数不需要按顺序
myCalc( ?opt "+" ?args list( 1 2 3 )) ; myCalc: 1 + 2 ; myCalc: 3 + 3 ; ; => 6
匿名函数(lambda)
匿名函数,顾名思义没有名字的函数,不同于 procedure 需要指定一个函数名,lambda 不需要指定一个函数名,它会返回一个 lambda 对象
示例:
sum = lambda(( a b )
a + b
)
funcall
上面已经定义了一个匿名函数,并将 lambda 对象赋值给了 sum 变量。 接下来就可以用 funcall 函数来使用它:
funcall( sum 1 2 )
; 3
funcall 的第一个参数接收一个函数对象 sum,后面的参数依次作为输入
apply
如果待输入的变量保存在一个 list 中,也可以用 apply 函数来使用它:
apply( sum list( 1 2 ))
; 3
可以看到,apply 的第一个参数接收一个函数对象 sum,第二个参数是一个 list ,list 中的元素依次对应 sum 需要接收的参数
这里需要注意的是,并不是
sum需要接收一个 list,而是apply接收一个 list,再把其中的元素依次作为sum的输入进行传参。sum接收到的依然是两个参数
此外 funcall、apply 不光可以接收 lambda,也可以接收一个 symbol 变量,前面讲到 symbol 存在一个 slot 是 Function binding ,因此也可以调用非匿名的子程序
apply( 'plus list( 1 2 ))
; 3
这里的
plus就作为plus函数的引用
mapcar
mapcar 的效果其实也是循环,之所以放到 《子程序》 章节来讲,是因为使用这个函数需要先了解子程序是什么
假设现在有一个 list:
numbers1 = list( 1 3 2 4 5 7 )
现在要将这个 list 中的每个元素都 +1 ,用 foreach 可以这样做:
numbers2 = nil
foreach( x numbers1
numbers2 = append1( numbers2 ++x )
)
println( numbers2 )
; ( 2 4 3 5 6 8 )
可以看到还是比较啰嗦的,换做 mapcar 就很方便了:
numbers3 = mapcar( 'add1 numbers1 )
; ( 2 4 3 5 6 8 )
也可以接受一个 lambda 匿名函数:
numbers4 = mapcar( lambda(( a ) a++ ) numbers1 )
; ( 2 4 3 5 6 8 )
另外前面讲到 foreach 的返回值是第一个 list,配合 mapcar 后可以将每次循环的结果作为返回值
numbers5 = foreach( mapcar x numbers1
++x
)
; ( 2 4 3 5 6 8 )
常用函数
string
strcat(字符串连接)
a = "Hello"
b = "World"
c = strcat( a b )
println( c )
; "HelloWorld"
strlen(字符串长度)
下面的 c 沿用上面的变量 c,为了演示不写得过于繁杂、重复。
println(strlen( c ))
; 9
list
append(连接两个list)
listC = append( listA listB )
println( listC )
; ( 1 2 3 4 )
append1(往末尾追加元素)
listD = append1( listC 5 )
println( listD )
; ( 1 2 3 4 5 )
cons(往开头追加元素)
listE = cons( 0 listD )
println( listE )
; ( 0 1 2 3 4 5 )
reverse(翻转list)
listF = reverse( listE )
println( listF )
; ( 5 4 3 2 1 0 )
length (获取list元素个数)
length( listF )
; 6
car(提取首元素值)
car( listF )
; 5
cdr(剔除首元素)
cdr( listF )
; ( 4 3 2 1 0 )
car/cdr(组合函数)
cadr( listF )其实就是car(cdr( listF ))的简写,一般用于嵌套list,类似还有cddar、caar、caddr...
cadr( listF ) ; 4
caddr( listF ) ; 3
nth(根据索引提取值)
nth( 0 listF ) ; 5
nth( 1 listF ) ; 4
nth( length(listF)-1 listF ) ; 0
removeListDuplicates(元素去重)
listG = list( 1 1 2 3 3 1 4 5 5 3 5 ) ; ( 1 1 2 3 3 1 4 5 5 3 5 )
removeListDuplicates( listG ) ; ( 1 2 3 4 5 )