--
-
访问时间
-
时间局部性(temporal locality)
时间局部性指的是:被引用过一次的存储器位置在未来会被多次引用(通常在循环中)。
空间局部性(spatial locality)
如果一个存储器的位置被引用,那么将来他附近的位置也会被引用。
---
--
-
访问时间
-
时间局部性(temporal locality)
时间局部性指的是:被引用过一次的存储器位置在未来会被多次引用(通常在循环中)。
空间局部性(spatial locality)
如果一个存储器的位置被引用,那么将来他附近的位置也会被引用。
---
转载于:https://www.cnblogs.com/Ph-one/p/7680913.html
《CSAPP》 6.2 局部性
1、局部性分类
局部性原理对硬件和软件系统的设计和性能都有极大的影响。
局部性通常分为:时间局部性和空间局部性。1)时间局部性
时间局部性是指被引用过一次的内存位置很可能在不远的将来再被多次引用。
2)空间局部性
空间局部性是指如果一个内存位置被引用了一次,那么程序很可能在不远的将来引用其附近的一个内存位置。
3)局部性原理举例
比如,一个多维数组,我们按照行优先遍历的方法即一行完了再下一行的方式就是具有良好的空间局部性,因为计算机存储这些数据时也是按照行优先的方式进行的,所以行优先遍历就是依次访问相邻位置的数据。如果用列优先遍历,空间局部性就很差,因为它不是访问相邻单元的数据。
int sumvec(int v[N])
{
int i, sum = 0;
for(i = 0; i < N; i++)
sum += v[i];
return sum;
}
下一篇进行解答。
《深入理解计算机系统》第6章.存储器层次结构
一、函数
函数是什么?
函数一词来源于数学,但编程中的「函数」概念,与数学中的函数是有很大不同的,具体区别,我们后面会讲,编程中的函数在英文中也有很多不同的叫法。在BASIC中叫做subroutine(子过程或子程序),在Pascal中叫做procedure(过程)和function,在C中只有function,在Java里面叫做method。
定义: 函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可
特性:
- 减少重复代码
- 使程序变的可扩展
- 使程序变得易维护
形参变量
只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量
实参
可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使参数获得确定值
默认参数
def
stu_register(name,age,course,country
=
"CN"
):
这样,这个参数在调用时不指定,那默认就是CN,指定了的话,就用你指定的值。关键参数
正常情况下,给函数传参数要按顺序,不想按顺序就可以用关键参数,只需指定参数名即可,但记住一个要求就是,关键参数必须放在位置
非固定参数
若你的函数在定义时不确定用户想传入多少个参数,就可以使用非固定参数
*
args
*args 会把多传入的参数变成一个元组形式
**kwargs
*kwargs 会把多传入的关键字参数变成一个dict形式
全局与局部变量
在子程序中定义的变量称为局部变量,在程序的一开始定义的变量称为全局变量。全局变量作用域是整个程序,局部变量作用域是定义该变量的子程序。当全局变量与局部变量同名时:在定义局部变量的子程序内,局部变量起作用;在其它地方全局变量起作用。在子程序中定义的变量称为局部变量,在程序的一开始定义的变量称为全局变量。全局变量作用域是整个程序,局部变量作用域是定义该变量的子程序。当全局变量与局部变量同名时:在定义局部变量的子程序内,局部变量起作用;在其它地方全局变量起作用。一些简单的str、int类型的参数可以不再函数内部被改变,但是如list等复杂的变量可以在函数内被修改返回值函数在执行过程中只要遇到return语句,就会停止执行并返回结果,so 也可以理解为 return 语句代表着函数的结束如果未在函数中指定return,那这个函数的返回值为None
#!/usr/bin/env python # -*- coding:utf-8 -*- # Author:Glen # 递归函数 def calc(n): print(n) if int(n/2) == 0: # 条出递归函数的条件 return n # 返回n,不再是函数 return calc(int(n/2)) ''' 函数的参数 1 形参 形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量 2 实参 可以是任何类型,把值传给形参 ''' # 默认参数 位置参数 def fun1(name, age, sex='n'): print(name, sex, age) fun1('glen', 26) # sex有默认参数,因此可以不用赋值 # fun1('glen', sex='f', 26) # 位置参数必须放在关键字参数前面,否则报错 fun1('glen', 25, 'f') # 传参数时sex='f' 或者 'f',都可以,但是这样做的时候参数的顺序必须固定 # 关键字参数 def fun2(name='glen', age=22, country='cn'): print(name, age, country) # 正常情况下,给函数传参数要按顺序,不想按顺序就可以用关键参数, # 只需指定参数名即可,但记住一个要求就是,关键参数必须放在位置参数之后。 fun2('jack', age=33, country='us') # 非固定参数 # 若你的函数在定义时不确定用户想传入多少个参数,就可以使用非固定参数 def fun3(name, age, *args): print(name, age, args) fun3('glen', 26, 'CN', 'IT', 'CD') # *args 会把多传入的参数变成一个元组形式 # 还可以有一个**kwargs def fun4(name, age=26, *args, country='cn', **kwargs): print(name, age, args, country, kwargs) fun4('glen', 23, 'a', 'b', a=1, b=2, c=3) # 把没有定义的关键字参数放入一个字典 # 局部变量 name = 'glen' list_test = [1, 2, 3, 7] age = 23 def change_name(): global age # global声明为全局变量,可以实现修改,但是不能这么做,函数被调用一次就会修改一次,逻辑混乱 age = 26 name = 'jack' # 只在这个函数的内部生效,且调用周期结束就回收 list_test[0] = 9 # 列表可以在被调用的函数里面修改,不能被修改的只能时字符串等一些简单变量 print(name) change_name() print(name) # 没有被改变 print(list_test) # 列表在函数内被改变了 print(age) # 被修改了 def fun5(n): if n == 5: return 5 # 当n=5时有返回值,为5,否则就返回None a = fun5(6) b = fun5(5) print(a, b)
二、作业
有以下员工信息表
当然此表你在文件存储时可以这样表示
11
,Alex Li,
22
,
13651054608
,IT,
2013
-
04
-
01
现需要对这个员工信息文件,实现增删改查操作
- 可进行模糊查询,语法至少支持下面3种:
- select name,age from staff_table where age > 22
- select * from staff_table where dept = "IT"
- select * from staff_table where enroll_date like "2013"
- 查到的信息,打印后,最后面还要显示查到的条数
- 可创建新员工纪录,以phone做唯一键,staff_id需自增
- 可删除指定员工信息纪录,输入员工id,即可删除
- 可修改员工信息,语法如下:
- UPDATE staff_table SET dept="Market" WHERE where dept = "IT"
注意:以上需求,要充分使用函数,请尽你的最大限度来减少重复代码!
#!/usr/bin/env python # -*- coding:utf-8 -*- # Author:Glen emp = {} staff_id_que = (x for x in range(1000)) select = {'op': 'select', 'field': [], 'condition': []} update = {'op': 'update', 'field': [], 'condition': []} def read_to_emp(): with open('emp', 'r') as f: for line in f: emp[line.split(',')[3]] = dict(zip(['staff_id', 'name', 'age', 'phone', 'dept', 'enroll_date'], line.split(','))) def cmd_exec(cmd): for key in emp: for field in cmd['field']: condition_field = cmd['condition'][0] condition_op = cmd['condition'][1] condition_value = cmd['condition'][2] if condition_op == '=': if emp[key][condition_field] == condition_value: if cmd['op'] == 'update': emp[key][cmd['field'][0]] = cmd['field'][1] else: print(emp[key][field] + ' ', end='') elif condition_op == '<': if int(emp[key][condition_field]) < int(condition_value): if cmd['op'] == 'update': emp[key][cmd['field'][0]] = cmd['field'][1] else: print(emp[key][field] + ' ', end='') else: if int(emp[key][condition_field]) > int(condition_value): if cmd['op'] == 'update': emp[key][cmd['field'][0]] = cmd['field'][1] else: print(emp[key][field] + ' ', end='') def handle_cmd(cmd='select name,age from emp where age > 10'): if cmd.startswith('select'): select_field = cmd.split()[1] select_condition = cmd.split()[4:] if select_field == '*': select['field'] = ['staff_id', 'name', 'age', 'phone', 'enroll_date'] else: select['field'] = select_field.split(',') if select_condition: select['condition'] = select_condition[1:] cmd_exec(select) elif cmd.startswith('update'): update_file = cmd.split()[3] update_condition = cmd.split()[5:] update['field'] = update_file.split('=') update['condition'] = update_condition cmd_exec(update) def write_to_file(member): with open('emp', 'w') as f2: for key in member: line = '%s,%s,%s,%s,%s,%s' % (member[key]['staff_id'], member[key]['name'], member[key]['age'], member[key]['phone'], member[key]['dept'], member[key]['enroll_date'],) print(line) f2.write(line) read_to_emp() while True: print(emp) command = input('>') if command.startswith('select') or command.startswith('update'): handle_cmd(command) elif command == 'w': write_to_file(emp) elif command == 'q': exit(0) else: print('input error')
转载于:https://www.cnblogs.com/starcor/p/9486312.html
在Java语言中,根据定义变量位置的不同,可以将变量分成两大类:成员变量和局部变量。成员变量和局部变量的运行机制存在较大差异。
成员变量和局部变量是什么
成员变量指的是声明在一个类中,但在方法、构造方法和语句块之外的变量, 局部变量指的是声明在方法、构造方法或者语句块中里的变量。
不管是成员变量还是局部变量,都应该遵守相同的命名规则:从语法角度来看,只要是一个合法的标识符即可;但从程序可读性角度来看,应该遵循驼峰命名法的原则:首个单词首字母小写,后面每个单词首字母大写。
成员变量
Java 程序中的变量划分如图一:变量被分为类变量和实例变量两种,定义成员变量时没有 static 修饰的就是实例变量,有 static 修饰的就是类变量。
正是基于这个原因,可以把类变量和实例变量统称为成员变量。
只要类存在,程序就可以访问该类的类变量。在程序中访问类变量通过如下语法
类.类变量
只要实例存在,程序就可以访问该实例的实例变量。在程序中访问实例变量通过如下语法:
实例.实例变量
当然,类变量 可以让该类的实例来访问 通过实例来访问类变量的语法如下:
实例.类变量
但由于这个实例并不拥有这个类变量,因此它访问的并不是这个实例的变量,依然是访问它对应类的类变量。
也就是说如果通过一个实例修改了类变量的值,由于这个类变并不属于它,而是属于它对应的类,因此,修改的依然是类的类变量,与通过该类来修改类变量的结果完全相同。
下面程序定义了 Person类, 在这个Person类中定义两个成员变量,一个实例变量 name ,以及一个类变量 eyeNum,并分别通过 Person 类和 Person实例来访问实例变量和类变量:
class Person {
//定义一个实例变量
public String name;
//定义一个类变量
public static int eyeNum;
}
public class PersonTest {
public static void main(String[] args) {
//第一次主动使用Person类,该类自动初始化,则eyeNum变量开始起作用,输出
System.out.println("Person 的 eyeNum 类变量值:" + Person.eyeNum);
//创建Person对象
Person p = new Person();
//通过Person对象的引用p来访问Person对象name实例变量
//并通过实例访问eyeNum类变量
System.out.println("p变量的 name变量值是:" + p.name + ";p对象的 eyeNum 变量值是:" + p.eyeNum);
//直接为name实例变量赋值
p.name = "孙悟空";
//通过P访问eyeNum类变量,依然是访问Person类的eyeNum类变量
p.eyeNum = 2;
//再次通过Person对象来访问name实例变量和eyeNum类变量
System.out.println("p变量的 name变量值是:" + p.name + ";P 对象的 eyeNum 变量值是:" + p.eyeNum);
//前面通过P修改了 Person的eyeNum,此处的Person. eyeNum将输出2
System.out.println("Person类的 eyeNum 类变量值:" + Person.eyeNum);
Person p2 = new Person();
// P2访问的eyeNum类变量依然引用Person类的,因此依然输出2
System.out.println("p2对象的 eyeNum类变量值:" + p2.eyeNum);
}
}
结果:
Person 的 eyeNum 类变量值:0
p变量的 name变量值是:null;p对象的 eyeNum 变量值是:0
p变量的 name变量值是:孙悟空;P 对象的 eyeNum 变量值是:2
Person类的 eyeNum 类变量值:2
p2对象的 eyeNum类变量值:2
成员变量无须显式初始化:只要为一个类定义了类变量或实例变量,系统就会在这个类的准备阶段或创建该类的实例时进行默认初始化,成员变量默认初始化时的赋值规则与数组动态初始化时数组元素的赋值规则完全相同。
类变量的作用域比实例变量的作用域更大:实例变量随实例的 存在而存在,而类变量则随类的存在而存在。实例也可访问类变量,同一个类的所有实例访问类变量时, 实际上访问的是该类本身的同一个变量,也就是说,访问了同一片内存区。
局部变量根据定义形式的不同,又可以被分为如下三种。
与成员变量不同的是,局部变量除了形参之外,都必须显式初始化。也就是说,必须先给方法局部变量和代码块局部变量指定初始值,否则不可以访问它们。
代码块局部变量的作用域是所在代码块,只要离开了代码块局部变量所在的代码块,这个局部变量就立即被销毁, 变为不可见
方法局部变量,其作用域从定义该变量开始,直到该方法结束
形参的作用域是整个方法体内有效,而且形参也无须显式初始化,形参的初始化在调用该方法时由 系统完成,形参的值由方法的调用者负责指定
当通过类或对象调用某个方法时,系统会在该方法栈区内为所有的形参分配内存空间,并将实参的值赋给对应的形参,这就完成了形参的初始化。
在同一个类里,成员变量的作用范围是整个类内有效,
Java允许局部变量和成员变量同名,如果方法里的局部变量和成员变量同名,局部变量会覆盖成员 变量,如果需要在这个方法里引用被覆盖的成员变量,则可使用this (对于实例变量)或类名(对于类变量)作为调用者来限定访问成员变量。
当系统加载类或创建该类的实例时,系统自动为成员变量分配内存空间,并在分配内存空间后,自动为成员变量指定初始值。
//创建第一个Person对象
Person pl = new Person();
//创建第二个Person对象
Person p2 = new Person();
//分别对两个Person对象的name实例变量赋值
pl.name = "张三";
p2.name = "孙悟空";
//分别为两个Person对象的eyeNum类变量赋值
pl.eyeNum =2;
p2.eyeNum = 3;
从图一中可以看出,当Person 类初始化完成后,系统将在堆内存中为Person类分配一块内存区(当 Person类初始化完成后,系统会为 Person类创建一个类对象),在这块内存区里包含了保存eyeNum类变量的内存,并设 置eyeNum的默认初始值:0。
从图二中可以看出,eyeNum类变量并不属于Person对象,它是属于Person类的,所以创建第 ―个Person对象时并不需要为eyeNum类变量分配内存,系统只是为name实例变量分配了内存空间, 并指定默认初始值:null。
接着执行Person p2 = new Person(); 代码创建第二个Person对象,此时因为Person类已经存在于堆内存中了,所以不再需要对Person类进行初始化。创建第二个Person对象与创建第一个Person对象并 没有什么不同。
当程序执行 p1.name =“张三”; 代码时,将为p1的name实例变量赋值,也就是让图二中堆内存中的name指向”张三"字符串。执行完成后,两个Person对象在内存中的存储示意图如图三所示。
从图四中可以看出,当通过p1来访问类变量时,实际上访问的是Person类的eyeNum类变量。 事实上,所有的Person实例访问eyeNum类变量时都将访问到Person类的eyeNum类变量,也就是图四中灰色覆盖的区域,本质其实还是通过Person类来访问eyeNum类变量时,访问的是同一块内存。基于这个理由,当程序需要访问类变量时,尽量使用类作为主调,而不要使用对象作为主调,这样可以避免程序产生歧义,提高程序的可读性。
局部变量定义后,必须经过显式初始化后才能使用,系统不会为局部变量执行初始化。这意味着定 义局部变量后,系统并未为这个变量分配内存空间,直到等到程序为这个变量赋初始值时,系统才会为局部变量分配内存,并将初始值保存到这块内存中。
与成员变量不同,局部变量不属于任何类或实例,因此它总是保存在其所在方法的栈内存中。
栈内存中的变量无须系统垃圾回收,往往随方法或代码块的运行结束而结束。因此,局部变量的作用域是从初始化该变量开始,直到该方法或该代码块运行完成而结束。因为局部变量只保存基本类型的值或者对象的引用,因此局部变量所占的内存区通常比较小。
Java Review系列目录
⇐⇐Java Review (七、面向对象----方法深入) Java Review (九、面向对象----封装)⇒⇒ |
【1】:《疯狂Java讲义》
【2】:《Java核心技术 卷一》
【3】:https://www.runoob.com/java/java-variable-types.html