zoukankan      html  css  js  c++  java
  • [ Skill ] Cadence Skill 语言入门

    https://www.cnblogs.com/yeungchie/

    写个大笔记,低速更新中 ...

    Cadence Skill

    Cadence 提供二次开发的 SKILL 语言,它是一种基于通用人工智能语言— Lisp 的交互式高级编程语言 (1)。

    SKILL 语言支持一套类似 C 语言的语法,大大降低了初学者学习的难度,同时高水平的编程者可以选择使用类似 Lisp 语言的全部功能。所以 SKILL 语言既可以用作最简单的工具语言,也可以作为开发任何应用的、强大的编程语言。

    SKILL 可以与底层系统交互,也提供了访问 Cadence 各个工具的丰富接口。用户可以通过 Skill 语言来访问,并且可以开发自己的基于 Cadence 平台的工具。

    LISP 即 List Processing-表处理,是最早和最重要的符号处理编程语言之一,它于1958年由美国的 J. McCarthy 提出,LISP 在人工智能AI方面获得广泛应用。

    我的环境是 Virtuoso IC618 平台,可能存在某些特性不适用于旧版本。

    如何查看官方资料

    cdsFinder

    • 用于模糊查找 Skill 函数,查看简单介绍。
    which cdsFinder
    # $CDSHOME/tools/bin/cdsFinder
    

    cdnshelp

    • 用于查看更加详细的内容,软件的使用手册。
    which cdnshelp
    # $CDSHOME/tools/bin/cdnshelp
    

    代码风格

    语法风格

    由于 Skill 语言是基于 Lisp ,因此它支持 函数表示法前缀表示法 来书写代码。

    • 函数表示法

      func( arg1 arg2 )
      
    • 前缀表示法

      ( func arg1 arg2 )
      

    以上的两种表示法可以同时存在,因此在编写代码时候最好注意格式的统一。

    推荐统一使用 函数表示法,并需要注意函数名和括号之间不能有空格。

    错误写法 : func ( arg1 arg2 )

    func 后面多了一个空格,这会将 arg1 作为函数名

    命名风格

    其次函数和变量的命名风格,Skill 中一般是使用 驼峰命名法 ( Camel-Case ),函数名中的每一个逻辑断点(单词)首字母大写,开头第一个单词全部小写,一般代表 函数/变量 的类别、作者,像我写的函数和全局变量一般都习惯用 yc 开头。

    这也是现在更为流行的一种命名方式,相对于传统的 下划线命名法,不会使较长的命名显得更长,且更加能提高可读性,当代码量上来的时候,满屏的下划线看起来会非常蛋疼。

    下面是一个对比

    • 驼峰命名法

      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)
      

    最后再注意命名不能过于简化,例如 abc。命名的目标是至少让人能一眼看出来这个变量是做什么用的 ( 一眼不行看两眼 ),例如上面的 libNamecellNameviewName

    下面介绍语法部分我会使用的一些非常简化变量命名,因为只是用于演示,命名就会比较随意了。

    基础语法

    hello world

    举例三种 print 写 hello world,后面会具体讲几种 print 函数的区别。

    print( "hello world" )
    println( "hello world" )
    printf( "%s
    " "hello world" )
    

    注释

    • 单行注释
    ; 这是单行注释
    
    • 多行注释
    /*
      这是多行注释
    */
    

    真 / 假

    Skill 中 真用 t 来表示,假用 nil 来表示。

    其实参与判断中的值,除了 nil 和空链表以外都可以认为是真,例如:

    • 判断 t

      when( t
        println( "True" )
      )
      ; t 为真,打印 "True"
      
    • 判断 10

      when( 10
        println( "True" )
      )
      ; 10 为真,打印 "True"
      
    • 判断 "YEUNGCHIE"

      when( "YEUNGCHIE"
        println( "True" )
      )
      ; "YEUNGCHIE" 为真,打印 "True"
      
    • 判断 nil

      when( nil
        println( "True" )
      )
      ; nil 为假,不运行 println 语句,打印 nil
      

    when 语句中第一个参数为判断条件,条件为真才会运行。

    数据类型

    可以用函数 type 查看一个数据的类型标识。

    type( 'YEUNGCHIE )          ; symbol
    type( "YEUNGCHIE" )         ; string
    type( list( "YEUNGCHIE" ))  ; list
    

    println 打印一个数据的内容,同时也能够从打印结果观察到数据类型。

    println( 'YEUNGCHIE )          ; YEUNGCHIE
    println( "YEUNGCHIE" )         ; "YEUNGCHIE"
    println( list( "YEUNGCHIE" ))  ; ( YEUNGCHIE )
    

    数据类型标识

    官方的教程中一般会用来表明函数需要的输入数据是什么类型的( 标在前缀 )。也用于限制子函数输入数据的类型。

    前缀 内部命名 数据类型
    d dbobject id , Cadence 数据对象
    x integer 整数
    f flonum 浮点数
    n number 整数 或者 浮点数
    g general 通用的 , 任何数据类型
    l list 链表
    p port I / O 句柄
    t string 字符串
    s symbol " 符号 "
    S stringSymbol " 符号 " 或者 字符串
    u function 函数对象 , 函数名 或者 lambda对象
    ... ... ...

    字符串

    • 定义方式

    字符串用双引号括起来。

    "YEUNGCHIE"
    ; "YEUNGCHIE"
    
    • 相关函数

      • strcat 字符串连接

        a = "YEUNG"
        b = "CHIE"
        c = strcat( a b )
        println( c )
        ; "YEUNGCHIE"
        
      • strlen 字符串长度

        下面的 c 沿用上面的变量 c,为了教程不写得过于繁杂、重复。

        println(strlen( c ))
        ; 9
        

    数字

    数字分为 整数 和 浮点数。

    • 整数

      18
      

      也可以直接编写成 二进制 ( 0b 前缀 )、八进制 ( 0 前缀 )、十六进制 ( 0x 前缀 ),但默认会输出成 十进制

      0b10010    ; 18
      024        ; 20
      0xFE       ; 254
      
    • 浮点数

      3.14
      

      浮点数也可以使用 科学计数法 和 单位后缀 来表示

      1e-06    ; 0.000001
      1u       ; 0.000001
      

    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
    

    链表 list

    • 定义方式

      • list

        list( arg1 arg2 list( arg3 arg4 ) ... )

        listA = list( 1 2 )
        ; ( 1 2 )
        
      • '

        '( "value1" sym1 (1 2) ... )

        这种表达方式需要注意,它不适用于变量元素,例如上面 sym1 并不会带入变量值,而是 symbol 类型 'sym1

        listB = '( 3 4 )
        ; ( 3 4 )
        
      • :

        arg1 : arg2

        仅在只有两个元素时使用,通常用来表达一个坐标点

        point = 1 : 2
        ; ( 1 2 )
        
    • 相关函数

      • 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 提取除了第一个元素之后的 list

        cdr( listF )
        ; ( 4 3 2 1 0 )
        
      • cadr 及其更多组合

        cadr( listF ) 其实就是 car(cdr( listF )) 的简写

        cadr( listF )  ; 4
        caddr( listF ) ; 3
        
      • nth 根据索引提取

        nth( 0 listF )               ; 5
        nth( 1 listF )               ; 4
        nth( length(listF)-1 listF ) ; 0
        

    数组 / 向量

    数组 和 向量 不常用,了解一下长什么样就行。

    数组

    • 定义

      declare

      declare( ARRAY[10] )
      
    • 赋值

      ARRAY[2] = 4
      ARRAY[3] = 5
      
    • 引用

      println( ARRAY[2] * ARRAY[3] )
      ; 20
      println( ARRAY[0] )
      ; unbound
      println( ARRAY[11] )
      ; *Error* arrayref: array index out of bounds - ARRAY[11]
      

    向量

    • 定义

      makeVector

      VECTOR = makeVector( 10 )
      
    • 赋值

      VECTOR[2] = 4
      VECTOR[3] = 5
      
    • 引用

      println( VECTOR[2] * VECTOR[3] )
      ; 20
      println( VECTOR[0] )
      ; unbound
      println( VECTOR[11] )
      ; *Error* arrayref: array index out of bounds - VECTOR[11]
      

    对照表 / 哈希

    某些情况下可能需要用到哈希来优化代码逻辑和速度等等。

    • 定义

      makeTable

      HASH = makeTable( 'HASH )
      
    • 赋值

      HASH[1] = "ONE"
      HASH["2"] = 2
      HASH[cvId] = "cellName"
      
    • 引用

      • 查看一个哈希的所有 key / value

        HASH~>?
        ; ( "2" 1 db:0x21cfda1a )
        HASH~>??
        ; ( "2" 2 1 "ONE" db:0x21cfda1a "cellName" )
        
      • 遍历一个哈希

        foreach( key HASH
          printf( "key: %A  ,  value: %A
        " key HASH[key] )
        )
        ; key: db:0x21cfda1a  ,  value: "cellName"
        ; key: 1  ,  value: "ONE"
        ; key: "2"  ,  value: 2
        

    条件判断

    if

    • 真/假 情况运行的都是单一的语句

      if( 条件
        当条件成立时运行
        当条件不成立时运行
      )
      

      例如:当 a > b 成立时,打印 "Yes";不成立时,打印时 "No"。

      if( a > b
        println( "Yes" )
        println( "No"  )
      )
      
    • 真/假 情况需要运行多条语句

      if( 条件
      then
        当条件成立时运行 1
        当条件成立时运行 2
      else
        当条件不成立时运行 1
        当条件不成立时运行 2
      )
      

      例如:当 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中。

      例如:

      • 当 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( a > b
      println( "Yes" )
    )
    

    条件为假才运行。

    unless( a > b
      println( "No" )
    )
    

    case / cond

    前面说了 case/cond 可以用来优化多条件下的 if ,因此逻辑是一样的。

    • case

      当所有的条件都是对一个变量做是否相等的判断的时候,可以使用 case

      例如,现在有一个变量 arg:

      • 当 arg 等于 "a" 时,打印 "Is a";
      • 否则当 arg 等于 "b" 或 "c" 时,打印 "Is b or c";
      • 否则当 arg 等于 "d" 时,打印 "Is d";
      • 全都不成立时,打印 "Unmatch"。
      case( arg
          ( "a"
              println( "Is a" )
          )
          (( "b" "c" )
              println( "Is b or c" )
          )
          ( "d"
              println( "Is d" )
          )
          ( t
              println( "Unmatch" )
          )
      )
      

      上面的语句换成 if 需要这样写:

      if( arg == "a"
          println( "Is a" )
          if( arg == "b" || arg == "c"
              println( "Is b or c" )
              if( arg == "d"
                  println( "Is d" )
                  println( "Unmatch" )
              )
          )
      )
      

      很明显 case 的写法更加清晰,观感上也更加舒服。

    • cond

      case 的使用情景比较单一,但条件多且判断的对象或者逻辑不唯一的时候可以 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

    for( x 0 2
        println( x )
    )
    ; 0
    ; 1
    ; 2
    

    foreach

    foreach( x list( 0 1 2 )
        println( x )
    )
    ; 0
    ; 1
    ; 2
    

    while

    a = 0
    while( a < 3
        println( a )
        a++
    )
    ; 0
    ; 1
    ; 2
    

    运算

    赋值运算

    算数运算

    比较运算

    逻辑运算

  • 相关阅读:
    最大相邻差值 数学 桶排序
    hdu-1536 S-Nim SG函数
    博弈论初步(SG函数)
    hdu-5009 Paint Pearls DP+双向链表 with Map实现去重优化
    hdu-5015 233 Matrix 矩阵快速幂
    hdu-1430 魔板 康拓展开+映射优化
    hdu-1043 bfs+康拓展开hash
    康拓展开-排列的hash
    Daily Scrum 12.23
    Daily Scrum 12.22
  • 原文地址:https://www.cnblogs.com/yeungchie/p/15270224.html
Copyright © 2011-2022 走看看