导图社区 变量
这是一篇关于对象object变量 variable命名空间names的思维导图,主要内容包括:第一性原理 推导变量机制,python,变量 variable,lisp,生存期 extent,作用域 scope,引用 reference,绑定 binding,变量名称 variable name,命名空间 namespace,对象 object,鸿蒙初开, 有内存和cpu。
编辑于2024-02-06 16:34:48对象object 变量 variable 命名空间namespace 变量名称variable name 绑定binding 引用referencing 作用域scope 生存期extent
第一性原理 推导变量机制
鸿蒙初开, 有内存和cpu
内存是一个硬件 可以储存二进制状态 010101... 内存中可以划分一个区域, 存储一个数据对象 例如一个整数 123
cpu是计算用硬件 可以对内存中的数据进行运算 例如对 123 乘2, 得到 456. 运算结果可以返回给内存中, 划分一个区域存储.
对象 object
对象是内存中分配好的一段 表示一个值. 创建后在内存中存在, 直到手动销毁 或被机制销毁(gc)
变量 variable
一个对象创建后, 需要在程序其他部分被引用到, 其他部分包括空间上的距离 和 时间上的距离 变量是一个名字, 指向对象. a variable is a name refers to a value. 从名称到对象的映射关系. 变量在下文中指映射关系本身. 作为名字的符号/字符串称为变量名称.
变量名称 variable name
命名空间中一个唯一的, 不会重复的标识符, 作为变量的名称name. python用字符串, lisp用符号.
命名规则
1 蛇形 下划线连接
happy_ hacker
2 驼峰 单词首字母大写
HappyHack
3 脊柱 -号连接
happy-hacker
命名空间 namespace
存放变量名 到 对象地址 到映射关系的容器。 python用字典作为命名空间的实现 key是变量名称字符串 value是对象的地址
绑定 binding
添加一条从变量名称 -> 对象的映射关系 存入命名空间
引用 reference
用变量名称 从命名空间中取回绑定的对象
作用域 scope
Scope refers to the places in a program where a variable is visible and can be referenced. 作用域是程序中的一个文本范围, 其中变量是可见的, 对变量名的引用可以生效
最初的作用域 一个全局生效的作用域字典 变量名是全局变量名 重复的名称覆盖之前的绑定
一个作用域全局生效 生存期也等于全局(直到被手动删除) 早期的语言都这样 汇编 BASIC FORTRAN
问题 变量名冲突 无法模块化开发
程序所有部分不能有重名变量 否则会覆盖前一个的绑定 程序变大后难以协作开发 变量名越来越不够用
It is considered good programming practice to make the scope of variables as narrow as feasible so that different parts of a program do not accidentally interact with each other by modifying each other's variables.
模块化的需求
整个程序分成不同区域(块 函数). 区域内部的变量对外部不可见, 不同区域就不用考虑变量重名的问题了, 可以多人并行 模块化开发. 思路1 每个子区域给个作用域字典, 在区域内部生效. 可以嵌套. 重叠部分 最内层的生效. 一个变量名可以同时存在于多个作用域字典里. 思路2 变量名是全局唯一的, 每个变量名带一个FILO的绑定栈. 全局的绑定在栈底, 进入各个区域后, 结构内的赋值添加绑定到栈顶, 退出结构时, 弹出添加的绑定. 效果上, 也是可以嵌套, 当前最内层压入的绑定生效.
模块化后 变量可以分为 全局(global)变量 和 局部(local)变量 全局生效的绑定 和 在局部子结构内部生效的 对于局部结构内 变量可以分为 本地绑定的(bound) 和 自由的 (free) 前者是结构内部有赋值的 后者是结构内部没有 要到外侧找的
模块化的思路1 词法/静态作用域 在全局作用域(字典)外 每个子结构有自己的作用域(字典) 结构内部生效 编译时确定 区域是固定的 所以叫做词法作用域 固定的区域编译时就固定 运行时不变 所以叫静态作用域 嵌套时 最内层的生效
C Algol-like languages 绝大多数现代语言都是这种
优点是人/编译器, 检查代码就可以确定变量名指向的对象的定义是哪个 虽然值不一定是确定的
拥有自己作用域的结构
全局作用域 global scope
全局的命名空间 一个字典
块作用域 block scope
在代码块内部生效的绑定. 例如函数内部 条件语句内部 循环语句内部
for (i = 0; i < N; ++i) { /******************************/ /* printf("%d \n", i); */ /* i = i + 1; */ /******************************/ //scope of i } int n = 1; { int n = 2; /********************/ /* n = n + 1; */ /* n = n + 2; */ /* n = n + 3; */ /* printf("%d", n); */ /********************/ // scope of inner n } int factor(int n) { int i = 0; /**********************************/ /* int res = 1; */ /* for (i = 1; i < n; i++) */ /* { */ /* res = res * i; */ /* } */ /* return res; */ /**********************************/ // scope of i }
函数作用域 function scope
函数调用时绑定形参到传入的实参, 作用域范围是函数体
int add (int a, int b) { /**********************************/ /* int i = 0; */ /* for (i = 0; i < a; i++) */ /* { */ /* b = b + 1; */ /* } */ /* return b + a - a; */ /**********************************/ //scope of a and b }
模块作用域 module scope
名字的作用域是一个模块, python是典型的例子
模块化的另一个思路 全局/动态作用域 不是一个名字同时存在于多个作用域字典 而是全局一个名字, 每个名字带FILO的绑定栈 进入子结构时可以赋值压入绑定, 退出结构时弹出 效果也是最内层压入的生效 全局指名字全局生效 动态指不是编译时确定文本范围 而是运行时 在绑定栈里现查
早期的lisp只有这个 (emacs lisp) 后来scheme改用词法作用域 后来的common lisp 默认是词法的 可以声明动态的全局变量
变量名随时指向最近一次绑定(且未从栈中弹出)的值 范围是全局可用 编译时无法确定作用域的文本范围 运行时在栈顶找最近一次压入的 生效
词法与动态的对比
对一个结构中的 非自由变量(本地有赋值的) 动态作用域和词法作用域效果一样 都按最内层的绑定来 对于自由变量 词法作用域的会捕获嵌套外层字典的绑定(闭包) 动态作用域的不会 还是绑定栈现查
因为文本的嵌套关系时固定的 所以闭包捕获的绑定关系也是编译时确定的 词法作用域就是静态的文本作用域 而动态作用域的绑定栈可以在运行时压入/弹出绑定, 编译时无法确认哪个文本区域的绑定在生效, 引用时取当时栈顶的 所以称为动态的
生存期 extent
一个绑定 从开始到失效的 时间范围 绑定可引用到对象的时间范围 等于对象在内存里存在的时间范围
典型的 C语言 函数形参的生存期是从函数调用开始 到函数返回结束
动态生存期
进入结构时开始 退出结构时销毁 比如C语言的函数形参
未定义生存期
没有固定的开始结束位置 引用计数不为零则持续存在 引用计数归零被gc清理
lisp
绑定的作用域
词法 静态作用域 lexical/static scope
词法是指, 只在固定的文本范围内, 绑定生效, 例如函数形参的作用域是函数body. 静态是指, 生效范围在编译时就确定, 运行时不变, 是静态的. 如果没有绑定的对象, 编译时就会报错. 实现上是嵌套的字典 每个子结构一个字典
lisp中的变量默认词法作用域
全局 未定义 动态作用域 global/indefinite/dynamic scope
全局是指 没有固定的文本范围, 程序整体都可以查找当时的绑定 未定义是指, 不是在编译时确定的范围, 而是运行时, 在符号全局的绑定栈里查找, 顶上最新的一个生效 按绑定栈当时的内容来 不是编译器确定了的 如果绑定栈里没有 编译时不会报错 会在运行时报错 实现上每个intern的符号 带一个全局的绑定栈 可以用symbol-value从里边索引
声明为特殊的变量 defvar defparamerter的
对象的生存期
动态生存期 dynamic extent
一个对象 在结构开始时创建 退出是销毁
C语言的函数入参 函数调用时创建 函数返回时销毁
lisp中特殊变量的绑定 内层结构进入时创建 退出时销毁 不能被闭包捕获
未定义生存期 indefinite extent
一个对象,只要引用计数不为零,则持续存在。引用计数归零后被gc清理
C语言中的全局变量
lisp中绝大多数对象是这个
普通变量
词法作用域 + 未定义生存期 在静态的代码范围生效 生存期看引用计数 归零的被清理 可以被闭包捕获
引用普通变量时 固定是定义位置 词法范围里的绑定生效
特殊变量
未定义作用域 + 动态生存期 作用域没有固定的范围 理论上全局可ref 运行时从绑定栈里找最顶上的 绑定内层赋值时 往全局的绑定栈里压入 内层退出时从绑定栈中弹出销毁 内层的绑定 结构退出后必销毁 生存期是动态的 不可被闭包捕获
引用特殊变量时 是引用时间之前, 最近一次 最内层的绑定生效
catch throw的tag
未定义作用域 + 动态生存期 (和特殊变量一样)
block do prog 的退出名字
词法作用域 动态生存期
go tagbody的 tag
词法作用域 动态生存期
变量同名时 内层的生效 shadowing外层的
lambda 函数可以捕捉环境里用到的普通变量作为闭包 但是捕捉不了被声明为特殊变量的. 所以特殊变量需要被显式的在名字里区分 前后加上星号
*global-var1*
python
作用域只有词法作用域
默认按LEGB顺序从内向外查找
关键词可以指定查找位置
global 只在Global找
nonlocal 只在Enclosing找
生存期只有未定生存期
引用计数不归零就存在 归零被gc清理