程序设计语言原理复习总结 北航计算机专业课
这门课是上学期收获最大的一门,也是花费时间最多的。有一个大作业,是设计并开发一门新的语言。期末还有考试。
如果大作业被评为优秀,就不用参加期末的考试了。期末考试难度不低,上八十的很少。复习的话要根据重点来看。最重要的是平时的作业,比如用scala实现PageRank,Go实现的并发,java的继承多态。还要学会举一反三,比如pagerank给的数据结构变成了一个三元组怎么用scala处理,以及java继承的一些小细节。剩下的才是课件(其实我觉得课件大可不看...
下面是我根据课件和作业以及老师后来说的考试重点总结的知识点。希望对学弟学妹们有帮助。切记这门课老师并不是想考察知识点背诵或检索的,而是真正对语言知识的理解,会不会用它。所以下面的知识点理解即可,无需背诵!
考试重点:
过程,函数,并发,语义,ooc, go, scala, java过程式,函数式,并发,语义,oo
自动机,BNF, 谓词,描述式资料几乎可以不用打印
第一章
按语言的特点,可以将高级语言划分为:
过程式语言(如Cobol,Forturn,Algol,Pascal,Ada,C)
函数式语言(如Lisp)
数据流语言(如SISAL,VAL)
面向对象语言(如Smalltalk,CLU,C++)
逻辑语言(如Prolog)
字符串语言(如SNOBOL)
并发程序设计语言(如Concurrent Pascal,Modula 2,Go)等
重要语言20种
FORTRAN、COBOL、ALGOL、PL/1
LISP、ALGOL-68、BASIC、PASCAL
APL、Ada、Simula、C/C++
Smalltalk、ML、Prolog、SQL、Scala
Java、XML、Python、Go、Rust
熟悉Go、Python、Scala三种语言
第二章 程序设计语言设计概述
目录:
2.1 表示与抽象
2.2 PL设计目标显示表式和隐式表示语法规格说明词法元素词法和词法分析语法
2.4.1.1 文法有穷自动机正则语言和DFA非确定有穷NFA上下文无关文法(CFG)
2.4.1.3 BNF 和EBNF
2.4.1.4 语法图
2.4.1.5 语法分析
公理语义:Hoare逻辑指称语义
第三章 过程式程序设计语言
目录:
3.1 计算对象-值与类型3.1.1 字面量、变量、常量3.1.2 值是头等程序对象几种值类型讨论:字符串类型用户定义的序数类型数组类型3.1.3 类型系统3.1.4 类型兼容
3.2 计算对象的实现—存储3.2.1 程序变量的时空特性递引用 DEREFERENCE3.2.2 存储模型死堆对象处理3.2.3 悬挂引用
3.33.3.1 静态束定3.3.2 动态束定3.3.3 无类型语言束定3.3.4 声明DECLARATION
3.4 计算组织-程序控制3.4.1 一般概述3.4.2 异常处理
3.5 函数和过程3.5.1 函数和过程抽象3.5.2 参数机制
引用和指针
P指针是地址变量 *P是P所指的内容, 也有左值和右值。*P左值是P所指地址值,即P的值 *P右值是所指地址内的内容值
// & 和int一起用,只得是引用,而不是取地址符号了。
int &b=a;//声明b是整型变量a的别名
int a=2;
int &b=a;//这个声明语句中的&是一个引用
int *p=&b;//这个指针初始化语句中的&是取地址运算符
上面一行等价于 int *p=&a;
int i=5;
int *p=&i;
int * &pt=p;//建立指针变量p的引用pt
存储对象的生存期:
全局变量 和引用程序寿命一样长
局部变量 和程序中的一个模块寿命一样长
持久变量 比程序寿命长除非显式撤销 文件变量
瞬间变量(transient) 持久变量的逆
堆和栈的区别:
管理方式不同;(p26)
空间大小不同;
能否产生碎片不同;
生长方向不同;
分配方式不同;
分配和存取效率不同
左值和右值:将 L-value 的 L, 理解成 Location,表示定位,地址。将 R-value 的 R 理解成 Read,表示读取数据。
束定:把声明名字和存储对象或语义实体连接起来,如常量、变量的存储体、函数体、过程体、类型、异常。
静态束定:编译按数据类型为名字分配合适大小的存储对象, 该对象首地址填入符 号表即为静态束定, 编译后变量表销毁, 故静态束定不能变。
动态束定:运行时将名(字)束定于其体(得其语义操作),解释型语言一般保留名字表,运行中束定。
束定可以看作是完成名字指向存储对象指针的过程,但它与指针不同,首先束定是编译器(解释器)做的。为每一个名字分配其语法要求的存储,也可以跨越时间,编译时占个位置,运行时再分配。
参数机制
- 传值调用:实参表达式先求值;将值复制给对应的形参。形参和实参有同样大小的存储。过程运行后一般不再将改变了的形参值复制回实参。
- 传名调用:将未计算的参数表达式直接应用到函数内部,每次都会重新计算。
- 引用调用:引用参数实现时, 编译器在子程序堆栈中设许多中间指针,将形参名束定于此指针,而该指针的值是实参变量的地址(在主程序堆栈框架内),在子程序中每次对形参变量的存取都是自动地递引用到实参存储对象上。
静态作用域:
语句局部作用域中声明的变量 和祖先作用域中声明的所有可见变量的集合 为编译提供信息,生成代码和数据结构,运行时引用 其他作用域的变量
3.2.3 悬挂引用
当堆式管理同时提供显式除配命令KILL时;
堆栈式管理 外块指针指向内块对象时;
函数抽象作为第一类值时,都会产生悬挂引用(Dangling Reference)
解决办法:
Pascal把指针限制为堆对象且不用dispose,不提供地址运算。操
作数组不能按指针寻址, 快速索引。
C语言比较自由,悬挂引用留给程序员
闭包(closure)是可用到表达式上的操作。闭包应用:部分参数化
question:
函数回调(call back)是什么机制
使用回调写排序有什么好处
代码:有限循环的三种形式
//c language
# include <stdio.h>
int main(void){printf("sum = %lf\n", sum);return 0;
}
// while 与 do_while区别
//这个循环与while循环的不同在于:它先执行循环中的语句,然后再判断表达式是否为真。因此,do-while循环至少要执行一次循环语句
do{sum=sum+i;i++;
}
while(i<=100);
// do_until
//until命令和while命令工作的方式完全相反。until命令要求你指定一个通常返回非零退出状态码的测试命令。只有测试命令的退出状态码不为0,bash shell才会执行循环中列出的命令。 // switch
switch(k){ case 1: j = 2*k-1; break; case 2: j = 2*k-1; break; case 3: j = 3*k+1;break; case 4: j = 4*k-1;break; case 5: j = 3*k+1;break; case 6: j = k-2;break; case 7: j = k-2;break; case 8: j = k-2;break;
}
第四章 面向对象程序设计语言
目录
抽象与封装
small talk
赖自动垃圾收集(GC)
是否所有方法都采用动态束定
4.3 面向对象语言的基本特征与实现
向上向下强制
重载
课件中提到的问:
Java的对象如何算相同,如何clone
面向对象的发展与概念 为什么需要面向对象? OO 语言的发展 面向对象的基本概念
OO 技术和思想中的一个基本方面是数据和操作的封装 (封装定义:课件)
OO 的三个基本要素:封装、继承和动态方法束定都源于 Simula
软件实践也需要 OO 的思想:
// 长方形
public class Rectangle {protected long width;protected long height;public void setWidth(long width) {// differencethis.width = width;}public long getWidth() {return this.width;}public void setHeight(long height) {this.height = height;// difference}public long getHeight() {return this.height;}
}
// 正方形
public class Square extends Rectangle {public void setWidth(long width) {this.height = width;this.width = width;}public long getWidth() {return width;}public void setHeight(long height) {this.height = height;this.width = height;}public long getHeight() {return height;}
}
public class Test {public void resize(Rectangle r) {while (r.getHeight() <= r.getWidth()) {r.setHeight(r.getHeight() + 1);}}
}
// “向上强制” (c++代码)
class foo { // 基类指针可以引用派生类的对象。。“向上强制”class bar:public foo{ //但是子类指针不能引用基类对象foo F;bar B;foo* q;bar* s;...q = &B;s = &F; //error}
}
// 能 s = (bar*)q ,这种转换不安全,它要求 q 指向的确实是 bar
// 向下强制转换
bar *x = dynamic_cast<bar*>(q);
/*
如果 q 指向的确实是 bar 类的对象,转换将成功,x 指向该 bar 类对象
如果 q 指向的不是 bar 类的对象,转换失败,x 被赋空指针值 0
dynamic_cast,就要求在运行中能判断对象的类型和类型间关系
*/
多态问题 【reference: https://www.runoob.com/java/java-override-overload.html】
polymorphism: 对于给定的方法,获得不同但可归一化的对象行 为设计与实现机制
通过overload(重载)区分不同场景来重载方法,避免一个方法处理多种场景而导致 其逻辑复杂 (重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同)
通过override(重写/覆盖)让每个类根据其功能要求来重写所需要的方法,使用者无需区 别对待通过统一方式即可获得相应的功能
重写override:
// overload(重载)
class B{T0 tem(...){...sp(...)...}virtual T1 sp(...){...}
}
void fun(B &x){...x.tem(...)...}
class D:public B{T1 sp(...){...}
}
B b;
D d;
fun(b);
fun(d);
/*⚫ B 类里定义了一个一般性操作 tem,对所有 B类对象都可使用
⚫ tem 中调用了一个特殊操作 sp,该操作可能因子类不同而不同
⚫ 子类 D 覆盖操作 sp后,仍能正常地调用操作tem,而且其中对 sp 的调
用能调用到 D 类里新的操作定义*/
overload规则:
被重载的方法必须改变参数列表(参数个数或类型不一样);
被重载的方法可以改变返回类型;
被重载的方法可以改变访问修饰符;
被重载的方法可以声明新的或更广的检查异常
public class Overloading {public int test(){System.out.println("test1");return 1;}public void test(int a){System.out.println("test2");} //以下两个参数类型顺序不同 public static void main(String[] args){Overloading o = new Overloading();System.out.println(o.test());o.test(1);System.out.println(o.test(1,"test3"));System.out.println(o.test("test4",1));}
}
重写override
规则:
Animal b = new Dog();
b.bark() // error 因为b的引用类型Animal没有bark方法。
参数列表与被重写方法的参数列表必须完全相同。
返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
父类的成员方法只能被它的子类重写。
声明为 final 的方法不能被重写。
声明为 static 的方法不能被重写,但是能够被再次声明。
子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
构造方法不能被重写。
如果不能继承一个类,则不能重写该类的方法。
p94, 96代码
public class Foo {public void method1() {System.out.println("foo 1");}public void method2() {System.out.println("foo 2");}public String toString() {return "foo";}
}
public class Baz extends Foo {public void method1() {System.out.println("baz 1");}public String toString() {return "baz";}
}
public class Mumble extends Baz {public void method2() {System.out.println("mumble 2");}
}
public static void main(String[] args){Foo[] pity = {new Baz(), new Bar(), new Mumble(), new Foo()};for (int i = 0; i < pity.length;i++) {System.out.println(pity[i]);pity[i].method1();pity[i].method2();System.out.println();}
}
// question:
为什么method1和method2没有声明就没有返回值呢。
method1 和 tostring区别:没有区别貌似,不声明,都return []
method | foo | bar | Mumble | baz |
---|---|---|---|---|
method1 | foo 1 | baz 1 | ||
method2 | foo 2 | bar 2 | numble 2 | |
toString | foo | baz | ||
String: 只要没有声明就不return
覆盖override的方法:使用super的重要性。
public class Text {public static void main (String[] args) {new newPhone().showNum();}
}
//父类 手机
class Phone{public void sendMessage() {System.out.println("发短信");}public void call() {System.out.println("打电话");}public void showNum() {System.out.println("显示来电号码");}
}
//子类 新手机类
class newPhone extends Phone{//覆盖父类的来电显示号码功能,并增加自己的显示姓名和图片功能public void showNum() {//调用父类已经存在的功能使用supersuper.showNum();//增加自己特有显示姓名和头像功能System.out.println("显示姓名");System.out.println("显示头像");}
}
问题2:java是否有构造器
父类构造器无参数class Person {protected String name;Person(String name) {System.out.println("Person Constrctor-----" + name);}Person() {System.out.println("Person Constrctor-----");}
}
public class Husband extends Person {Husband() {// 从结果可以看出,系统自动调用的父类的Person()方法System.out.println("Husband Constructor...");}public static void main(String[] args) {Husband husband = new Husband();}
}
//results
Person Constrctor-----
Husband Constructor...
父类构造器无参数
class Person {protected String name;Person1(String name) {System.out.println("Person Constrctor-----" + name);}
}
public class Husband extends Person {Husband() {/** * 必须在这显式地调用父类的构造函数,否则会报错,因为父类里面我们新建了一* 个自定义了带参构造器,而没有重载一个无参构造器* 系统就不会为我们新建默认构造器了,在这里也就自然不会隐式地调用父类的* 默认构造器*/super("chenssy");System.out.println("Husband Constructor...");}public static void main(String[] args) {Husband husband = new Husband();}
}
// results:
Person Constrctor-----chenssy
Husband Constructor...
接口
public interface name {public type name(type name, ..., type name);public type name(type name, ..., type name);...
}
public class name implements interface
public interface ClosedShape {public double area();public double perimeter();
}
// Represents circles.
public class Circle implements ClosedShape {private double radius;// Constructs a new circle with the given radius.public Circle(double radius) {this.radius = radius;}// Returns the area of this circle.public double area() {return Math.PI * radius * radius;}// Returns the perimeter of this circle.public double perimeter() {return 2.0 * Math.PI * radius;}
}
JAVA封装、继承、多态仔细描述见课件4.6
第五章 函数式程序设计语言
目录
5.1 过程式语言存在的问题
懒求值
5.2.1 术语和表示法
lambda 表达式Church-Rosser定理
函数式描述方法
5.3.1 消除赋值/变量
5.3.2 以递归代替WHILE_DO
5.3.3 用嵌套代替语义相关的顺序
5.3.4 输出问题
5.3.5 表闭包
5.3.6 高阶函数
5.3.7 柯里化(Currying)
5.5 函数式语言SCALA
函数式语言的组成部分 : 程序结构 类型及其操作 表达式
以递归代替WHILE_DO:用递归仿真循环的终止条件是条件测试部分,函数如有返回值放 在then部分,递归函数体放在else部分,如果不需返回值则取消 一部分(else)。
急求值:急求值是严格求值(Strict),完全求值;急求值对应值调用,最安全
正规求值:函数中的形参每次出现均用实参替换(相当于传名调用)。要做多次实参计算。可能有副作用。
懒求值:是正规求值的特例。
它只到用到被结合的形参变量 的值时才对实参变元表达式求值(第一次出现),求值后将结果 值束定于形参变量,以后形参变量再次出现就不用求值了。
对于复杂的表达式如果子表达式求值对整个表达式求值没有影 响就不再求它。
短路(short circuit)求值,一般用作条件表达式,也叫短路条件。
E = Exp1 and Exp2 and …Expn 若Exp1求值为false,则E值已定为false。
再如: E = Exp1 or Exp2 or …Expn 若Exp1为true,则E值为true不论其它表达式取何值。
C、Ada及近代函数式语言均采用懒求值。
SCALA的常规操作
Int、Long、String、Char、Double、Float大写
val:常量;
var:值可变的变量;
元组是不同类型的值的聚集 (1, 3.14, "Fred")
list 类似于数组 Nil代表了空列表val nums: List[Int] = List(1, 2, 3, 4)// 整型列表val nums = 1 :: (2 :: (3 :: (4 :: Nil)))// 空列表val empty = Nil// 初始化ranks中每个值var ranks = links.mapValues(x=>1.0)
set: val set = Set(1,2,3)val nums: Set[Int] = Set()
map:var A:Map[Char,Int] = Map()val colors = Map("red" -> "#FF0000", "azure" -> "#F0FFFF")添加 key-value 对,可以使用 + 号 A + = ('J' -> 5)
mapValues不修改key, 只修改values
scala> var rdd1 = sc.makeRDD(Array((1,"A"),(2,"B"),(3,"C"),(4,"D")),2)//rdd1: org.apache.spark.rdd.RDD[(Int, String)] = ParallelCollectionRDD[27] at makeRDD at :21scala> rdd1.mapValues(x => x + "_").collect
//res26: Array[(Int, String)] = Array((1,A_), (2,B_), (3,C_), (4,D_))
// 表操作
L1 ::: L2 // 表L2并接到表L1的末尾
L1 ++: L2 // 表L1并接到表L2的头部
A +: List // 在列表List的头部添加一个元素A
List :+ A //在列表List的尾部添加一个元素A
(A /:List)(_+_) //对List中所有的元素进行相同的操作+A
++ 该方法用于连接两个集合,list1++list2。
// 函数定义
def functionName ([list of parameters]) : [return type] = {function bodyreturn [expr]
}
Scala的继承.
scala的trait:
Scala Trait(特征) 相当于 Java 的接口。
Scala的类只能够继承单一父类,但是如果是 Trait(特征) 的话就可以继承多个
trait Equal {def isEqual(x: Any): Boolean // isEqual 方法没有定义方法的实现def isNotEqual(x: Any): Boolean = !isEqual(x) // isNotEqual定义了方法的实现
}
使用SCALA extends 语言特性 关键字来扩展特征。然后使用override关键字来实现trait里面的任何抽象成员:
trait Iterator[A]{def hasNext: Booleandef next():A
}class IntIterator(to:Int) extends Iterator[Int]{private var current = 0override def hasNext: Boolean = current < tooverride def next(): Int={if(hasNext){val t = currentcurrent += 1t}else 0}
}
val iterator = new IntIterator(10)
iterator.next() // return 0
iterator.next() // return 1
SCALA的混入 (就是extend trait rather than extend class)
abstract class A{val message: String
}
class B extends A{val message = "instance of class B"
}
trait C extends A{def loadMessage = message.toUpperCase()
}
class D extends B with C
vak d = new D
println(d.message) // instance of class B
println(d.loudMessage) // INSTANCE OF CLASS B
Scala的条件选择
def gcd(a: Int,b: Int):Int = {if(a>b) gcd(a-b,b)else if (a<b) gcd(a,b-a) else a
}
柯里化(Currying)
p86
def add(x:Int): (Int)=>Int = {def square(t:Int): Int = { t*t }x + square(_)
} // add(1)(2) is 5
// 等价于下面的实现方式
def add(x:Int, y:Int): Int = { x + y*y }
val fun = 3 * _ //错误:无法推断出类型
val fun = 3 * (_: Double) //OK
(1 to 9) .map(“*” * _).foreach(println _)
*
**
***
****
*****
******
*******
********
*********
高阶函数
嵌套函数
def factorial(x:Int):Int = {def fact(x:Int, accumulator:Int):Int = {if(x<=1) accumulatorelse fact(x-1,x*accumulator)}fact(x,1)
}
println("factorial of 2:"+factorial(2))
println("factorial of 3:"+factorial(3))
//factorial of 2:2
//factorial of 3:6
案例类(Case classes)
案例类实例化时,不用使用关键字new
case class Book(isbn: String)
val frankenstein = Book("978-0486282114")
案例类在比较的时候是按值比较而非按引用比较:也就是和java不同,java比较的是地址。而case classes比较的是包括的变量的值是否相同,而不是这两个object存储地址是不是一个。
模式匹配match
def matchTest(x:Int):String = x match{case 1=> "one"case 2=> "two"case 3=> "other"
}
matchTest(3) // other
matchTest(1) // one
map
val scores = Map(“Alice” -> 10, “Bob” -> 3,”Cindy” -> 8)
map和flatMap操作的区别:
//flatMap(function)和map(function)操作都是传入的函数对RDD中的每一个元素进行处理的操作。flatMap是map的一种扩展。在flatMap中,我们会传入一个函数,该函数对每个输入都会返回一个集合(而不是一个元素),然后,flatMap把生成的多个集合“拍扁”成为一个集合。flatMap之后c变成List[(String, Double)]
var rdd1 = Array("hello world","good morning")
rdd1.map(_.splt(" "))
: Array[Array[String]] = Array(Array(hello,world),Array(good, morning))
rdd1.flatMap(_.splt(" "))
: Array[String] = Array(Hello, world, good, morning)
在Scala中, 用方括号来定义类型参数,例如
def printArgs(args:Array[String]):Unit={ // unit就是什么都不返回var i = 0while(i<args.length){i+=1}
}
def printArgs(args:Array[String]):Unit={ // unit就是什么都不返回for(arg<-args)println(arg)
}
def printArgs(args:Array[String]):Unit={ // unit就是什么都不返回args.foreach(println)
}
def formatArgs(args:Array[String]) = {args.mkString("\n")}
Scala的RDD
spark的分布式编程模型是RDD(弹性分布式数据集)。Spark将数据包装成RDD进行操作的。可以在并行计算中高效的进行数据共享和而提升计算性能。
// 由集合创建RDD
val rdd = sc.parallelize( List (1,2,3,4,5,6))
val rdd = sc.parallelize( List (1,2,3,4,5,6), 5)
// 结果一样但是分区不同。有多少个分区就有多少个任务。
//可以在parallelize中传入第二个参数设置RDD分区数量,否则就会根据集群的情况设置分区的数量。Spark为每一个分区运行一个任务来进行处理
//由文件创建RDD
val distFile = sc.textFile("hello.txt")
makeValues
作业
links: (pageID, linkList)
ranks:(pageID, PR)
每个页面的排序值:0.15+0.85*(contributionsReceived)
PR(A) = (PR(B)/L(B) + PR(C)/L(C) + ...)d + (1-d)/N // 为什么会有这个N呢
// links , ranks 是spark中的两个RDD
// 相邻页面列表 (pageID, 相邻链接ID列表)
var links
// 初始化ranks中每个页面的PR值为1.0
var ranks = links.mapValues(x=>1.0)
答案用到的函数:
建立RDD:sc.paralleize( Array( (),(),(),() ))
mapValue修改map的value部分
RDD的join操作
返回结果是前面和后面集合中配对成功的
reduceByKey将相同key的value按定义的规则合并
join操作将两个RDD按key连接value,c的形式是Array[(String, (List[String], Double))]
答案:
// 初始化ranks and links
var links = sc.parallelize(Array(("A",List("B","C","D")), ("B",List("A")), ("C",List("A","B")), ("D",List("B","C"))),1)
var ranks = links.mapValues(x=>1.0) links.first ranks.first
for ( i <- 1 to 10){ // join 函数将 key 相同的 value 连接到⼀个列表中 val contribs = links.join(ranks) // (A (B,C,D) 1.0), (B (A) 1.0) ...val flatMapRDD = contribs.flatMap { case (url,(links,rank)) => links.map(dest => (dest, rank/links.size)) } //通过 reduce 函数计算每个 link 本轮更新后的权值 val reduceByKeyRDD = flatMapRDD.reduceByKey(_ + _) ranks = reduceByKeyRDD.mapValues(0.15 + 0.85 * _)
第六章 逻辑式程序设计语言
第 七 章 并发程序设计语言
并发程序设计的模型:基本概念
1.共享变量模型 2.消息传递模型 3.数据并行模型 4.面向对象模型
基于共享变量的通讯机制
一、 忙等待(busy wait)
二、 信号量(semaphore)
三、条件临界区(CCR)
例: 用CCR实现的生产者与消费者
四、监控器
GO
代码初始化 “:= ” 而不是“=”
goroutine
在go里,每一个并发执行的活动称为goroutine
程序启动时,只有一个goroutine来调用main函数,其被称为主goroutine。
终止:main函数返回时,所有正在运行的goroutine都直接中止
channel
channel是让一个goroutine发送特定值到另一个goroutine的通信机制
一个通道只能发送相应类型 的值
// chan是关键字。通道在使用前必须先创建。
ch := make(chan int) // create channel
ch := make(chan int) // 无缓冲通道
ch := make(chan int,0) // 无缓冲通道
ch := make(chan int,3) // 容量为3的缓冲通道
默认情况下,通道是不带缓冲区的。发送端发送数据,同时必须有接收端相应的接收数据。通道的两个主要操作:发送(send)和接收(receive)
ch <- x //将x发送到通道ch
x = <- ch //使用x接受通道的值
<- ch // 接受ch传递过来的值后,丢弃结果
通道的第三个操作:关闭(close)。
close(ch)
\1. 互斥锁:sync.Mutex
\2. 读写互斥锁:sync.RWMutex
\1. 组等待:sync.Mutex
\2. 延迟初始化:sync.Once.
cap(c) : channel c的缓冲大小
WaitGroup的用途:它能够一直等到所有的goroutine执行完成,并且阻塞主线程的执行,直到所有的goroutine执行完成。
WaitGroup总共有三个方法:Add(delta int),Done(),Wait()。
Add:添加或者减少等待goroutine的数量
Done:相当于Add(-1)
Wait:执行阻塞,直到所有的WaitGroup数量变成0
go的并发机制举例:
go例子1:
有for循环
package main
import ("fmt""time"
)
func say(s string) {for i := 0; i < 5; i++ {time.Sleep(100 * time.Millisecond)fmt.Println(s)}
}
func main() {go say("world")say("hello")
}
// 执行以上代码,你会看到输出的 hello 和 world 是没有固定先后顺序。因为它们是两个 goroutine 在执行:
go例子2:
有数组, for 别种便利方式
package main
import "fmt"
func sum(s []int, c chan int) {sum := 0for _, v := range s {sum += v}c <- sum // 把 sum 发送到通道 c
}
func main() {s := []int{7, 2, 8, -9, 4, 0}c := make(chan int) go sum(s[:len(s)/2], c) // 前三个go sum(s[len(s)/2:], c) // 后三个x, y := <-c, <-c // 从通道 c 中接收fmt.Println(x, y, x+y)
}
// output: -5 17 12。 通道是先进来的后出去,当容量是1的时候。
go 斐波那契额
package main
import ("fmt"
)
func fibonacci(n int, c chan int) {x, y := 0, 1for i := 0; i < n; i++ {c <- xx, y = y, x+y}close(c)
}
func main() {c := make(chan int, 10)go fibonacci(cap(c), c)// range 函数遍历每个从通道接收到的数据,因为 c 在发送完 10 个// 会结束,从而在接收第 11 个数据的时候就阻塞了。for i := range c {fmt.Println(i)}
}
go中 sync.WaitGroup 的作用
reference:https://blog.csdn.net/u013474436/article/details/88749749
主线程为了等待goroutine都运行完毕,不得不在程序的末尾使用time.Sleep()
来睡眠一段时间,
package main
import ("fmt""time"
)
func main(){for i := 0; i < 100 ; i++{go fmt.Println(i)}time.Sleep(time.Second)
}
但是实际中,并不知道goroutine线程运行时间,所以需要用管道来完成上述操作。(因为管道内部的数据不清楚是会阻塞的。
go语言中有一个其他的工具sync.WaitGroup
能更加方便的帮助我们达到这个目的
WaitGroup
对象内部有一个计数器,最初从0开始,它有三个方法:Add(), Done(), Wait()
用来控制计数器的数量。Add(n)
把计数器设置为n
,Done()
每次把计数器-1
,wait()
会阻塞代码的运行,直到计数器地值减为0
。
func main() {wg := sync.WaitGroup{}wg.Add(100)for i := 0; i < 100; i++ {go func(i int) {fmt.Println(i)wg.Done()}(i)}wg.Wait()
}
指称语义的原理与应用(指称语义那道题竟然被我压中了,所以考试直接写的hh)
存储:
Store = Location → ( stored Storable + undefined + unused)
辅助函数
empty-store : Store
allocate : Store →Store × Location //sto映射为了sto' , 某些单元loc从未使用变成了未定义
deallocate : Store ×Location → Store //loc变成了未定义,从而映射出新的sto'
update : Store × Location × Storable→Store //loc单元的值更新为stable, 得到sto'
fetch : Store × Location→Storable // 取地址中的值
环境:
Environment, Identifier, Bindable
Environ = ldentifier→(bound Bindable + unbound)
辅助函数
empty_environment
bind // bind(k,3) 只要有了标识符束定于可束定体,环境状态就改变了
overlay //overlay(env,env)两个束定环境组成新环境
find //find(env,j)环境env中找到标识符I所对应的可束定体bdble
1 command
语义函数:Command -> (Environ -> Store -> Store).
关键词:execute
sto变为sto',env没变
Command ::= Skip
| ldentifier := Expression
| let Declaration in Command
| Command; Command
| if Expression then Command else Command
| while Expression do Command
// 加入了函数
| Identifier (Actual_Parametor)
execute【c】env sto = sto'
execute 〖Skip〗 env sto = sto
execute 〖I:= E〗 env sto =let val = evaluate E env sto inlet variable loc = find (env,I) inupdate(sto,loc,val) // 更新loc 得到新的stoexecute 〖let D in C〗 env sto = // D改变了env和stolet (env',sto') = elaborate D env sto in // D是elaborate【D】 env sto = (env' , sto')execute C (overlay (env',env)) sto' // 但是in C,所以也包括c的env?execute 〖C1; C2〗 env sto =execute C2 env (execute C1 env sto)execute 〖 if E then C1 else C2〗 env sto =if evaluate E env sto = truth_value true // evaluate【E】env sto = ...then execute C1 env sto // command不改env,改变stoelse execute C2 env sto
execute 〖while E do C〗=let execute_while env sto = if evaluate E env sto = truth_value truethen execute_while env (execute C env sto)else stoinexecute_while execute 〖I(AP)〗 env sto =let procedure proc = find(env,I) inlet arg = give_argument AP env sto inpro argg sto
2 expression
Expression ->(Environ -> Store - > Value)
关键词evaluate:
Expression ::= Numeral
| false
| true
| Identifier // 可以当成一个变量
| Expression + Expression
| Expression < Expression
| not Expression
| ...
//加入了函数
| Identifier (Actual_Parmenter)
evaluate【E】env sto = ...
evaluate〖N〗env sto = integer(valuation N)
evaluate〖false〗env sto = truth_value false
evatuate〖I〗env sto = coerce(sto,find(env,I))
evatuate〖E1+E2〗env sto = let integer int1 = evaluate E1 env sto inlet integer int2 = evaluate E2 env sto ininteger(sum (int1,int2 ))
evatuate〖E1<E2〗env sto = let integer int1 = evaluate E1 env sto inlet integer int2 = evaluate E2 env sto intruth_value( less(int1,int2 ))
evaluate〖not E〗 env sto =let truth_value tr = evaluate E env sto intruth_value(not(tr))
// 函数
evaluate 〖I(AP)〗 env =let function func = find (env,I) in // find在环境中找到标识符I所束定的束定体let arg = give_argument AP env in // AP标准:give_argument [] envfunc arg
3 Declaration
Elaborate: Declaration ->(Environ->Store -> Environ * store)
Declaration ::= const ldentifier = Expression
| var ldentifier : Type_denoter
// 加入了函数
| func Identifier (Formal_Parameter) is Expression
| proc Identifier (Formal_paramenter) is Command
elaborate【D】 env sto = (env' , sto' )
elaborate 〖 const I = E 〗 env sto =let val = evaluate E env sto in(bind (I,value val),sto)elaborate 〖 var I:T〗 env sto =let (sto',loc)= allocate sto in(bind (I,variable loc),sto') elaborate 〖fun I(FP) is E 〗 env =let func arg =let parenv = bind_parameter FP arg in //fp标准:bind_parameter [] argevaluate E (overlay (parenv,env )) // overlay环境的结合, 束定改变的是条件in(bind (I,function func)) // bind表示有了新的标识符束定于可束定体,环境改变elaborate 〖proc I(FP) is C〗env sto=let proc arg sto'=let parent = bind - parameter FP arg inexecute C(overlay (parenv env)) sto'in(bind(I,[procedure proc],sto)
程序设计语言原理复习总结 北航计算机专业课相关推荐
- 北航2020秋季学期程序设计语言原理复习
课程内容复习 Lec5 函数式语言 急求值:急求值是严格求值(Strict),完全求值:急求值对应值调用,最安全 正规求值:函数中的形参每次出现均用实参替换(相当于传名调用).要做多次实参计算.可能有 ...
- 【编程实践】第一章 C++入门 《C++程序设计语言》 / By 禅与计算机程序设计艺术ChatGPT
目录 <C++程序设计语言> / By 禅与计算机程序设计艺术&ChatGPT 内容简介 第一章 C++入门 1.1 C++的起源和发展 1.1.1 C++的历史和背景 1.1.2 ...
- 【编程实践】第二章 C++面向对象编程《C++程序设计语言》 / By 禅与计算机程序设计艺术ChatGPT
[编程实践]第二章 C++面向对象编程<C++程序设计语言> / By 禅与计算机程序设计艺术&ChatGPT 第二章 C++面向对象编程 在C++中,面向对象编程主要通过类和对象 ...
- 计算机考研专业课复习资料,考研计算机专业课复习重点归纳
考研计算机专业课复习重点归纳中国大学网考研:一.数据结构的章节结构及重点构成 数据结构学科的章节划分基本上为:概论,线性表,栈和队列,串,多维数组和广义表,树和二叉树,图,查找,内排,外排,文件,动态 ...
- 考研计算机专业课怎么复习,2016考研计算机专业课如何复习?
2016考研计算机专业课如何复习? ?基础复习阶段 以指定参考书为主,兼顾笔记,进行专业课的第一轮复习.看书要以理解为主,不必纠缠于细节,并在不懂的知识点处做上标记. 第一步,选择一本难度适宜.内容全 ...
- 考研计算机专业课复习,考研中计算机专业课的复习考试.pdf
考研中计算机专业课的复习考试.pdf 第20卷第1期 西昌学院学报·自然科学版 VoL20-№1 of ScienceEdition Mar..2006 2006年3月 Joumal College' ...
- python是面向什么的计算机程序设计语言_Python是一种计算机程序设计语言,python到底该怎么学习...
1.首先我们要了解python是什么: Python是一种计算机程序设计语言.是一种面向对象的动态类型语言,最初被设计用于编写自动化脚本(shell)随着版本的不断更新和语言新功能的添加,越来越多被用 ...
- 是计算机程序设计语言的是,计算机设计语言
计算机设计语言是编写计算机程序所用的语言,可分为机器语言.汇编语言和高级语言.[1] 中文名 计算机设计语言 外文名 Computer language design 定 义 编写计算机程序的语 ...
- C语言是应用最为广泛的一种高级程序设计语言
前 言 近年来,C语言是应用最为广泛的一种高级程序设计语言,它不仅是计算机专业学生的必修课,也是许多非计算机专业学生所青睐的技术学科.C语言程序设计是全国和各省计算机等级考试的重要考试内容.C语言功能 ...
最新文章
- “5G杀手级应用”Cloud VR 华为如何打响5G第一枪
- 使用Eclipse+PyDev+EclipseHtmlEditor搭建Django开发环境
- 第一人称视角的一种解决方案
- beacon帧字段结构最全总结(三)——VHT字段总结
- Boost:Bimap简单示例
- php网站xml链接,xml图像超链接的制作代码
- 使用Python构建推荐系统的机器学习
- opencv Hog学习总结
- MySQL数据库服务器搭建及基本管理
- 生成.pkl文件,读取.pkl文件的内容
- Linux环境下配置虚拟ip,方法1:新增多个子网卡,每个子网卡有独立的配置文件
- 电子政务互联互通软件平台的体系架构
- 第三届“泰迪杯”数据分析职业技能大赛: 教育平台的线上课程智能推荐 (决赛候选)答辩PPT
- 深度学习入门极简教程(一)
- 系统集成项目管理工程师是哪个部门发证?
- 痛苦的evo安装之旅,终于成功了
- 吐槽最新的chrome浏览器.
- 滴水石穿,奇迹是一点点实现的
- 计算机专业实用设计专利例子,计算机类实用新型专利有几个发明人
- 004永磁同步电机的工作原理:大白话详细讲解从最简单的直流有刷电机到永磁同步电机是如何转动起来的
热门文章
- 大物实验密立根数据处理Python program1
- windows系统下搭建cloudreve网盘系统
- 调用worldlingo.com翻译服务,制作的一个多国语言翻译工具
- 戴尔服务器光盘装Linux系统,DELL服务器引导光盘下载连接,DELL引导盘万能驱动支持所有型号...
- 【WRF如何在输出的wrfoutput文件中设置添加/删除变量】
- Android变脸幕后的魔法师:各巨头…
- [考研]考研倒计时第5天
- [附源码]Nodejs计算机毕业设计洗浴管理系统Express(程序+LW)
- Python 模拟发送键盘按键
- hp刀片服务器系统flex-10,HPE ProLiant BL460c Gen10 刀片服务器