如何如何高效完成任务务的学问 kindle

《世界管理学圣经大合集共12册(现代管理学之父德鲁克,日本经营之圣稻盛和夫,“定位”之父里斯、特劳特,全球排名第一的管理咨询大师拉姆·查兰经典著作合集,启蒙中国现代管理经典合集!终生受用的案头必读书!)》 (日)稻盛和夫, (美)彼得.德鲁克, (美)艾.里斯(Al Ries), 杰克.特劳特, (美)拉里.博西迪(Larry Bossidy), 拉姆.查兰 书评 简介 电子书下载 Kindle电子书
iPhone/iPad/Mac
Android手机或平板电脑
请输入您的手机号码,获取Kindle阅读软件的下载链接。
电子书定价:
Kindle电子书价格:
这些促销将适用于该商品:
部分促销可以同时享受;部分促销不可与其他促销同时享受。更多详情请查看促销条款。
发送至您的Kindle设备或Kindle阅读软件
发送至您的Kindle设备或Kindle阅读软件
加入心愿单
无法加入到心愿单。请重试。
世界管理学圣经大合集ͬ(现代管理学之父德鲁克,日本经营之圣稻盛和夫,&定位&之父里斯、特劳特,全球排名第一的管理咨询大师拉姆&查兰经典著作合集,启蒙中国现代管理经典合集!终生受用的案头必读书!)
Kindle电子书
广告
亚马逊价格
全新品最低价
非全新品最低价
Kindle电子书
内容包&#x62:《管理的实践(&#x73&#x85版)》、《卓有成效的管理者(&#x73&#x85版)》、《公司的概念(&#x73&#x85版)》、《定位——有史以来对美&#x56营销影&#x54最大的观念》、《商战》、《22条商规(平装版)》、《干法》、《领&#x5者的资质》、《调动员工&#x79极性的七个关键:稻&#x76和夫&#x7营&#x95答》、《CEO说:&#x50企业家一样思考》、《执行:如何完成&#x4务的学&#x95》、《领&#x5&#x68队:全面打造领&#x5力驱动型公司》
更先进的排版模式:
由于文件较大,下载时间可能较长。
第 1 页, 共 1 页
这项购物功能将继续加载商品。要导航到此轮盘之外,请使用您的标题快捷键,导航到之前或之后的标题。
(日)梅原猛、(日 …
Kindle电子书¥62.86
(日)稻盛和夫 著
Kindle电子书¥18.00
(日)稻盛和夫 著
Kindle电子书¥23.40
…Kindle电子书¥129.81
[美] 奇普·基德Kindle电子书¥123.30
(日)森田直行 著
Kindle电子书¥18.00
发售日期: 日
内容包&#x62:《管理的实践(&#x73&#x85版)》、《卓有成效的管理者(&#x73&#x85版)》、《公司的概念(&#x73&#x85版)》、《定位——有史以来对美&#x56营销影&#x54最大的观念》、《商战》、《22条商规(平装版)》、《干法》、《领&#x5者的资质》、《调动员工&#x79极性的七个关键:稻&#x76和夫&#x7营&#x95答》、《CEO说:&#x50企业家一样思考》、《执行:如何完成&#x4务的学&#x95》、《领&#x5&#x68队:全面打造领&#x5力驱动型公司》
内容包&#x62:《管理的实践(&#x73&#x85版)》、《卓有成效的管理者(&#x73&#x85版)》、《公司的概念(&#x73&#x85版)》、《定位——有史以来对美&#x56营销影&#x54最大的观念》、《商战》、《22条商规(平装版)》、《干法》、《领&#x5者的资质》、《调动员工&#x79极性的七个关键:稻&#x76和夫&#x7营&#x95答》、《CEO说:&#x50企业家一样思考》、《执行:如何完成&#x4务的学&#x95》、《领&#x5&#x68队:全面打造领&#x5力驱动型公司》
版本: Kindle电子书
文件大小: 39623 KB
纸书页数: 3672
出版社: 机械工业出版社; 第1版 (日)
语种: 简体中文
ASIN: B01ASZKD5S
品牌: 北京华章图文信息有限公司
生词提示功能: 未启用
亚马逊热销商品排名:
Kindle商店商品里排第51,325名 ()
看过此商品后顾客买的其它商品?
Kindle电子书
(日)稻盛和夫
Kindle电子书
(日)稻盛和夫 著
Kindle电子书
(日)稻盛和夫
Kindle电子书
04 星03 星02 星01 星0与其他买家分享您的想法
查找其它相似商品
<iframe name="iFrameHMD" id="iFrameHMD" height="0" width="0" style="border:none" src="javascript:''">
如果您需要联系客户服务获取帮助或解答问题,。
您是否想要报告这本书的质量或排版问题?
您认为该书有不恰当内容吗?
您认为这个商品侵犯了版权吗?
查看或变更您的订单。
查看我们的
一件商品 &#40;here&#39;s 我们的 &#41;.
查看产品详情页面完毕后,在此处了解返回您感兴趣的页面的方式。
查看产品详情页面完毕后,在此处了解返回您感兴趣的页面的方式。张鹤瑶 - Kindle本季特价书 - 亚马逊
非常抱歉,没有找到和您搜索内容匹配的内容。&img src=&/v2-07b23f69ccceba1e355817_b.png& data-rawwidth=&926& data-rawheight=&769& class=&origin_image zh-lightbox-thumb& width=&926& data-original=&/v2-07b23f69ccceba1e355817_r.png&&最近逛B站,无意发现了一个视频合集,叫《微积分的本质》,想着我微积分一直学的不是很好,就点进去看了看。&p&于是我发现了新大陆。&/p&&p&视频用最简单粗暴的几何来展示微积分的意义。虽然我学的时候老师也尝试用几何来解释,但当时并不能完全理解。直到我看到了这些视频。&/p&&p&我还是第一次有这么强烈的愿望想要把好东西分享出去。因为这个系列的视频真的是太赞了。&/p&&p&原视频是发布在youtube上的,制作者叫3Blue1Brown,B站的翻译了一下,有中文字幕。因为《微积分》系列还没有翻译完,我没看过瘾,又找了另一个集合《线性代数的本质》。&/p&&p&顿时泪流满面。简直打开新世界大门啊!!!!&/p&&p&我知道知乎上有好多的学生,初中高中本科读研的都有。这两个系列真的是良心啊!而且简明扼要。唉,我要是早些年看了这个视频,感觉能少奋斗好几年啊。&/p&&p&哦话不多说先上链接。&/p&&p&&a href=&/?target=http%3A///%21/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&哔哩哔哩 ( ゜- ゜)つロ 乾杯~ Bilibili&i class=&icon-external&&&/i&&/a& 这个是UP主首页。&br&&/p&&p&&a href=&/?target=http%3A///video/av/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&【官方双语】微积分的本质 - 01 -&i class=&icon-external&&&/i&&/a&这是微积分的第一集。&br&&/p&&p&&a href=&/?target=http%3A///video/av6731067/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&【官方双语/合集】线性代数的本质 - 系列合集&i class=&icon-external&&&/i&&/a& 这是线性代数的合集。&br&&/p&&p&当然还有其他关于数学的视频。那些东西我还没看。但是以上两个集合,真的是感动到哭。&/p&&br&&p&喜欢学习的同学别墨迹了,赶紧去看吧,看完你一定会回来给我点赞的。&/p&&br&&p&对了,补充一下。&/p&&p&&b&&u&这些视频并不能或者说不适合当教材,但能很好的帮助理解。&/u&&/b&视频一集大概也就20分钟,但是内容可能是你高中一个月课程的浓缩。如果你想着在家看看视频就上课就能睡觉了那是不可能的。我觉得这视频更适合本科生一些。比如我这样的渣渣,线性代数和微积分学的一知半解,看完视频简直让我有质的飞升。&/p&&p&再强调一遍,&u&这些视频能很好的帮助你理解数学,但并不能作为教材。&/u&&/p&&br&&p&————分———割———线————————————————————————&/p&&p&更新一下。&/p&&br&&p&几天这么多赞我真的说吓到了。&/p&&p&不过也说明这个系列视频很赞对吧。大家都很好学嘛。&/p&&p&评论区好多同学问有没有概率论的视频。额我没看过这个诶所以也就不知道了。好多人推荐可汗学院,有兴趣的可以自己去看看。嗯我有空也去看看。&/p&&p&另外好多人问b站视频能不能下载。我也是搜了一下才发现有个网站叫kanbilibili哈哈哈哈专门提供b站下载。只要在b站找到你想下载的视频,再在网址栏里的bilibili前面加上kan然后猛敲回车就好啦。&/p&
最近逛B站,无意发现了一个视频合集,叫《微积分的本质》,想着我微积分一直学的不是很好,就点进去看了看。于是我发现了新大陆。视频用最简单粗暴的几何来展示微积分的意义。虽然我学的时候老师也尝试用几何来解释,但当时并不能完全理解。直到我看到了这…
&img src=&/v2-82a5a924d6_b.jpg& data-rawwidth=&1500& data-rawheight=&1000& class=&origin_image zh-lightbox-thumb& width=&1500& data-original=&/v2-82a5a924d6_r.jpg&&&blockquote&首发于《程序员》杂志五月刊&/blockquote&&h2&一、前言&/h2&&p&万维网发明人 Tim Berners-Lee 谈到设计原理时说过:“简单性和模块化是软件工程的基石;分布式和容错性是互联网的生命。” 由此可见模块化之于软件工程领域的重要性。&/p&&p&从 2016 年开始,模块化在 Android 社区越来越多的被提及。随着移动平台的不断发展,移动平台上的软件慢慢走向复杂化,体积也变得臃肿庞大;为了降低大型软件复杂性和耦合度,同时也为了适应模块重用、多团队并行开发测试等等需求,模块化在 Android 平台上变得势在必行。阿里 Android 团队在年初开源了他们的容器化框架 Atlas 就很大程度说明了当前 Android 平台开发大型商业项目所面临的问题。&/p&&h2&二、什么是模块化&/h2&&p&那么什么是模块化呢?《 Java 应用架构设计:模块化模式与 OSGi 》一书中对它的定义是:模块化是一种处理复杂系统分解为更好的可管理模块的方式。&/p&&p&上面这种描述太过生涩难懂,不够直观。下面这种类比的方式则可能加容易理解。&/p&&p&我们可以把软件看做是一辆汽车,开发一款软件的过程就是生产一辆汽车的过程。一辆汽车由车架、发动机、变数箱、车轮等一系列模块组成;同样,一款大型商业软件也是由各个不同的模块组成的。&/p&&p&汽车的这些模块是由不同的工厂生产的,一辆 BMW 的发动机可能是由位于德国的工厂生产的,它的自动变数箱可能是 Jatco(世界三大变速箱厂商之一)位于日本的工厂生产的,车轮可能是中国的工厂生产的,最后交给华晨宝马的工厂统一组装成一辆完整的汽车。这就类似于我们在软件工程领域里说的多团队并行开发,最后将各个团队开发的模块统一打包成我们可使用的 App 。&/p&&p&一款发动机、一款变数箱都不可能只应用于一个车型,比如同一款 Jatco 的 6AT 自动变速箱既可能被安装在 BMW 的车型上,也可能被安装在 Mazda 的车型上。这就如同软件开发领域里的模块重用。&/p&&p&到了冬天,特别是在北方我们可能需要开着车走雪路,为了安全起见往往我们会将汽车的公路胎升级为雪地胎;轮胎可以很轻易的更换,这就是我们在软件开发领域谈到的低耦合。一个模块的升级替换不会影响到其它模块,也不会受其它模块的限制;同时这也类似于我们在软件开发领域提到的可插拔。&/p&&h2&三、模块化分层设计&/h2&&p&上面的类比很清晰的说明的模块化带来的好处:&/p&&ul&&li&多团队并行开发测试;&/li&&li&模块间解耦、重用;&/li&&li&可单独编译打包某一模块,提升开发效率。&/li&&/ul&&p&在&a href=&/p/& class=&internal&&《安居客 Android 项目架构演进》&/a&这篇文章中,我介绍了安居客 Android 端的模块化设计方案,这里我还是拿它来举例。但首先要对本文中的&strong&组件&/strong&和&strong&模块&/strong&做个区别定义&/p&&ul&&li&&p&&strong&组件&/strong&:指的是单一的功能组件,如地图组件(MapSDK)、支付组件(AnjukePay)、路由组件(Router)等等;&/p&&/li&&li&&p&&strong&模块&/strong&:指的是独立的业务模块,如新房模块(NewHouseModule)、二手房模块(SecondHouseModule)、即时通讯模块(InstantMessagingModule)等等;模块相对于组件来说粒度更大。&/p&&/li&&/ul&&p&具体设计方案如下图:&/p&&p&&img src=&/v2-faadc3b573ca874a722e3780_b.png& data-rawwidth=&2222& data-rawheight=&1226& class=&origin_image zh-lightbox-thumb& width=&2222& data-original=&/v2-faadc3b573ca874a722e3780_r.png&&?整个项目分为三层,从下至上分别是:&/p&&ul&&li&Basic Component Layer: 基础组件层,顾名思义就是一些基础组件,包含了各种开源库以及和业务无关的各种自研工具库;&/li&&li&Business Component Layer: 业务组件层,这一层的所有组件都是业务相关的,例如上图中的支付组件 AnjukePay、数据模拟组件 DataSimulator 等等;&/li&&li&Business Module Layer: 业务 Module 层,在 Android Studio 中每块业务对应一个单独的 Module。例如安居客用户 App 我们就可以拆分成新房 Module、二手房 Module、IM Module 等等,每个单独的 Business Module 都必须准遵守我们自己的 MVP 架构。&/li&&/ul&&p&我们在谈模块化的时候,其实就是将业务模块层的各个功能业务拆分层独立的业务模块。所以我们进行模块化的第一步就是业务模块划分,但是模块划分并没有一个业界通用的标准,因此划分的粒度需要根据项目情况进行合理把控,这就需要对业务和项目有较为透彻的理解。拿安居客来举例,我们会将项目划分为新房模块、二手房模块、IM 模块等等。&/p&&p&每个业务模块在 Android Studio 中的都是一个 Module ,因此在命名方面我们要求每个业务模块都以 Module 为后缀。如下图所示:&/p&&br&&img src=&/v2-1ff6ebb7a4de657abfa467b_b.png& data-rawwidth=&540& data-rawheight=&412& class=&origin_image zh-lightbox-thumb& width=&540& data-original=&/v2-1ff6ebb7a4de657abfa467b_r.png&&&br&&p&对于模块化项目,每个单独的 Business Module 都可以单独编译成 APK。在开发阶段需要单独打包编译,项目发布的时候又需要它作为项目的一个 Module 来整体编译打包。简单的说就是开发时是 Application,发布时是 Library。因此需要在 Business Module 的 build.gradle 中加入如下代码:&/p&&div class=&highlight&&&pre&&code class=&language-groovy&&&span&&/span&&span class=&k&&if&/span&&span class=&o&&(&/span&&span class=&n&&isBuildModule&/span&&span class=&o&&.&/span&&span class=&na&&toBoolean&/span&&span class=&o&&()){&/span&
&span class=&n&&apply&/span& &span class=&nl&&plugin:&/span& &span class=&s1&&'com.android.application'&/span&
&span class=&o&&}&/span&&span class=&k&&else&/span&&span class=&o&&{&/span&
&span class=&n&&apply&/span& &span class=&nl&&plugin:&/span& &span class=&s1&&'com.android.library'&/span&
&span class=&o&&}&/span&
&/code&&/pre&&/div&&blockquote&&p&isBuildModule 在项目根目录的 gradle.properties 中定义:&/p&&div class=&highlight&&&pre&&code class=&language-groovy&&&span&&/span&&span class=&n&&isBuildModule&/span&&span class=&o&&=&/span&&span class=&kc&&false&/span&
&/code&&/pre&&/div&&/blockquote&&p&同样 Manifest.xml 也需要有两套:&/p&&div class=&highlight&&&pre&&code class=&language-groovy&&&span&&/span&&span class=&n&&sourceSets&/span& &span class=&o&&{&/span&
&span class=&n&&main&/span& &span class=&o&&{&/span&
&span class=&k&&if&/span& &span class=&o&&(&/span&&span class=&n&&isBuildModule&/span&&span class=&o&&.&/span&&span class=&na&&toBoolean&/span&&span class=&o&&())&/span& &span class=&o&&{&/span&
&span class=&n&&manifest&/span&&span class=&o&&.&/span&&span class=&na&&srcFile&/span& &span class=&s1&&'src/main/debug/AndroidManifest.xml'&/span&
&span class=&o&&}&/span& &span class=&k&&else&/span& &span class=&o&&{&/span&
&span class=&n&&manifest&/span&&span class=&o&&.&/span&&span class=&na&&srcFile&/span& &span class=&s1&&'src/main/release/AndroidManifest.xml'&/span&
&span class=&o&&}&/span&
&span class=&o&&}&/span&
&span class=&o&&}&/span&
&/code&&/pre&&/div&&p&如图:&/p&&br&&img src=&/v2-5a927c1f4ba210b074c3b977defe7b8b_b.png& data-rawwidth=&610& data-rawheight=&234& class=&origin_image zh-lightbox-thumb& width=&610& data-original=&/v2-5a927c1f4ba210b074c3b977defe7b8b_r.png&&&p&debug 模式下的 AndroidManifest.xml :&/p&&div class=&highlight&&&pre&&code class=&language-xml&&&span&&/span&&span class=&nt&&&application&/span&
&span class=&err&&...&/span&
&span class=&nt&&&&/span&
&span class=&nt&&&activity&/span&
&span class=&na&&android:name=&/span&&span class=&s&&&com.baronzhang.android.newhouse.NewHouseMainActivity&&/span&
&span class=&na&&android:label=&/span&&span class=&s&&&@string/new_house_label_home_page&&/span&&span class=&nt&&&&/span&
&span class=&nt&&&intent-filter&&/span&
&span class=&nt&&&action&/span& &span class=&na&&android:name=&/span&&span class=&s&&&android.intent.action.MAIN&&/span& &span class=&nt&&/&&/span&
&span class=&nt&&&category&/span& &span class=&na&&android:name=&/span&&span class=&s&&&android.intent.category.LAUNCHER&&/span& &span class=&nt&&/&&/span&
&span class=&nt&&&/intent-filter&&/span&
&span class=&nt&&&/activity&&/span&
&span class=&nt&&&/application&&/span&
&/code&&/pre&&/div&&p&realease 模式下的 AndroidManifest.xml :&/p&&div class=&highlight&&&pre&&code class=&language-xml&&&span&&/span&&span class=&nt&&&application&/span&
&span class=&err&&...&/span&
&span class=&nt&&&&/span&
&span class=&nt&&&activity&/span&
&span class=&na&&android:name=&/span&&span class=&s&&&com.baronzhang.android.newhouse.NewHouseMainActivity&&/span&
&span class=&na&&android:label=&/span&&span class=&s&&&@string/new_house_label_home_page&&/span&&span class=&nt&&&&/span&
&span class=&nt&&&intent-filter&&/span&
&span class=&nt&&&category&/span& &span class=&na&&android:name=&/span&&span class=&s&&&android.intent.category.DEFAULT&&/span& &span class=&nt&&/&&/span&
&span class=&nt&&&category&/span& &span class=&na&&android:name=&/span&&span class=&s&&&android.intent.category.BROWSABLE&&/span& &span class=&nt&&/&&/span&
&span class=&nt&&&action&/span& &span class=&na&&android:name=&/span&&span class=&s&&&android.intent.action.VIEW&&/span& &span class=&nt&&/&&/span&
&span class=&nt&&&data&/span& &span class=&na&&android:host=&/span&&span class=&s&&&com.baronzhang.android.newhouse&&/span&
&span class=&na&&android:scheme=&/span&&span class=&s&&&router&&/span& &span class=&nt&&/&&/span&
&span class=&nt&&&/intent-filter&&/span&
&span class=&nt&&&/activity&&/span&
&span class=&nt&&&/application&&/span&
&/code&&/pre&&/div&&p&同时针对模块化我们也定义了一些自己的游戏规则:&/p&&ul&&li&对于 Business Module Layer,各业务模块之间不允许存在相互依赖关系,它们之间的跳转通讯采用路由框架 Router 来实现(后面会介绍 Router 框架的实现);&/li&&li&对于 Business Component Layer,单一业务组件只能对应某一项具体的业务,个性化需求对外部提供接口让调用方定制;&/li&&li&合理控制各组件和各业务模块的拆分粒度,太小的公有模块不足以构成单独组件或者模块的,我们先放到类似于 CommonBusiness 的组件中,在后期不断的重构迭代中视情况进行进一步的拆分;&/li&&li&上层的公有业务或者功能模块可以逐步下放到下层,合理把握好度就好;&/li&&li&各 Layer 间严禁反向依赖,横向依赖关系由各业务 Leader 和技术小组商讨决定。&/li&&/ul&&h2&四、模块间跳转通讯(Router)&/h2&&p&对业务进行模块化拆分后,为了使各业务模块间解耦,因此各个 Bussiness Module 都是独立的模块,它们之间是没有依赖关系。那么各个模块间的跳转通讯如何实现呢?&/p&&p&比如业务上要求从&strong&新房的列表页&/strong&跳转到&strong&二手房的列表页&/strong&,那么由于是 NewHouseModule 和 SecondHouseModule 之间并不相互依赖,我们通过想如下这种显式跳转的方式来实现 Activity 跳转显然是不可能的实现的。&/p&&div class=&highlight&&&pre&&code class=&language-java&&&span&&/span&&span class=&n&&Intent&/span& &span class=&n&&intent&/span& &span class=&o&&=&/span& &span class=&k&&new&/span& &span class=&n&&Intent&/span&&span class=&o&&(&/span&&span class=&n&&NewHouseListActivity&/span&&span class=&o&&.&/span&&span class=&na&&this&/span&&span class=&o&&,&/span& &span class=&n&&SecondHouseListActivity&/span&&span class=&o&&.&/span&&span class=&na&&class&/span&&span class=&o&&);&/span&
&span class=&n&&startActivity&/span&&span class=&o&&(&/span&&span class=&n&&intent&/span&&span class=&o&&);&/span&
&/code&&/pre&&/div&&p&有的同学可能会想到用隐式跳转,通过 Intent 匹配规则来实现:&/p&&div class=&highlight&&&pre&&code class=&language-java&&&span&&/span&&span class=&n&&Intent&/span& &span class=&n&&intent&/span& &span class=&o&&=&/span& &span class=&k&&new&/span& &span class=&n&&Intent&/span&&span class=&o&&(&/span&&span class=&n&&Intent&/span&&span class=&o&&.&/span&&span class=&na&&ACTION_VIEW&/span&&span class=&o&&,&/span& &span class=&s&&&&scheme&://&host&:&port&/&path&&&/span&&span class=&o&&);&/span&
&span class=&n&&startActivity&/span&&span class=&o&&(&/span&&span class=&n&&intent&/span&&span class=&o&&);&/span&
&/code&&/pre&&/div&&p&但是这种代码写起来比较繁琐,且容易出错,出错也不太容易定位问题。因此一个简单易用、解放开发的路由框架是必须的了。&/p&&br&&p&&img src=&/v2-bbee890f13e574acf283c305_b.png& data-rawwidth=&1710& data-rawheight=&1110& class=&origin_image zh-lightbox-thumb& width=&1710& data-original=&/v2-bbee890f13e574acf283c305_r.png&&我自己实现的路由框架分为&strong&路由(Router)&/strong&和&strong&参数注入器(Injector)&/strong&两部分:&/p&&img src=&/v2-39b94f5f1f623ad619e430c0f5bec2ab_b.png& data-rawwidth=&2160& data-rawheight=&792& class=&origin_image zh-lightbox-thumb& width=&2160& data-original=&/v2-39b94f5f1f623ad619e430c0f5bec2ab_r.png&&&br&&blockquote&&p&Router 提供 Activity 跳转传参的功能;Injector 提供参数注入功能,通过编译时生成代码的方式在 Activity 获取获取传递过来的参数,简化开发。&/p&&/blockquote&&h3&4.1 Router&/h3&&p&路由(Router)部分通过 Java 注解结合动态代理来实现,这一点和 Retrofit 的实现原理是一样的。&/p&&p&首先需要定义我们自己的注解(篇幅有限,这里只列出少部分源码)。&/p&&p&用于定义跳转 URI 的注解 FullUri:&/p&&div class=&highlight&&&pre&&code class=&language-java&&&span&&/span&&span class=&nd&&@Target&/span&&span class=&o&&(&/span&&span class=&n&&ElementType&/span&&span class=&o&&.&/span&&span class=&na&&METHOD&/span&&span class=&o&&)&/span&
&span class=&nd&&@Retention&/span&&span class=&o&&(&/span&&span class=&n&&RetentionPolicy&/span&&span class=&o&&.&/span&&span class=&na&&RUNTIME&/span&&span class=&o&&)&/span&
&span class=&kd&&public&/span& &span class=&nd&&@interface&/span& &span class=&n&&FullUri&/span& &span class=&o&&{&/span&
&span class=&n&&String&/span& &span class=&nf&&value&/span&&span class=&o&&();&/span&
&span class=&o&&}&/span&
&/code&&/pre&&/div&&p&用于定义跳转传参的 UriParam( UriParam 注解的参数用于拼接到 URI 后面):&/p&&div class=&highlight&&&pre&&code class=&language-java&&&span&&/span&&span class=&nd&&@Target&/span&&span class=&o&&(&/span&&span class=&n&&ElementType&/span&&span class=&o&&.&/span&&span class=&na&&PARAMETER&/span&&span class=&o&&)&/span&
&span class=&nd&&@Retention&/span&&span class=&o&&(&/span&&span class=&n&&RetentionPolicy&/span&&span class=&o&&.&/span&&span class=&na&&RUNTIME&/span&&span class=&o&&)&/span&
&span class=&kd&&public&/span& &span class=&nd&&@interface&/span& &span class=&n&&UriParam&/span& &span class=&o&&{&/span&
&span class=&n&&String&/span& &span class=&nf&&value&/span&&span class=&o&&();&/span&
&span class=&o&&}&/span&
&/code&&/pre&&/div&&p&用于定义跳转传参的 IntentExtrasParam( IntentExtrasParam 注解的参数最终通过 Intent 来传递):&/p&&div class=&highlight&&&pre&&code class=&language-java&&&span&&/span&&span class=&nd&&@Target&/span&&span class=&o&&(&/span&&span class=&n&&ElementType&/span&&span class=&o&&.&/span&&span class=&na&&PARAMETER&/span&&span class=&o&&)&/span&
&span class=&nd&&@Retention&/span&&span class=&o&&(&/span&&span class=&n&&RetentionPolicy&/span&&span class=&o&&.&/span&&span class=&na&&RUNTIME&/span&&span class=&o&&)&/span&
&span class=&kd&&public&/span& &span class=&nd&&@interface&/span& &span class=&n&&IntentExtrasParam&/span& &span class=&o&&{&/span&
&span class=&n&&String&/span& &span class=&nf&&value&/span&&span class=&o&&();&/span&
&span class=&o&&}&/span&
&/code&&/pre&&/div&&p&然后实现 Router ,内部通过动态代理的方式来实现 Activity 跳转:&/p&&div class=&highlight&&&pre&&code class=&language-java&&&span&&/span&&span class=&kd&&public&/span& &span class=&kd&&final&/span& &span class=&kd&&class&/span& &span class=&nc&&Router&/span& &span class=&o&&{&/span&
&span class=&o&&...&/span&
&span class=&kd&&public&/span& &span class=&o&&&&/span&&span class=&n&&T&/span&&span class=&o&&&&/span& &span class=&n&&T&/span& &span class=&nf&&create&/span&&span class=&o&&(&/span&&span class=&kd&&final&/span& &span class=&n&&Class&/span&&span class=&o&&&&/span&&span class=&n&&T&/span&&span class=&o&&&&/span& &span class=&n&&service&/span&&span class=&o&&)&/span& &span class=&o&&{&/span&
&span class=&k&&return&/span& &span class=&o&&(&/span&&span class=&n&&T&/span&&span class=&o&&)&/span& &span class=&n&&Proxy&/span&&span class=&o&&.&/span&&span class=&na&&newProxyInstance&/span&&span class=&o&&(&/span&&span class=&n&&service&/span&&span class=&o&&.&/span&&span class=&na&&getClassLoader&/span&&span class=&o&&(),&/span& &span class=&k&&new&/span& &span class=&n&&Class&/span&&span class=&o&&[]{&/span&&span class=&n&&service&/span&&span class=&o&&},&/span& &span class=&k&&new&/span& &span class=&n&&InvocationHandler&/span&&span class=&o&&()&/span& &span class=&o&&{&/span&
&span class=&nd&&@Override&/span&
&span class=&kd&&public&/span& &span class=&n&&Object&/span& &span class=&nf&&invoke&/span&&span class=&o&&(&/span&&span class=&n&&Object&/span& &span class=&n&&proxy&/span&&span class=&o&&,&/span& &span class=&n&&Method&/span& &span class=&n&&method&/span&&span class=&o&&,&/span& &span class=&n&&Object&/span&&span class=&o&&[]&/span& &span class=&n&&args&/span&&span class=&o&&)&/span& &span class=&kd&&throws&/span& &span class=&n&&Throwable&/span& &span class=&o&&{&/span&
&span class=&n&&FullUri&/span& &span class=&n&&fullUri&/span& &span class=&o&&=&/span& &span class=&n&&method&/span&&span class=&o&&.&/span&&span class=&na&&getAnnotation&/span&&span class=&o&&(&/span&&span class=&n&&FullUri&/span&&span class=&o&&.&/span&&span class=&na&&class&/span&&span class=&o&&);&/span&
&span class=&n&&StringBuilder&/span& &span class=&n&&urlBuilder&/span& &span class=&o&&=&/span& &span class=&k&&new&/span& &span class=&n&&StringBuilder&/span&&span class=&o&&();&/span&
&span class=&n&&urlBuilder&/span&&span class=&o&&.&/span&&span class=&na&&append&/span&&span class=&o&&(&/span&&span class=&n&&fullUri&/span&&span class=&o&&.&/span&&span class=&na&&value&/span&&span class=&o&&());&/span&
&span class=&c1&&//获取注解参数&/span&
&span class=&n&&Annotation&/span&&span class=&o&&[][]&/span& &span class=&n&&parameterAnnotations&/span& &span class=&o&&=&/span& &span class=&n&&method&/span&&span class=&o&&.&/span&&span class=&na&&getParameterAnnotations&/span&&span class=&o&&();&/span&
&span class=&n&&HashMap&/span&&span class=&o&&&&/span&&span class=&n&&String&/span&&span class=&o&&,&/span& &span class=&n&&Object&/span&&span class=&o&&&&/span& &span class=&n&&serializedParams&/span& &span class=&o&&=&/span& &span class=&k&&new&/span& &span class=&n&&HashMap&/span&&span class=&o&&&&();&/span&
&span class=&c1&&//拼接跳转 URI&/span&
&span class=&kt&&int&/span& &span class=&n&&position&/span& &span class=&o&&=&/span& &span class=&mi&&0&/span&&span class=&o&&;&/span&
&span class=&k&&for&/span& &span class=&o&&(&/span&&span class=&kt&&int&/span& &span class=&n&&i&/span& &span class=&o&&=&/span& &span class=&mi&&0&/span&&span class=&o&&;&/span& &span class=&n&&i&/span& &span class=&o&&&&/span& &span class=&n&&parameterAnnotations&/span&&span class=&o&&.&/span&&span class=&na&&length&/span&&span class=&o&&;&/span& &span class=&n&&i&/span&&span class=&o&&++)&/span& &span class=&o&&{&/span&
&span class=&n&&Annotation&/span&&span class=&o&&[]&/span& &span class=&n&&annotations&/span& &span class=&o&&=&/span& &span class=&n&&parameterAnnotations&/span&&span class=&o&&[&/span&&span class=&n&&i&/span&&span class=&o&&];&/span&
&span class=&k&&if&/span& &span class=&o&&(&/span&&span class=&n&&annotations&/span& &span class=&o&&==&/span& &span class=&kc&&null&/span& &span class=&o&&||&/span& &span class=&n&&annotations&/span&&span class=&o&&.&/span&&span class=&na&&length&/span& &span class=&o&&==&/span& &span class=&mi&&0&/span&&span class=&o&&)&/span&
&span class=&k&&break&/span&&span class=&o&&;&/span&
&span class=&n&&Annotation&/span& &span class=&n&&annotation&/span& &span class=&o&&=&/span& &span class=&n&&annotations&/span&&span class=&o&&[&/span&&span class=&mi&&0&/span&&span class=&o&&];&/span&
&span class=&k&&if&/span& &span class=&o&&(&/span&&span class=&n&&annotation&/span& &span class=&k&&instanceof&/span& &span class=&n&&UriParam&/span&&span class=&o&&)&/span& &span class=&o&&{&/span&
&span class=&c1&&//拼接 URI 后的参数&/span&
&span class=&o&&...&/span&
&span class=&o&&}&/span& &span class=&k&&else&/span& &span class=&k&&if&/span& &span class=&o&&(&/span&&span class=&n&&annotation&/span& &span class=&k&&instanceof&/span& &span class=&n&&IntentExtrasParam&/span&&span class=&o&&)&/span& &span class=&o&&{&/span&
&span class=&c1&&//Intent 传参处理&/span&
&span class=&o&&...&/span&
&span class=&o&&}&/span&
&span class=&o&&}&/span&
&span class=&c1&&//执行Activity跳转操作&/span&
&span class=&n&&performJump&/span&&span class=&o&&(&/span&&span class=&n&&urlBuilder&/span&&span class=&o&&.&/span&&span class=&na&&toString&/span&&span class=&o&&(),&/span& &span class=&n&&serializedParams&/span&&span class=&o&&);&/span&
&span class=&k&&return&/span& &span class=&kc&&null&/span&&span class=&o&&;&/span&
&span class=&o&&}&/span&
&span class=&o&&});&/span&
&span class=&o&&}&/span&
&span class=&o&&...&/span&
&span class=&o&&}&/span&
&/code&&/pre&&/div&&p&上面是 Router 实现的部分代码,在使用 Router 来跳转的时候,首先需要定义一个 Interface(类似于 Retrofit 的使用方式):&/p&&div class=&highlight&&&pre&&code class=&language-java&&&span&&/span&&span class=&kd&&public&/span& &span class=&kd&&interface&/span& &span class=&nc&&RouterService&/span& &span class=&o&&{&/span&
&span class=&nd&&@FullUri&/span&&span class=&o&&(&/span&&span class=&s&&&router://com.baronzhang.android.router.FourthActivity&&/span&&span class=&o&&)&/span&
&span class=&kt&&void&/span& &span class=&nf&&startUserActivity&/span&&span class=&o&&(&/span&&span class=&nd&&@UriParam&/span&&span class=&o&&(&/span&&span class=&s&&&cityName&&/span&&span class=&o&&)&/span&
&span class=&n&&String&/span& &span class=&n&&cityName&/span&&span class=&o&&,&/span& &span class=&nd&&@IntentExtrasParam&/span&&span class=&o&&(&/span&&span class=&s&&&user&&/span&&span class=&o&&)&/span& &span class=&n&&User&/span& &span class=&n&&user&/span&&span class=&o&&);&/span&
&span class=&o&&}&/span&
&/code&&/pre&&/div&&p&接下来我们就可以通过如下方式实现 Activity 的跳转传参了:&/p&&div class=&highlight&&&pre&&code class=&language-java&&&span&&/span&&span class=&n&&RouterService&/span& &span class=&n&&routerService&/span& &span class=&o&&=&/span& &span class=&k&&new&/span& &span class=&n&&Router&/span&&span class=&o&&(&/span&&span class=&k&&this&/span&&span class=&o&&).&/span&&span class=&na&&create&/span&&span class=&o&&(&/span&&span class=&n&&RouterService&/span&&span class=&o&&.&/span&&span class=&na&&class&/span&&span class=&o&&);&/span&
&span class=&n&&User&/span& &span class=&n&&user&/span& &span class=&o&&=&/span& &span class=&k&&new&/span& &span class=&n&&User&/span&&span class=&o&&(&/span&&span class=&s&&&张三&&/span&&span class=&o&&,&/span& &span class=&mi&&17&/span&&span class=&o&&,&/span& &span class=&mi&&165&/span&&span class=&o&&,&/span& &span class=&mi&&88&/span&&span class=&o&&);&/span&
&span class=&n&&routerService&/span&&span class=&o&&.&/span&&span class=&na&&startUserActivity&/span&&span class=&o&&(&/span&&span class=&s&&&上海&&/span&&span class=&o&&,&/span& &span class=&n&&user&/span&&span class=&o&&);&/span&
&/code&&/pre&&/div&&h3&4.2 Injector&/h3&&p&通过 Router 跳转到目标 Activity 后,我们需要在目标 Activity 中获取通过 Intent 传过来的参数:&/p&&div class=&highlight&&&pre&&code class=&language-java&&&span&&/span&&span class=&n&&getIntent&/span&&span class=&o&&().&/span&&span class=&na&&getIntExtra&/span&&span class=&o&&(&/span&&span class=&s&&&intParam&&/span&&span class=&o&&,&/span& &span class=&mi&&0&/span&&span class=&o&&);&/span&
&span class=&n&&getIntent&/span&&span class=&o&&().&/span&&span class=&na&&getData&/span&&span class=&o&&().&/span&&span class=&na&&getQueryParameter&/span&&span class=&o&&(&/span&&span class=&s&&&preActivity&&/span&&span class=&o&&);&/span&
&/code&&/pre&&/div&&p&为了简化这部分工作,路由框架 Router 中提供了 Injector 模块在编译时生成上述代码。参数注入器(Injector)部分通过 Java 编译时注解来实现,实现思路和 ButterKnife 这类编译时注解框架类似。&/p&&p&首先定义我们的参数注解 InjectUriParam :&/p&&div class=&highlight&&&pre&&code class=&language-java&&&span&&/span&&span class=&nd&&@Target&/span&&span class=&o&&(&/span&&span class=&n&&ElementType&/span&&span class=&o&&.&/span&&span class=&na&&FIELD&/span&&span class=&o&&)&/span&
&span class=&nd&&@Retention&/span&&span class=&o&&(&/span&&span class=&n&&RetentionPolicy&/span&&span class=&o&&.&/span&&span class=&na&&CLASS&/span&&span class=&o&&)&/span&
&span class=&kd&&public&/span& &span class=&nd&&@interface&/span& &span class=&n&&InjectUriParam&/span& &span class=&o&&{&/span&
&span class=&n&&String&/span& &span class=&nf&&value&/span&&span class=&o&&()&/span& &span class=&k&&default&/span& &span class=&s&&&&&/span&&span class=&o&&;&/span&
&span class=&o&&}&/span&
&/code&&/pre&&/div&&p&然后实现一个注解处理器 InjectProcessor ,在编译阶段生成获取参数的代码:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&@AutoService(Processor.class)
public class InjectProcessor extends AbstractProcessor {
public boolean process(Set&? extends TypeElement& set, RoundEnvironment roundEnvironment) {
//解析注解
Map&TypeElement, TargetClass& targetClassMap = findAndParseTargets(roundEnvironment);
//解析完成后,生成的代码的结构已经有了,它们存在InjectingClass中
for (Map.Entry&TypeElement, TargetClass& entry : targetClassMap.entrySet()) {
&/code&&/pre&&/div&&p&使用方式类似于 ButterKnife ,在 Activity 中我们使用 Inject 来注解一个全局变量:&/p&&div class=&highlight&&&pre&&code class=&language-java&&&span&&/span&&span class=&nd&&@Inject&/span& &span class=&n&&User&/span& &span class=&n&&user&/span&&span class=&o&&;&/span&
&/code&&/pre&&/div&&p&然后 onCreate 方法中需要调用 inject(Activity activity) 方法实现注入:&/p&&div class=&highlight&&&pre&&code class=&language-java&&&span&&/span&&span class=&n&&RouterInjector&/span&&span class=&o&&.&/span&&span class=&na&&inject&/span&&span class=&o&&(&/span&&span class=&k&&this&/span&&span class=&o&&);&/span&
&/code&&/pre&&/div&&p&这样我们就可以获取到前面通过 Router 跳转的传参了。&/p&&blockquote&&p&由于篇幅限制,加上为了便于理解,这里只贴出了极少部分 &a href=&/?target=https%3A///BaronZ88/Router& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Router&i class=&icon-external&&&/i&&/a& 框架的源码。希望进一步了解 Router 实现原理的可以到 GiuHub 去翻阅源码,&a href=&/?target=https%3A///BaronZ88/Router& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Router&i class=&icon-external&&&/i&&/a& 的实现还比较简陋,后面会进一步完善功能和文档,之后也会有单独的文章详细介绍。源码地址:&a href=&/?target=https%3A///BaronZ88/Router& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&BaronZ88/Router&i class=&icon-external&&&/i&&/a&&/p&&/blockquote&&h2&五、问题及建议&/h2&&h3&5.1 资源名冲突&/h3&&p&对于多个 Bussines Module 中资源名冲突的问题,可以通过在 build.gradle 定义前缀的方式解决:&/p&&div class=&highlight&&&pre&&code class=&language-groovy&&&span&&/span&&span class=&n&&defaultConfig&/span& &span class=&o&&{&/span&
&span class=&o&&...&/span&
&span class=&n&&resourcePrefix&/span& &span class=&s2&&&new_house_&&/span&
&span class=&o&&...&/span&
&span class=&o&&}&/span&
&/code&&/pre&&/div&&p&而对于 Module 中有些资源不想被外部访问的,我们可以创建 res/values/public.xml,添加到 public.xml 中的 resource 则可被外部访问,未添加的则视为私有:&/p&&div class=&highlight&&&pre&&code class=&language-xml&&&span&&/span&&span class=&nt&&&resources&&/span&
&span class=&nt&&&public&/span& &span class=&na&&name=&/span&&span class=&s&&&new_house_settings&&/span& &span class=&na&&type=&/span&&span class=&s&&&string&&/span&&span class=&nt&&/&&/span&
&span class=&nt&&&/resources&&/span&
&/code&&/pre&&/div&&h3&5.2 重复依赖&/h3&&p&模块化的过程中我们常常会遇到重复依赖的问题,如果是通过 aar 依赖, gradle 会自动帮我们找出新版本,而抛弃老版本的重复依赖。如果是以 project 的方式依赖,则在打包的时候会出现重复类。对于这种情况我们可以在 build.gradle 中将 compile 改为 provided,只在最终的项目中 compile 对应的 library ;&/p&&p&其实从前面的安居客模块化设计图上能看出来,我们的设计方案能一定程度上规避重复依赖的问题。比如我们所有的第三方库的依赖都会放到 OpenSoureLibraries 中,其他需要用到相关类库的项目,只需要依赖 OpenSoureLibraries 就好了。&/p&&h3&5.3 模块化过程中的建议&/h3&&p&对于大型的商业项目,在重构过程中可能会遇到业务耦合严重,难以拆分的问题。&strong&我们需要先理清业务,再动手拆分业务模块&/strong&。比如可以先在原先的项目中根据业务分包,在一定程度上将各业务解耦后拆分到不同的 package 中。比如之前新房和二手房由于同属于 app module,因此他们之前是通过隐式的 intent 跳转的,现在可以先将他们改为通过 Router 来实现跳转。又比如新房和二手房中公用的模块可以先下放到 Business Component Layer 或者 Basic Component Layer 中。在这一系列工作完成后再将各个业务拆分成多个 module 。&/p&&p&模块化重构需要渐进式的展开,不可一触而就,不要想着将整个项目推翻重写。线上成熟稳定的业务代码,是经过了时间和大量用户考验的;全部推翻重写往往费时费力,实际的效果通常也很不理想,各种问题层出不穷得不偿失。对于这种项目的模块化重构,我们需要一点点的改进重构,可以分散到每次的业务迭代中去,逐步淘汰掉陈旧的代码。&/p&&p&各业务模块间肯定会有公用的部分,按照我前面的设计图,公用的部分我们会根据业务相关性下放到业务组件层(Business Component Layer)或者基础组件层(Common Component Layer)。对于太小的公有模块不足以构成单独组件或者模块的,我们先放到类似于 CommonBusiness 的组件中,在后期不断的重构迭代中视情况进行进一步的拆分。过程中完美主义可以有,切记不可过度。&/p&&p&以上就是我在模块化探索实践方面的一些经验,不住之处还望大家指出。&/p&&ul&&li&模块化示例项目 &a href=&/?target=https%3A///BaronZ88/ModularizationProject& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&ModularizationProject&i class=&icon-external&&&/i&&/a& 源码地址:&a href=&/?target=https%3A///BaronZ88/ModularizationProject& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&/BaronZ88/Mod&/span&&span class=&invisible&&ularizationProject&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&&/li&&li&路由框架 &a href=&/?target=https%3A///BaronZ88/Router& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Router&i class=&icon-external&&&/i&&/a& 源码地址:&a href=&/?target=https%3A///BaronZ88/Router& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&BaronZ88/Router&i class=&icon-external&&&/i&&/a&&/li&&/ul&&p&参考资料:&/p&&a href=&/?target=http%3A///android/didi-internationalization-android-evolution/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&滴滴国际化项目 Android 端演进&i class=&icon-external&&&/i&&/a&&br&&a href=&/?target=https%3A//www.diycode.cc/topics/362& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Android 业务组件化开发实践&i class=&icon-external&&&/i&&/a&&br&&a href=&/?target=https%3A///MDCC2016/Android-Session-Slides/blob/master/02-From.Containerization.To.Modularity.pdf& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&/MDCC2016/And&/span&&span class=&invisible&&roid-Session-Slides/blob/master/02-From.Containerization.To.Modularity.pdf&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&&blockquote&&p&如果你喜欢我的文章,就关注下我的 &a href=&/baron& class=&internal&&知乎专栏&/a& 或者在 &a href=&/?target=https%3A///BaronZ88& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&GitHub&i class=&icon-external&&&/i&&/a& 上添个 Star 吧!&/p&&ul&&li&知乎专栏:&a href=&/baron& class=&internal&&&span class=&invisible&&https://&/span&&span class=&visible&&/baro&/span&&span class=&invisible&&n&/span&&span class=&ellipsis&&&/span&&/a&&br&&/li&&li&GitHub:&a href=&/?target=https%3A///BaronZ88& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&/BaronZ88&/span&&span class=&invisible&&&/span&&i class=&icon-external&&&/i&&/a&&/li&&li&个人博客:&a href=&/?target=http%3A//& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&&/span&&span class=&invisible&&&/span&&i class=&icon-external&&&/i&&/a&&/li&&/ul&&/blockquote&
首发于《程序员》杂志五月刊一、前言万维网发明人 Tim Berners-Lee 谈到设计原理时说过:“简单性和模块化是软件工程的基石;分布式和容错性是互联网的生命。” 由此可见模块化之于软件工程领域的重要性。从 2016 年开始,模块化在 Android 社区越来越多的…
&img src=&/v2-9f6e09d2f158c03ecc9ef0e_b.png& data-rawwidth=&1920& data-rawheight=&1014& class=&origin_image zh-lightbox-thumb& width=&1920& data-original=&/v2-9f6e09d2f158c03ecc9ef0e_r.png&&&p&一个人的命运啊,当然要靠自我奋斗,但是也要考虑到历史的进程。人生往往有很多机会是超乎预料的,今天想着要去当教授,明天说不定就要被请去当教皇了。所以啊,技多不压身,不管在哪一个行业,多学一个技能,指不定什么时候就能派上用场。&/p&&p&你说我一个学机器学习的,怎么就跑来做数据库了呢?下面分享一段有趣的个人经历。&/p&&p&前两天公司里测试发现了一个bug,“尸检”(postmortem)之后同事发了封邮件分享了这个有趣的GCC &bug&:&br&&/p&&div class=&highlight&&&pre&&code class=&language-cpp&&&span&&/span&&span class=&cp&&#include&/span& &span class=&cpf&&&iostream&&/span&&span class=&cp&&&/span&
&span class=&cp&&#include&/span& &span class=&cpf&&&string&&/span&&span class=&cp&&&/span&
&span class=&cp&&#include&/span& &span class=&cpf&&&limits&&/span&&span class=&cp&&&/span&
&span class=&cp&&#include&/span& &span class=&cpf&&&inttypes.h&&/span&&span class=&cp&&&/span&
&span class=&kt&&void&/span& &span class=&nf&&ABSTest&/span&&span class=&p&&(&/span&&span class=&kt&&int64_t&/span& &span class=&n&&value1&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&kt&&uint64_t&/span& &span class=&n&&value&/span& &span class=&o&&=&/span& &span class=&k&&static_cast&/span&&span class=&o&&&&/span&&span class=&kt&&uint64_t&/span&&span class=&o&&&&/span&&span class=&p&&(&/span&&span class=&n&&value1&/span& &span class=&o&&&&/span& &span class=&mi&&0&/span& &span class=&o&&?&/span& &span class=&mi&&0&/span& &span class=&o&&-&/span& &span class=&nl&&value1&/span& &span class=&p&&:&/span& &span class=&n&&value1&/span&&span class=&p&&);&/span&
&span class=&n&&std&/span&&span class=&o&&::&/span&&span class=&n&&cout&/span& &span class=&o&&&&&/span& &span class=&s&&&value &&/span& &span class=&o&&&&&/span& &span class=&n&&value&/span&&span class=&p&&;&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&n&&value&/span& &span class=&o&&&&/span& &span class=&mi&&2ul&/span&&span class=&p&&)&/span&
&span class=&n&&std&/span&&span class=&o&&::&/span&&span class=&n&&cout&/span& &span class=&o&&&&&/span& &span class=&s&&& less than &&/span& &span class=&o&&&&&/span& &span class=&mi&&2&/span& &span class=&o&&&&&/span& &span class=&s&&&?!&/span&&span class=&se&&\n&/span&&span class=&s&&&&/span&&span class=&p&&;&/span&
&span class=&k&&else&/span&
&span class=&n&&std&/span&&span class=&o&&::&/span&&span class=&n&&cout&/span& &span class=&o&&&&&/span& &span class=&s&&& compare correct&/span&&span class=&se&&\n&/span&&span class=&s&&&&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&span class=&kt&&int&/span& &span class=&nf&&main&/span&&span class=&p&&()&/span&
&span class=&p&&{&/span&
&span class=&kt&&int64_t&/span& &span class=&n&&value1&/span& &span class=&o&&=&/span& &span class=&n&&std&/span&&span class=&o&&::&/span&&span class=&n&&numeric_limits&/span&&span class=&o&&&&/span&&span class=&kt&&int64_t&/span&&span class=&o&&&::&/span&&span class=&n&&min&/span&&span class=&p&&();&/span&
&span class=&n&&ABSTest&/span&&span class=&p&&(&/span&&span class=&n&&value1&/span&&span class=&p&&);&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&这段代码其实是把64位有符号整型转换成无符号整型,并与无符号整型数2相比较。&p&在GCC4.9以及更高的版本(可在Ubuntu 16.04上测试,或使用gcc -v查看编译器版本)不加任何优化的时候,其输出结果是:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&test@instance-3 /tmp/gcc $ g++ test.cpp -o test && ./test
value 4775808 compare correct
&/code&&/pre&&/div&&p&然而,加了最常用的-O2优化参数之后,结果却不对了:&br&&/p&&div class=&highlight&&&pre&&code class=&language-bash&&&span&&/span&test@instance-3 /tmp/gcc $ g++ test.cpp -o &span class=&nb&&test&/span& -O2 &span class=&o&&&&&/span& ./test
value &span class=&m&&4775808&/span& less than 2?!
&/code&&/pre&&/div&&p&更有趣的是,同事发现,当给ABSTest加上inline关键字,或者把该函数的第一行改为:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&uint64_t value = static_cast&uint64_t&(value1 & 0 ? 0 - (uint64_t)value1 : value1);
&/code&&/pre&&/div&&p&输出结果又是对的。&/p&&p&于是大牛认为这是gcc的bug,虽然修正的方法有点奇怪,但是编译器不是自己写的也没办法。遂记录下来给所有工程师分享讨论。&/p&&p&我看到这个问题也觉得很有意思,因为&a href=&/question/& class=&internal&&除了Jeff Dean,好像没怎么见过有人成功鄙视编译器&/a&。&/p&&p&好在我旦计算机教学的体系比较完善,本科课程从模拟电路到操作系统整个体系都涉及,虽然学得不是太好,但是整个体系结构的大致理解还是可以的,加之当年计算机体系结构直接引入了CMU的教材:《&a href=&/?target=https%3A///subject/1230413/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&深入理解计算机系统&i class=&icon-external&&&/i&&/a&》,拆过二进制炸弹,所以看这种级别程序的汇编代码问题不大,所以我决定深入研究一下造成这个bug的原因,或许可以提交个报告。&/p&&p&测试了一下,我发现这个bug在加编译参数-O1的时候并没有出现,考虑到不加优化的汇编代码差异比较大,不好直接比较,于是我直接直接用编译器加-O1和-O2生成的汇编代码来进行比较。汇编语言,但是有个大致了解非常简单,比C++的原理简单得多,《深入理解计算机系统》那本书讲得非常透彻。&/p&&p&对编译器gcc,使用-S参数可直接生成汇编代码。&/p&&div class=&highlight&&&pre&&code class=&language-bash&&&span&&/span&g++ test.cpp -O2 -S -o test.S
&/code&&/pre&&/div&&p&这样打开test.S可以直接看到汇编代码,我仅截取有用的部分进行讨论。&/p&&p&-O1&/p&&div class=&highlight&&&pre&&code class=&language-nasm&&&span&&/span&&span class=&nf&&sarq&/span&
&span class=&kc&&$&/span&&span class=&mi&&63&/span&&span class=&p&&,&/span& &span class=&o&&%&/span&&span class=&nb&&rax&/span&
&span class=&nf&&movq&/span&
&span class=&o&&%&/span&&span class=&nb&&rdi&/span&&span class=&p&&,&/span& &span class=&o&&%&/span&&span class=&nb&&rbx&/span&
&span class=&nf&&xorq&/span&
&span class=&o&&%&/span&&span class=&nb&&rax&/span&&span class=&p&&,&/span& &span class=&o&&%&/span&&span class=&nb&&rbx&/span&
&span class=&nf&&subq&/span&
&span class=&o&&%&/span&&span class=&nb&&rax&/span&&span class=&p&&,&/span& &span class=&o&&%&/span&&span class=&nb&&rbx&/span&
&span class=&nf&&movl&/span&
&span class=&kc&&$&/span&&span class=&mi&&6&/span&&span class=&p&&,&/span& &span class=&o&&%&/span&&span class=&nb&&edx&/span&
&span class=&nf&&movl&/span&
&span class=&kc&&$&/span&&span class=&nv&&.LC0&/span&&span class=&p&&,&/span& &span class=&o&&%&/span&&span class=&nb&&esi&/span&
&span class=&nf&&movl&/span&
&span class=&kc&&$&/span&&span class=&nv&&_ZSt4cout&/span&&span class=&p&&,&/span& &span class=&o&&%&/span&&span class=&nb&&edi&/span&
&span class=&nf&&call&/span&
&span class=&nv&&_ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l&/span&
&span class=&nf&&movq&/span&
&span class=&o&&%&/span&&span class=&nb&&rbx&/span&&span class=&p&&,&/span& &span class=&o&&%&/span&&span class=&nb&&rsi&/span&
&span class=&nf&&movl&/span&
&span class=&kc&&$&/span&&span class=&nv&&_ZSt4cout&/span&&span class=&p&&,&/span& &span class=&o&&%&/span&&span class=&nb&&edi&/span&
&span class=&nf&&call&/span&
&span class=&nv&&_ZNSo9_M_insertImEERSoT_&/span&
&span class=&nf&&cmpq&/span&
&span class=&kc&&$&/span&&span class=&mi&&1&/span&&span class=&p&&,&/span& &span class=&o&&%&/span&&span class=&nb&&rbx&/span&
&span class=&nf&&ja&/span&
&span class=&nv&&.L2&/span&
&/code&&/pre&&/div&&p&-O2&/p&&div class=&highlight&&&pre&&code class=&language-nasm&&&span&&/span&
&span class=&nf&&sarq&/span&
&span class=&kc&&$&/span&&span class=&mi&&63&/span&&span class=&p&&,&/span& &span class=&o&&%&/span&&span class=&nb&&rax&/span&
&span class=&nf&&movl&/span&
&span class=&kc&&$&/span&&span class=&mi&&6&/span&&span class=&p&&,&/span& &span class=&o&&%&/span&&span class=&nb&&edx&/span&
&span class=&nf&&movl&/span&
&span class=&kc&&$&/span&&span class=&nv&&.LC0&/span&&span class=&p&&,&/span& &span class=&o&&%&/span&&span class=&nb&&esi&/span&
&span class=&nf&&xorq&/span&
&span class=&o&&%&/span&&span class=&nb&&rax&/span&&span class=&p&&,&/span& &span class=&o&&%&/span&&span class=&nb&&rbx&/span&
&span class=&nf&&movl&/span&
&span class=&kc&&$&/span&&span class=&nv&&_ZSt4cout&/span&&span class=&p&&,&/span& &span class=&o&&%&/span&&span class=&nb&&edi&/span&
&span class=&nf&&subq&/span&
&span class=&o&&%&/span&&span class=&nb&&rax&/span&&span class=&p&&,&/span& &span class=&o&&%&/span&&span class=&nb&&rbx&/span&
&span class=&nf&&call&/span&
&span class=&nv&&_ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l&/span&
&span class=&nf&&movq&/span&
&span class=&o&&%&/span&&span class=&nb&&rbx&/span&&span class=&p&&,&/span& &span class=&o&&%&/span&&span class=&nb&&rsi&/span&
&span class=&nf&&movl&/span&
&span class=&kc&&$&/span&&span class=&nv&&_ZSt4cout&/span&&span class=&p&&,&/span& &span class=&o&&%&/span&&span class=&nb&&edi&/span&
&span class=&nf&&call&/span&
&span class=&nv&&_ZNSo9_M_insertImEERSoT_&/span&
&span class=&nf&&cmpq&/span&
&span class=&kc&&$&/span&&span class=&mi&&1&/span&&span class=&p&&,&/span& &span class=&o&&%&/span&&span class=&nb&&rbx&/span&
&span class=&nf&&jle&/span&
&span class=&nv&&.L5&/span&
&/code&&/pre&&/div&&p&注意到两段代码其实非常相似,只有最后一句有差异,一个是JA(无符号跳转),另一个JLE(有符号跳转)。&/p&&p&乍一看似乎的确是gcc的bug。稍微有一些C++编程经验的程序员应该了解-O1和-O2的区别只在于O2多了一些优化选项。为了搞清楚更深层次的原因,需要进一步确定是哪些优化选项导致的。在gcc的文档页面可以看到各种优化选项:&a href=&/?target=https%3A//gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Optimize Options&i class=&icon-external&&&/i&&/a&&/p&&p&这样我们一下子就把范围缩小到了37个优化选项,我们只需要确定是哪个选项导致的这个问题。怎么最快找到这题图书馆管理员大妈都能做,我就不说了。&/p&&p&最后锁定下来,竟然是两个选项同时开启才会造成的:&/p&&p&-ftree-vrp 和 -fstrict-overflow &br&&/p&&p&这下就有意思了,先看看ftree-vrp:&/p&&blockquote&-ftree-vrp&br&Perform Value Range Propagation on trees. This is similar to the constant propagation pass, but instead of values, ranges of values are propagated. This allows the optimizers to remove unnecessary range checks like array bound checks and null pointer checks. This is enabled by default at -O2 and higher. Null pointer check elimination is only done if -fdelete-null-pointer-checks is enabled.&br&来源:&a href=&/?target=https%3A//gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Optimize Options&i class=&icon-external&&&/i&&/a&&/blockquote&&p&这个优化的主要思想是,在编译过程中,如果一个变量能够知道他的最大和最小值,如果之后再出现相应的类型检查,就可以将其删除。&br&&/p&&p&比如说下面这段代码(&a href=&/?target=http%3A///category/range.htm& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&来源&i class=&icon-external&&&/i&&/a&):&/p&&div class=&highlight&&&pre&&code class=&language-cpp&&&span&&/span&&span class=&k&&for&/span& &span class=&p&&(&/span&&span class=&n&&i&/span& &span class=&o&&=&/span& &span class=&mi&&1&/span&&span class=&p&&;&/span& &span class=&n&&i&/span& &span class=&o&&&&/span& &span class=&mi&&100&/span&&span class=&p&&;&/span& &span class=&n&&i&/span&&span class=&o&&++&/span&&span class=&p&&)&/span&
&span class=&p&&{&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&n&&i&/span&&span class=&p&&)&/span&
&span class=&n&&g&/span& &span class=&p&&();&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&在编译过程中就可以明确知道,i 的范围是[1, 100]的整型,所以if(i)这个条件永远为真,就可以优化掉,变成:&div class=&highlight&&&pre&&code class=&language-cpp&&&span&&/span&&span class=&k&&for&/span& &span class=&p&&(&/span&&span class=&n&&i&/span& &span class=&o&&=&/span& &span class=&mi&&1&/span&&span class=&p&&;&/span& &span class=&n&&i&/span& &span class=&o&&&&/span& &span class=&mi&&100&/span&&span class=&p&&;&/span& &span class=&n&&i&/span&&span class=&o&&++&/span&&span class=&p&&)&/span&
&span class=&p&&{&/span&
&span class=&n&&g&/span& &span class=&p&&();&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&这个优化其实非常合理,一时半会儿也看不出什么问题。那么再来看下一个:&blockquote&-fstrict-overflow&br&Allow the compiler to assume strict signed overflow rules, depending on the language being compiled. For C (and C++) this means that overflow when doing arithmetic with signed numbers is undefined, which means that the compiler may assume that it does not happen. This permits various optimizations. For example, the compiler assumes that an expression like i + 10 & i is always true for signed i. This assumption is only valid if signed overflow is undefined, as the expression is false if i + 10 overflows when using twos complement arithmetic. When this option is in effect any attempt to determine whether an operation on signed numbers overflows must be written carefully to not actually involve overflow.&br&This option also allows the compiler to assume strict pointer semantics: given a pointer to an object, if adding an offset to that pointer does not produce a pointer to the same object, the addition is undefined. This permits the compiler to conclude that p + u & p is always true for a pointer p and unsigned integer u. This assumption is only valid because pointer wraparound is undefined, as the expression is false if p + uoverflows using twos complement arithmetic.&br&See also the -fwrapv option. Using -fwrapv means that integer signed overflow is fully defined: it wraps. When -fwrapv is used, there is no difference between -fstrict-overflow and -fno-strict-overflowfor
integers. With -fwrapv certain types of overflow are permitted. For example, if the compiler gets an overflow when doing arithmetic on constants, the overflowed value can still be used with -fwrapv, but not otherwise.&br&The -fstrict-overflow option is enabled at levels -O2, -O3, -Os.&br&&br&来源:&a href=&/?target=https%3A//gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Optimize Options&i class=&icon-external&&&/i&&/a&&/blockquote&&p&说实话,看到前两句话,我的内心是非常激动的:&b&这不是一个bug,而是一个feature!&/b&该优化假设计算不存在有符号整型的溢出,否则是未定义行为(未定义行为指的是编译器想干嘛就干嘛,所以结果不一定总是错的,也可能是对的,优化怎么强大怎么来)!那一切都有了合理的解释:&/p&&p&首先,value1是定义了64位整型的最小值,也就是&img src=&/equation?tex=%5B-2%5E%7B63%7D%2C+2%5E%7B63%7D-1%5D& alt=&[-2^{63}, 2^{63}-1]& eeimg=&1&&. 当对value1 进行0-value1计算的时候,发生了溢出。&/p&&p&然后,由于两个编译选项是-O2默认开启的,所以编译过程编译器假设value1不会溢出,编译器直接进行了优化,由于value是无符号整型,要跟2比较大小,在JLE和JA都可以用的情况下,JLE由于某种原因被选中了,所以就出现了诡异的结果。&/p&&p&他给的两个修改方法,加上inline那个能够得出正确结果,估计是纯粹是偶然,编译器恰好没选择导致出错的JLE,而选择了JA,这估计跟具体的优化策略有关。换一个编译器版本或者平台估计还是会出错。&/p&&p&而加unsigned也是错误的。有符号整型强制转换成无符号整型的时候,无论正数负数都是保留所有二进制位不变:&/p&&blockquote&&If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2^n where n is the number of bits used to represent the unsigned type). [ Note: In a two’s complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is no truncation). —end note ]&br&&br&来源:&a href=&/?target=http%3A///questions/2711522/what-happens-if-i-assign-a-negative-value-to-an-unsigned-variable& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&What happens if I assign a negative value to an unsigned variable?&i class=&icon-external&&&/i&&/a&&/blockquote&&p&同事的本意是取绝对值,但是由于正数的最大值比负数的最小值绝对值小1,这种情况下同样长度的有符号整型是没办法表示的。同事期望的表现是在这种情况下,直接变成无符号整型,这样就能够表示了。这样看似乎这样做是可以的,但是别忘了,大部分计算机表示负数是用补码,比如1字节有符号整型有8位,它的-1是,如果直接这样转换成无符号整型,就变成了1+2+4+8+16+32+64+128 = 255,这显然不是取绝对值操作。&/p&&p&下面来考虑怎么解决这个问题。&/p&&p&如果学过汇编语言,这题其实很简单。那段C++代码,能够造成溢出的只有那一个值。所以,在初始化的时候将其改成std::numeric_limits&int64_t&::min() + 1就可以避免溢出。这是一种改法,当然这样改并不好,原因在于当其它程序员在用同样的方法做事情的时候,会导致同样的错误。&/p&&p&去掉那两个flag中的任意一个吗?这显然也不好。因为我们公司是做数据库的,代码性能异常重要,这两个优化很多时候还是效果非常明显。&/p&&p&读了一下文档,我发现有两个选择:&/p&&br&&ul&&li&打开编译器警告。在编译选项中加上 -Wstrict-overflow=2 即可出现报警:&br&&/li&&/ul&&br&&ol&&li&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&test@instance-3 /tmp/gcc $ g++ test1.cpp -o test1.S -S -O2 --std=c++11 -Wstrict-overflow=2
test1.cpp: In function ‘size_t GetCompressedIntByteSize(int64_t)’:
test1.cpp:6:3: warning: assuming signed overflow does not occur when simplifying conditional [-Wstrict-overflow]
if (value & (((uint64_t)1) && 6)) return 1;
&/code&&/pre&&/div&&/li&&/ol&&br&&ul&&li&加上-fwrapv选项。这个选项我是从上文中-fstrict-overflow看到的,它定义了溢出时候编译器的行为——采用二补码的方式进行操作:&/li&&/ul&&blockquote&-fwrapv&br&This option instructs the compiler to assume that signed arithmetic overflow of addition, subtraction and multiplication wraps around using twos-complement representation. This flag enables some optimizations and disables others. This option is enabled by default for the Java front-end, as required by the Java language specification.&br&&br&来源:&a href=&/?target=https%3A//gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Using the GNU Compiler Collection (GCC): Code Gen Options&i class=&icon-external&&&/i&&/a&&/blockquote&&br&&p&文档写这么详细,肯定是有人踩过同样的坑。但是,做这么强的假设,应该直接丢警告才对啊!&br&&/p&&p&除了解决方法,我还给同事提了一条建议:仔细阅读所有编译选项,看看有没有类似的强假设。&/p&&br&&h2&总结&/h2&&ul&&li&这不是一个bug,而是一个feature&/li&&li&C++程序员非常有必要了解编译器的原理,读编译器文档,知道编译器在做各种优化时候所做的假设&/li&&li&GCC文档上比LLVM完善不少,所以如果考虑整体迁移到LLVM,虽然好处不少,但是文档不全这方面需要慎重考虑&/li&&li&虽然现代软件工程强调解耦、模块化,试图把不同的层级分隔开,让每一层的程序员只专注于自己的那一层。但是毕竟软件是人来写的,有人的地方不可避免的有人为失误(bug),很多时候会遇到像本文这样的bug,牵涉到下一层的东西。所以,要想成为一个优秀的软件工程师,需要对从上到下的体系结构都有一定了解。这一点,复旦计算机学院的课程设计就很好,赵一鸣院长就说,培养的学生要建立从基本电路,到制作CPU,到计算机体系结构,设计编译器、操作系统的全方面知识体系。虽然在教学上跟美国顶尖大学比仍有非常大的差距,但是总体教学设计思路仍然让我收益颇丰。&/li&&/ul&&h2&思考题&br&&/h2&&p&下面两道题我懒得写答案了,如果有人问的话我会在评论里写答案。&/p&&p&1、上文中所说的代码是由于编译器的优化导致的不确定行为,按照上述解释,同样的值传入同样的函数,结果应该相同。解释下列代码运行结果出现的原因(提示:原因非常有趣!)&/p&&p&代码&/p&&div class=&highlight&&&pre&&code class=&language-cpp&&&span&&/span&&span class=&cp&&#include&/span& &span class=&cpf&&&iostream&&/span&&span class=&cp&&&/span&
&span class=&cp&&#include&/span& &span class=&cpf&&&string&&/span&&span class=&cp&&&/span&
&span class=&cp&&#include&/span& &span class=&cpf&&&limits&&/span&&span class=&cp&&&/span&
&span class=&cp&&#include&/span& &span class=&cpf&&&inttypes.h&&/span&&span class=&cp&&&/span&
&span class=&kt&&size_t&/span& &span class=&nf&&test&/span&&span class=&p&&(&/span&&span class=&kt&&int64_t&/span& &span class=&n&&value1&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&kt&&uint64_t&/span& &span class=&n&&value&/span& &span class=&o&&=&/span& &span class=&k&&static_cast&/span&&span class=&o&&&&/span&&span class=&kt&&uint64_t&/span&&span class=&o&&&&/span&&span class=&p&&(&/span&&span class=&n&&value1&/span& &span class=&o&&&&/span& &span class=&mi&&0&/span& &span class=&o&&?&/span& &span class=&mi&&0&/span& &span class=&o&&-&/span& &span class=&nl&&value1&/span& &span class=&p&&:&/span& &span class=&n&&value1&/span&&span class=&p&&);&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&n&&value&/span& &span class=&o&&&&/span& &span class=&p&&(((&/span&&span class=&kt&&uint64_t&/span&&span class=&p&&)&/span&&span class=&mi&&1&/span&&span class=&p&&)&/span& &span class=&o&&&&&/span& &span class=&mi&&6&/span&&span class=&p&&))&/span& &span class=&k&&return&/span& &span class=&mi&&1&/span&&span class=&p&&;&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&n&&value&/span& &span class=&o&&&&/span& &span class=&p&&(((&/span&&span class=&kt&&uint64_t&/span&&span class=&p&&)&/span&&span class=&mi&&1&/span&&span class=&p&&)&/span& &span class=&o&&&&&/span& &span class=&mi&&13&/span&&span class=&p&&))&/span& &span class=&k&&return&/span& &span class=&mi&&2&/span&&span class=&p&&;&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&n&&value&/span& &span class=&o&&&&/span& &span class=&p&&(((&/span&&span class=&kt&&uint64_t&/span&&span class=&p&&)&/span&&span class=&mi&&1&/span&&span class=&p&&)&/span& &span class=&o&&&&&/span& &span class=&mi&&20&/span&&span class=&p&&))&/span& &span class=&k&&return&/span& &span class=&mi&&3&/span&&span class=&p&&;&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&n&&value&/span& &span class=&o&&&&/span& &span class=&p&&(((&/span&&span class=&kt&&uint64_t&/span&&span class=&p&&)&/span&&span class=&mi&&1&/span&&span class=&p&&)&/span& &span class=&o&&&&&/span& &span class=&mi&&27&/span&&span class=&p&&))&/span& &span class=&k&&return&/span& &span class=&mi&&4&/span&&span class=&p&&;&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&n&&value&/span& &span class=&o&&&&/span& &span class=&p&&(((&/span&&span class=&kt&&uint64_t&/span&&span class=&p&&)&/span&&span class=&mi&&1&/span&&span class=&p&&)&/span& &span class=&o&&&&&/span& &span class=&mi&&34&/span&&span class=&p&&))&/span& &span class=&k&&return&/span& &span class=&mi&&5&/span&&span class=&p&&;&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&n&&value&/span& &span class=&o&&&&/span& &span class=&p&&(((&/span&&span class=&kt&&uint64_t&/span&&span class=&p&&)&/span&&span class=&mi&&1&/span&&span class=&p&&)&/span& &span class=&o&&&&&/span& &span class=&mi&&41&/span&&span class=&p&&))&/span& &span class=&k&&return&/span& &span class=&mi&&6&/span&&span class=&p&&;&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&n&&value&/span& &span class=&o&&&&/span& &span class=&p&&(((&/span&&span class=&kt&&uint64_t&/span&&span class=&p&&)&/span&&span class=&mi&&1&/span&&span class=&p&&)&/span& &span class=&o&&&&&/span& &span class=&mi&&48&/span&&span class=&p&&))&/span& &span class=&k&&return&/span& &span class=&mi&&7&/span&&span class=&p&&;&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&n&&value&/span& &span class=&o&&&&/span& &span class=&p&&(((&/span&&span class=&kt&&uint64_t&/span&&span class=&p&&)&/span&&span class=&mi&&1&/span&&span class=&p&&)&/span& &span class=&o&&&&&/span& &span class=&mi&&55&/span&&span class=&p&&))&/span& &span class=&k&&return&/span& &span class=&mi&&8&/span&&span class=&p&&;&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&n&&value&/span& &span class=&o&&&&/span& &span class=&p&&(((&/span&&span class=&kt&&uint64_t&/span&&span class=&p&&)&/span&&span class=&mi&&1&/span&&span class=&p&&)&/span& &span class=&o&&&&&/span& &span class=&mi&&62&/span&&span class=&p&&))&/span& &span class=&k&&return&/span& &span class=&mi&&9&/span&&span class=&p&&;&/span&
&span class=&k&&return&/span& &span class=&mi&&10&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&span class=&kt&&int&/span& &span class=&nf&&main&/span&&span class=&p&&(){&/span&
&span class=&p&&{&/span&
&span class=&kt&&int64_t&/span& &span class=&n&&test2&/span& &span class=&o&&=&/span& &span class=&n&&std&/span&&span class=&o&&::&/span&&span class=&n&&numeric_limits&/span&&span class=&o&&&&/span&&span class=&kt&&int64_t&/span&&span class=&o&&&::&/span&&span class=&n&&max&/span&&span class=&p&&();&/span&
&span class=&n&&std&/s}

我要回帖

更多关于 如何高效完成任务 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信