• 第6章:控制流——《实践之路》笔记


    聊聊这章都讲了啥

    有很多有趣的问题

    1.表达式的递归定义:为什么很多概念都是以递归的形式给出的?

    递归有一个终结条件,定义最终会递归到一个不可分元素,最小表达式就是一个常量或变量

    通常我们是用结构(迭代)的角度考虑问题的,把表达式看做一个个运算对象拼起来的,因为程序就是这么敲出来的哈哈

    而迭代的定义通常会趋向于无限,我们可以指出最小的表达式,但无法指出一个表达式最大会是什么样

    所以,用递归定义简洁描述了一个表达式是如何逐步分解至不可分的

    2.一行代码中发生了什么?为什么分别定义表达式与语句?

    求值(表达式完成)

    副作用(赋值语句完成)

    区分表达式与赋值,就区分了求值与副作用

    3.表达式与语句的关系

    赋值需要一个新值

    求表达式,得到值,赋值保存值

    4.把运算符归结到函数

    运算符是内部实现的函数,运算对象自然就是实参

    这么看来运算符像是自举的结果,定义上更加简洁了

    内置的运算应该经过了优化,因为出现频繁,所以决定了算法的效率,应该并不只是自举

    5.函数定义与调用发生了什么?

    函数签名里说明了运算需要啥数据,巧妇难为无米之炊

    调用时,把数据传入函数,需要有效区分传入的实参:位置参数,关键字参数

    6.计算的顺序

    先计算哪个运算符?优先级

    都是同样的运算符?结合律

    运算对象的值先求哪个?应用求值,正则求值

    7.什么是变量的值模型,引用模型?

    名字——地址——值

    选两个打包起来

    值模型:名字与地址打包

    引用模型:地址与值打包

    9.要求表达式的上下文,要求语句的上下文?

    需要一个值

    需要执行的步骤

    10.为什么要关注类型?

    类型指定操作的

    不同类型互操作:可能引起精度损失,或发生错误

    11.goto的废弃?

    goto+地址很难表达整体意图

    12.goto的替代方案

    异常处理也是一种语句跳转

    13.函数能修改啥?不该修改啥?

    参数是否修改?替代修改参数的方案?

    函数作用域与其他作用域的相互可见性

    14.迭代与递归的作用

    程序规模不再局限于程序正文的线性长度

    15.迭代器在各种语言中怎么实现?

    编程约定,模仿缺失的特征,获得等价的功能

    引言

    顺序执行是命令式语言的核心

    函数式语言强调表达式的求值

    逻辑式语言藏起控制流

    6.1表达式求值

    表达式递归定义:

    1.简单对象(内置类型)、自定义类型实例(支持运算符)

    2.运算符与函数应用于运算对象或参数

    运算对象或参数也是表达式

    运算符:可看做简单形式的内部函数

    运算对象:运算符的参数

    就此把运算符与运算对象归结到了函数的讨论范畴

    函数调用

    函数名(参数列表)

    前缀,中缀,后缀

    三元中缀运算符

    if  then  else

    优先级结合性

    决定了求值顺序(还有之后提到的参数求值顺序

    c语言优先级丰富

    类型强制,函数调用,数组下标,记录域选取

    6.1.1优先级与结合性

    优先级

    括号  算数  比较  逻辑

    结合律

    左至右

    赋值  右至左

    6.1.2赋值

    命令式语言的计算:通过修改内存中的变量

    修改的基本操作:赋值

    赋值的参数:值,变量引用

    通过修改变量值,影响后续计算

    所以赋值是最基本的副作用

    命令式语言严格区分  表达式与语句

    表达式:产生值

    语句:产生副作用

    纯函数式:没有副作用

    表达式是引用透明

    引用透明:幂等,无副作用

    没有副作用,意味着耦合度低,不易出错

    变量值模型:变量是值的容器,只能改变值:内存中数据

    变量引用模型:变量是值的命名引用,改变引用没有改变值

    左表达式:计算出地址

    右表达式:计算出

    引用模型中需要对变量做解引用取得值

    通常实现了隐式解引用来取得值

    两种模型的区别

    被引用的值在原位改变

    引用相同值的不同对象

    引用不同对象的值相同

    效率

    访问时的间接运算

    内存效率:不变对象的多个  副本(本书中用副本表示具体的值)

    名字——地址——值

    Java内部类型:值模型

    用户类型:引用模型

    要求传递内部类型:自动装箱拆箱

    语言正交性

    各个特征正交,任意组合使用,组合中保持意义

    要求表达式与要求语句的上下文

    c允许表达式中出现赋值

    赋值语句返回值(赋值成功?)

    增加了正交性:表达式中运算对象的副作用——赋值,返回值——成功标志 

    c与c++提供了到布尔类型自动强制:类型安全弱化

    组合赋值:减少冗余地址计算

    !注意表达式地址计算的副作用

    增量减量运算符

    重载后用于下标与指针地址运算

    前缀:+=的语法糖——赋值的优先级高

    后缀:减少临时变量

    多路赋值元组解包

    消除了函数的非正交性

    6.1.3初始化

    赋值语句:修改变量

    初始化:指定初始值

    初始值的用处:

    子程序中:局部静态变量需要初始值,然后使用

    静态分配的变量:在声明上下文中指定初始值,由编译器放入全局内存,避免了赋值开销

    避免使用未初始化的值造成错误

    对内置类型:系统提供相应字面量

    更正交——复合类型,自定义类型:需要聚集值——结构化的值

    js:对象字面量

    初始化的时期

    初始化只对静态分配的变量节约时间

    堆栈中变量只能运行时初始化

    未初始化问题对应另一种情况:破坏了原有值而之后未分配有效值

    如:悬空引用

    语言可在声明时赋予默认值:内存填零

    动态检查未初始化:动态语义错误

    语义检查的优势:区分未初始化值有效值(默认初始化值为有效值)

    效率:需要维护更新每个变量的使用情况

    java:定义性赋值

    所有之前可达路径中都完成了变量的赋值(初始化)

    自定义类型初始化与赋值

    默认值初始化:可能在之后改变对象大小

    有效值初始化:避免释放默认值分配的空间

    6.1.4表达式中的顺序问题=运算符运算顺序+运算对象求值顺序

    优先级与结合性定义了运算符的顺序

    未定义运算对象的求值顺序(参数列表的求值顺序)

    求值顺序的重要性:

    副作用:引入歧义

    代码改进:公共子表达式

    java:

    更多运行时开销(编译器试图减少)

    更清晰的语义

    更高的可靠性

    方法调用使用动态约束,参数检查?

    数组下标越界

    类型崩溃?

    其他语义错误的运行时检查

    基于运算定律的表达式整理

    精度变化溢出运算符副作用等不安全因素引入

    6.1.5短路求值

    表达式

    不完全求值

    新的求值顺序

    代码改进与提高可读写(读到逻辑确定即可)

    短路求值改变了布尔表达式语义(表达式:求出所有运算对象,按优先级执行运算符)

    按位运算——非短路求值

    用于避免下标越界,避免除0(放在布尔表达式前部)

    注意布尔表达式产生的副作用

    延迟,被动求值?

    在用于确定控制流时,布尔表达式转化为跳转表

    6.2结构化与非结构化的流程

    6.2.1goto的结构化替代品

    结构化程序设计:自顶向下设计(逐步精化),代码模块化(可见性控制:导入导出), 结构化类型(集合,指针数组),描述性的变量常量名

    ##函数参数与返回值:一种可见性控制?其他作用域内变量(全局,外层函数的局部变量:python显示导入)。

    ##其他可见性控制?各种导入导出动作的联系与差别

    return:跳至子程序末尾

    break:跳出单次循环

    异常:特定外围上下文

    回卷:修复运行栈上的子程序调用信息

    包括:释放并跳出帧栈信息管理(恢复寄存器

    错误与异常

    多层返回假定了被调用方知道传递调用方信息,即返回合适的值

    组件未能完成所执行功能的规范——退出至能恢复执行的地方。退出条件:异常

    结构化的异常处理机制:在可能出现异常的地方放置处理器(一个代码块)

    处理器完成修复工作

    多层返回与异常的工作有很多相似性:

    控制转移内层嵌套上下文——>特定外层上下文

    运行栈回卷

    区别:异常处理内层上下文无法完成工作的情况

    简单异常处理:辅助变量

    6.2.2继续

    代码地址+引用环境

    进入代码地址——恢复环境

    一种抽象:可以继续的执行上下文

    6.3顺序执行

    语句顺序执行:也是一种优先级

    复合语句:加了分隔符的语句列表

    块:声明+复合语句

    无副作用函数:幂等透明:同参数重复调用 返回相同值

    Ada折中:函数修改全局变量与静态变量,不允许修改参数

    6.4选择

    6.4.1短路条件

    else歧义消除:最近匹配

    elif:保持平行结构

    计算布尔表达式不是为了保存值,而是生成转跳码

    未显式存入寄存器,而是隐式用于流程控制

    机器提供条件分支指令

    6.4.2case/ switch

    整数表达式与编译时常数对比

    生成转跳表

    转跳表搜索:顺序,散列,折半

    查找策略:取决于标号数目范围

    处理未出现的标号  default

    落入方式:连续落入,或直接中断

    6.5迭代

    迭代与递归使程序规模不局限于程序正文的线性长度

    迭代:用循环实现,执行为了副作用——修改变量值

    迭代次数的确定方式:枚举控制,逻辑控制:更明确的结构语义

    6.5.1枚举控制

    循环下标:初始化,范围,步进量

    各分量是否要求编译时确定

    是否可以在循环中修改

    预先得到迭代次数,存于寄存器

    下标的作用域

    ##为何要求编译时确定?效率?

    引入循环头部——进一步结构化集中循环控制代码解耦合

    是否限定循环体中修改循环变量

    在集合中迭代

    枚举类型:要求枚举(作为特定类型),允许枚举(支持枚举的类型)

    6.5.2组合循环:循环嵌套

    各层的作用域可见性

    语言结构是否引入作用域,以及作用域间的可见性(特别的,全局变量)

    6.5.3迭代器

    实现迭代器对象接口

    生成器:迭代器的更一般形式,将枚举回溯 组合

    Python生成器——真迭代器

    真迭代器:容器抽象提供 枚举自己元素的迭代器

    #真迭代器:枚举值的独立上下文?

    迭代器在前面结束的地方继续

    像是控制进程:有自己的程序计数器,降低枚举元素的代码使用元素代码的耦合

    Python中range迭代器是预定义的?

     命令式语言中

    实现迭代器设计for循环的特殊形式

    与一种在循环中枚举值的机制

    这两个概念可以分开

    C++,java提供了类似python的枚举控制循环

    没有yield语句,没有用于枚举值独立的上下文(类似线程)

    迭代器只是个对象,提供方法:用于初始化,生成下个下标值,检测结束

    不同的调用之间,保持迭代器状态(作为数据成员)

    真迭代器:显式的数据结构来维护中间状态

    迭代器对象:中间状态维护在程序计数器与局部变量(构成可重新唤醒的执行上下文)?具体?

    c++将迭代器看做指针,自增,间接引用,end:特殊迭代器

    运算符重载变量值模型,显式废料收集容器声明泛型,针对特定元素实例化

    scheme:循环体写成函数,循环下标作为参数函数作为参数传递为迭代器

    6.5.5逻辑控制循环

    何处检查结束条件

    先检测,后检测,中间检测

    6.6递归

    允许函数调用自身

    迭代与递归算法可以相互转换

    迭代修改值

    递归不修改值(值作为参数继续传递?)

    6.6.1迭代与递归

    尾递归:调用后没有其他计算函数不需要动态分配栈空间,重复使用当前计算空间

    非尾递归——>转化为尾递归,图解?

    构造尾递归树?

    6.6.2应用序求值与正则序求值

    应用序求值:调用前求值

    正则序求值:实际需要值的时候求值(函数执行,计算参数)

    参数在传递给调用函数时未完成求值

    传递未求值实参——>实际需要值——>求值,运行函数

    正则序求值:出现在短路求值按名调用参数

    应用序求值:清晰高效

    正则序求值:产生更快的代码,参数值不需要

    惰性求值:惰性数据结构,用于组合搜索问题

    函数声明,传参,可调用对象,调用操作

    6.7非确定性

    非确定性:随意选择顺序依然保持正确

    形式化意义上的公平

    编程约定模仿缺失的特征,获得等价的功能

  • 相关阅读:
    求24点
    关于参数和返回值的常量性
    点到平面的距离公式
    大端序与小端序
    Quake3中的绝对值函数
    整数超出范围时如何表示?
    关于数组的几道面试题
    在移位数组中查找数
    时间复杂度O(n),空间复杂度O(1)的排序
    C++之对象切割
  • 原文地址:https://www.cnblogs.com/qmcj/p/9100267.html
Copyright © 2020-2023  润新知