琐碎的想法(五)for 的前世今生
for
起因
记得大学上C语言的课,第一次遇到的问题就是循环结构里面的 for
。
选择结构的 if
非常易懂,和日常生活的判断没有区别。
循环结构的 while
同样比较好理解。
本质上是一个判断
- 如果为真,继续循环。
- 如果不假,则退出循环。
而 for
会稍微复杂一些。
for (init-expr; test-expr; update-expr)
body-statement
- 初始化表达式只执行一次
- 判断表达式执行
- 判断为真执行循环体
- 判断为假退出循环体
- 执行更新表达式
在实际的语义等同于(唯一区别是init-expr一个是内部变量,一个是外部变量)
init-expr
while (test-expr) {
body-statement
update-expr
}
那么我们为什么要设计一个不那么好理解的循环结构呢?
因为这时候才入了编程的门————抽象,以及约定。
如果我们再往底层挖,会发现在汇编语言中是不存在while
、for
关键字的。
最开始的程序总是从左到右,从上到下一条路走到黑的。
后面编程人员意识到编写重复的代码过于麻烦才创造了 loop
。
所以最开始需要人工写一个for或者while循环。
while
好理解在于和自然语言(英语)完全符合。
当 条件满足 时, {
执行 流程;
}
而 for
循环的好处在于规范了 while
的使用。
- 初始化语句(init-expr)一般只用于循环,所以放在内部,便于回收变量。
- 循环条件(test-expr)一般配合更新语句一起使用(update-expr),实现循环有限次数。
- 三者的拆分使编写大段的循环或者嵌套循环时,更易读。
// 传统while循环
int i = 0;
while (i < 10) {
handleX();
i++;
int j = 0;
while (j < 5) {
handleY();
j++;
}
}
// for循环
for (int i = 0; i < 10; i++) {
handleX();
for (int j = 0; j < 5; j++) {
handleY();
}
}
所以 for 循环的出现也意味着编程人员开始在意的不仅仅是功能,而且看重可读性。
然而这并不会被满足。
之后还出现了
- 增强
for
,部分语言的for-each,for...in
。 lamdba
表达式中的forEach()
方法。
注意:以下按 Java 实现的 foreach 举例。(其他编程语言不太熟悉)
增强 for
foreach
的规则
- 所有使用
foreach
的集合都必须实现Iterable
接口 - 通过
iterator()
获取iterator
对象 - 通过
iterator.hasNext()
判断是否存在元素。 - 通过
iterator.next()
获取下一个元素。 - 通过
iterator.remove()
移除返回的元素。(可选)
增强 for
的语法
List<String> list = Arrays.asList("1", "2", "3", "4", "5");
// for 版本
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
// foreach 版本
for (String e : list) {
System.out.println(e);
}
// 去"糖"后的while版本
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
可以看出,foreach
对于 for、while
来说,好处是更加简单,符合直觉。
- 无需判断集合个数和中间变量减少代码出错可能性,统一通过
hasNext()
处理。 - 不用分析每个集合类如何获取元素,统一通过
next()
处理。 - 通过语法糖隐藏了
hasNext(), next()
逻辑,代码更易读。
forEach()
Java 7/8 受到了函数式语言的影响,实现了更简练的写法。
// Iterable<T> 内实现的forEach
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
List<String> list = Arrays.asList("1", "2", "3", "4", "5");
// forEach()
list.forEach(i -> System.out.print(i));
// 方法引用
list.forEach(System.out::print);
可以看出,forEach()的好处显而易见。
- 代码量比
foreach
更少,只关注遍历元素,甚至连元素类型都可以省略。 - 使用了方法引用后更进一步,我们关注的是这个集合执行了哪些操作,遍历每一次的含义在forEach()的方法已经体现了,甚至不需要写遍历的元素。
总结
可以看出,编程人员一直追寻的是更简单,更易读的代码。
- 他们不满足于汇编语言一遍遍的写同一行代码,创造了 while
- 不满足于 复杂或多层 while 的不可读, 创造了 for
- 不满足于 for 循环每一次定义的中间变量,创造了 foreach
- 不满足于 foreach 需要循环每一次的元素,利用了lamdba 的 Consumer, 去掉了元素。
琐碎的想法(五)for 的前世今生的更多相关文章
- Python-装饰器(语法糖)上下五千年和前世今生
装饰器上下五千年和前世今生,这里我们始终要问,装饰器为何产生?装饰器产生解决了什么问题?什么样的需求推动了装饰器的产生?思考问题的时候,始终要问,为什么要这样,而不是那样或者其他样.这里我不先说,也不 ...
- 琐碎的想法(三)对Java的批评的看法
编写本文的目的 在大环境下,Java是一个饱受争议的语言,一方面在工程上它的流行程度非常高:另一方面,越是资深的软件工程师就越容易对这个语言感到不满. 在这种情况下,博主希望每一个Java程序员能够耐 ...
- 《principles of model checking》中的离散时间马尔科夫链
<principles of model checking>中的离散时间马尔科夫链 说明:此文为我自学<principles of model checking>第十章内容的笔 ...
- Angular2+ 使用 Protractor 与 Modify Header Value (HTTP Headers) 插件 完成 Windows Authorization 验证
入职新公司第二周,接到了一个E2E测试的任务,两天的时间把所有的测试条件都写完了,结果剩下三天都卡在了Windows Authorization验证这里. 先说一下公司项目Authorize的逻辑 第 ...
- 一个想法(续五):IT联盟创业计划:现阶段进度公示、疑问解答及进行中的计划
前言: 首先今天是元宵节,先祝大伙元宵节快,单纯的快乐! 然后看看开展中的计划: IT联盟创业计划众筹发起:一个想法(续三):一份IT技术联盟创业计划书,开启众筹创业征程 IT联盟创业计划众筹进度:一 ...
- Python GUI之tkinter窗口视窗教程大集合(看这篇就够了) JAVA日志的前世今生 .NET MVC采用SignalR更新在线用户数 C#多线程编程系列(五)- 使用任务并行库 C#多线程编程系列(三)- 线程同步 C#多线程编程系列(二)- 线程基础 C#多线程编程系列(一)- 简介
Python GUI之tkinter窗口视窗教程大集合(看这篇就够了) 一.前言 由于本篇文章较长,所以下面给出内容目录方便跳转阅读,当然也可以用博客页面最右侧的文章目录导航栏进行跳转查阅. 一.前言 ...
- JVM(五)垃圾回收器的前世今生
全文共 2195 个字,读完大约需要 8 分钟. 如果垃圾回收的算法属于内存回收的方法论的话,那本文讨论的垃圾回收器就属于内存回收的具体实现. 因为不同的厂商(IBM.Oracle),实现的垃圾回收器 ...
- JavaScript的前世今生
和CSS一样,JavaScript在各浏览器下并非完全一致,它所带来的兼容性问题时常困扰着我们,以至于现在“能否处理流行浏览器的兼容性问题”成为了检验一个程序员是否合格的标准之一.了解JavaScri ...
- JavaScript 异步编程的前世今生(上)
前言 提到 JavaScript 异步编程,很多小伙伴都很迷茫,本人花费大约一周的业余时间来对 JS 异步做一个完整的总结,和各位同学共勉共进步! 目录 part1 基础部分 什么是异步 part2 ...
- MVVM模式解析和在WPF中的实现(五)View和ViewModel的通信
MVVM模式解析和在WPF中的实现(五) View和ViewModel的通信 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 M ...
随机推荐
- Windows活动目录_初识
计算机组织形式 工作组(用户本地登录时构造SID进行权限分配): 域(统一身份验证与管理) 域注意事项 实体:域控.域用户.加入域的机子. 依赖的服务:netlogon服务 强制刷新组策略gpupda ...
- 42.JSON Web Token认证
JSON Web Token认证介绍 简称JWT认证,一般用于用户认证 JWT是一种相当新的标准,可用于基于token的身份验证 与内置的TokenAuthentication方案不同,JWT不需要使 ...
- Python 嵌入式打包 (图文)
Python嵌入式打包过程 目录 Python嵌入式打包过程 下载嵌入式包 解压和配置 安装pip和其他依赖 启动项目 python嵌入式打包:将python环境与项目代码打包到同一个文件夹中,在其他 ...
- docker常用配置以及命令
1. Docker基本概念 1.1 什么是 docker hub DockHub是一个仓库 https://hub.docker.com/ 仓库是集中存放镜像文件的场所 仓库分为公开仓库(Public ...
- LoadRunner11使用代理录制脚本
一.背景 电脑安装了LoadRunner11,在进行脚本录制时发现录制的脚本为空,即录制时事件为0,也没有自动调出对应的浏览器:如下图: 问了度娘,发现LR11要成功录制脚本,对各浏览器的版本有要求! ...
- Electron是什么以及可以做什么
新用户购买<Electron + Vue 3 桌面应用开发>,加小册专属微信群,参与群抽奖,送<深入浅出Electron>.<Electron实战>作者签名版. 1 ...
- VO层
springboot中vo的作用: VO:View Object,视图层,其作用是将指定页面的展示数据封装起来,通常用于业务层之间的数据传递. 一般将vo层放置在model层下 类似于将前端页面传输的 ...
- 【lwip】11-UDP协议&源码分析
目录 前言 11.1 传输层说明 11.2 UDP协议简介 11.3 UDP特点 11.4 UDP端口号 11.5 UDP报文 11.6 UDP伪首部和校验和 11.7 wireshark报文分析 1 ...
- C++ 中指针常量、指向常量的指针、引用类型的常量
命题1. 在C++ 中 const T a 与 T const a 是一样的, 表示a是一个T类型的常量. 测试: 一. 形参定义为引用类型的常量 在函数传参时,形参若定义为 const T& ...
- c#使用Bitmap绘图的时候,内存增大问题
最近碰到一个问题,就是使用Biamap绘图的时候,为了防止闪烁,使用了双缓存绘制的方式,但是会碰到内存急剧增加的情况,而且在XP的工控机和Win10的机器上运行结果不一样,在Win10 上运行的时候, ...