精华内容
下载资源
问答
  • 鹤岗盆地断裂构造发育,对煤炭勘查和煤矿生产造成巨大影响。基于鹤岗盆地内13个矿区346条正断层、逆断层的走向分布规律,分析了各期构造应力与区内断裂的生成关系。研究表明:区内逆断层走向以NE、NNE、NW为优势方向,正...
  • React 演化史

    2017-08-25 22:41:46
    在 Stack Overflow Trends 中快速搜索 React 的技术趋势, 可以清楚发现 2014 到 2017 年 React 呈现稳步增长的趋势, 而且在各大社区不难发现, 世界各地的开发人员在最近几年都在积极寻求并获得有关 React 问题的...

    Stack Overflow Trends 中快速搜索 React 的技术趋势, 可以清楚地发现 20142017 年 React 呈现稳步增长的趋势, 而且在各大社区不难发现, 世界各地的开发人员在最近几年都在积极寻求并获得有关 React 问题的答案.

    React 真的是非常的美妙, 我们将会用接下来十年的时间来继续探索.
    – Guillermo Rauch

    这里写图片描述

    ReactJS 最开始只是 XHP 的一个扩展. 简单说说 XHP, 它是 Facebook 2010 年推出的 PHP 新的书写方式, 旨在让前端开发变的更简单, 除此之外, 它还能防御跨站点脚本攻击(XSS), XSS 对于有经验的 Web 开发人员就再熟悉不过了, 它允许用户恶意将代码注入到网页中, 是一种常见的 Web 应用程序的安全漏洞攻击. XHP 有 Automatic XSS protection 的功能, 能够很好地规避以上问题.

    然而, XHP 在创建动态 Web 应用程序时却出现了问题, 比如一旦应用程序 state 发生了改变, 整个程序将会重新 render, 这必将导致用户失去之前存储在 DOM 中的 所有数据. 这让 Facebook 的 Ads Org 团队不得不深思: 为什么要重新渲染整个 Page, 只因为其中一个 state 的改变? 他们及时地意识到这个问题, 这样的 Web 应用程序将对未来的用户体验造成严重的干扰.

    2011 年, Facebook 高级软件工程 Jordan Walke 开始着手解决这个问题: 如何让 Web 应用程序更加高效, 更好地提升用户体验. 就这样, 一个用于构建用户界面的 JavaScript 库诞生了.

    就在当时, Instagram 想用 React 来构建他们的网站, 然而, 却遇到了很多的问题, 比如 React 跟 Facebook 的 stack 联系可谓十分紧密. 但他们并没有放弃, 反而做了很多的工作, 他们希望, 今后全世界的开发者都会庆幸有 React 这么棒的东西

    2013 年 5 月, React 在美国 JSConf 开源. 自那以后, 全世界的开发者都很快地将 React 投入到了生产环境中, 像 Trello, Slack, Docker, Airbnb, Khan Academy, New York Times 这些公司都冲在了前列.

    2015 年 4 月, Facebook 发布了关于使用 React 的专利, 说: 只要不起诉我们专利侵权, 我们就免费给你使用该软件. 你可以点击这里阅读关于专利的更多内容.

    6 个 React 亮点

    Virtual Document Object Model

    为了搞清楚这个, 我们假设有一个对象, 它有很多个属性, 然后, 我们去修改其中的一个属性的值, 这时, React Virtual Document Object Model 的高效 Diff 算法就起作用了, 它会首先快速识别出哪些属性发生了改变, 而且它将这个过程的复杂度控制在了 O(n), 接下来就是非常了不起的 reconciliation 操作, 它只会对界面上真正发生变化的部分进行实际的 DOM 操作, 而不是整个 DOM, 这让我们可以无需担心性能问题而毫无顾忌的随时刷新整个页面了, 就问屌不屌.

    Server-Side Rendering

    React 可以很优雅地实现 Server-Side Rendering. 即当用户向应用程序发送请求时, 服务器会将其所需要的组件渲染成 HTML 字符串, 然后把它返回给浏览器, 之后, 浏览器直接解析 HTML 就行. 这样不仅缩短了响应时间, 提升了用户体验, 而且有利于 SEO, 最后, 还给开发者带来了组件式开发代码同构的便利.

    React Native

    React Native 于 2015 年发布. 它支持开发者用 JavaSciptReact 构建真正 native 的 Android 和 iOS 应用. 这样, 我们就不需要再去拼命地学习 JavaObjective-C 了.

    React VR

    React VR 在最近的 F8 Developer Conference 上发布. 它允许用户只用 JavaScipt 就能构建 virtual reality (VR) 应用. React VR 还能用 WebGLWebVR 给用户带来完美的 VR 体验. 最后, 跟 React 一样, 开发者也可以采用声明组件的方式.

    React Fiber

    React Fiber 将在 React 16 中闪亮登场, 最初于 2016 年 7 月公开发布, 它是 Facebook 开发的一个全新的架构, 不仅包含新的协调引擎,而且提供了可串联使用的全新渲染器. 该架构可向后兼容,彻底重写了 React 的协调(Reconciliation)算法, 蕴含着过去多年来 Facebook 不断改进的工作成果. 你可以访问该链接跟进发布.

    React Primitives

    react-primitivesLeland Richardson 创建的一个库. 该库提供了一套理想的 primitives 让不同平台中的 React 应用程序都可以使用.

    总结

    如果你瞬间对 React 产生了浓厚的兴趣, 那么就快点击这里展开你的 React 生涯吧.

    原文链接: The evolution of React (FISAYO AFOLAYAN)

    展开全文
  • 游戏引擎演化史

    2009-06-21 22:55:00
    一、什么是引擎 游戏的引擎直接控制玩家所体验到的剧情、关卡、美工、音乐、操作等内容,它扮演着中场发动机的角色,把游戏中的所有元素捆绑在一起,在后台指挥它们同时、有序工作。简单说,引擎就是“用于控制...

    一、什么是引擎

        游戏的引擎直接控制玩家所体验到的剧情、关卡、美工、音乐、操作等内容,它扮演着中场发动机的角色,把游戏中的所有元素捆绑在一起,在后台指挥它们同时、有序地工作。简单地说,引擎就是“用于控制所有游戏功能的主程序,从计算碰撞、物理系统和物体的相对位置,到接受玩家的输入,以及按照正确的音量输出声音等等。如今的游戏引擎已经发展为一套由多个子系统共同构成的复杂系统,从建模、动画到光影、粒子特效,从物理系统、碰撞检测到文件管理、网络特性,还有专业的编辑工具和插件,几乎涵盖了开发过程中的所有重要环节,以下就对引擎的一些关键部件作一个简单的介绍。

        首先是光影效果,即场景中的光源对处于其中的人和物的影响方式。游戏的光影效果完全是由引擎控制的,折射、反射等基本的光学原理以及动态光源、彩色光源等高级效果都是通过引擎的不同编程技术实现的。

        其次是动画,目前游戏所采用的动画系统可以分为两种:一是骨骼动画系统,一是模型动画系统,前者用内置的骨骼带动物体产生运动,比较常见,后者则是在模型的基础上直接进行变形。引擎把这两种动画系统预先植入游戏,方便动画师为角色设计丰富的动作造型。

        引擎的另一重要功能是提供物理系统,这可以使物体的运动遵循固定的规律,例如,当角色跳起的时候,系统内定的重力值将决定他能跳多高,以及他下落的速度有多快,子弹的飞行轨迹、车辆的颠簸方式也都是由物理系统决定的。

        碰撞探测是物理系统的核心部分,它可以探测游戏中各物体的物理边缘。当两个3D物体撞在一起的时候,这种技术可以防止它们相互穿过,这就确保了当你撞在墙上的时候,不会穿墙而过,也不会把墙撞倒,因为碰撞探测会根据你和墙之间的特性确定两者的位置和相互的作用关系。

        渲染是引擎最重要的功能之一,当3D模型制作完毕之后,美工会按照不同的面把材质贴图赋予模型,这相当于为骨骼蒙上皮肤,最后再通过渲染引擎把模型、动画、光影、特效等所有效果实时计算出来并展示在屏幕上。渲染引擎在引擎的所有部件当中是最复杂的,它的强大与否直接决定着最终的输出质量。

        引擎还有一个重要的职责就是负责玩家与电脑之间的沟通,处理来自键盘、鼠标、摇杆和其它外设的信号。如果游戏支持联网特性的话,网络代码也会被集成在引擎中,用于管理客户端与服务器之间的通信。

        通过上面这些枯燥的介绍我们至少可以了解到一点:引擎相当于游戏的框架,框架打好后,关卡设计师、建模师、动画师只要往里填充内容就可以了。因此,在3D游戏的开发过程中,引擎的制作往往会占用非常多的时间,《马科斯·佩恩》的MAX-FX引擎从最初的雏形Final Reality到最终的成品共花了四年多时间,LithTech引擎的开发共花了整整五年时间,耗资700万美元,Monolith公司(LithTech引擎的开发者)的老板詹森·霍尔甚至不无懊悔地说:“如果当初意识到制作自己的引擎要付出这么大的代价的话,我们根本就不可能去做这种傻事。没有人会预料得到五年后的市场究竟是怎样的。”

        正是出于节约成本、缩短周期和降低风险这三方面的考虑,越来越多的开发者倾向于使用第三方的现成引擎制作自己的游戏,一个庞大的引擎授权市场已经形成。

    二、引擎的进化  

         曾经有一段时期,游戏开发者关心的只是如何尽量多地开发出新的游戏并把它们推销给玩家。尽管那时的游戏大多简单粗糙,但每款游戏的平均开发周期也要达到8到10个月以上,这一方面是由于技术的原因,另一方面则是因为几乎每款游戏都要从头编写代码,造成了大量的重复劳动。渐渐地,一些有经验的开发者摸索出了一条偷懒的方法,他们借用上一款类似题材的游戏中的部分代码作为新游戏的基本框架,以节省开发时间和开发费用。根据马老先生的生产力学说,单位产品的成本因生产力水平的提高而降低,自动化程度较高的手工业者最终将把那些生产力低下的手工业者淘汰出局,引擎的概念就是在这种机器化作业的背景下诞生的。

        每一款游戏都有自己的引擎,但真正能获得他人认可并成为标准的引擎并不多。纵观九年多的发展历程,我们可以看出引擎最大的驱动力来自于3D游戏,尤其是3D射击游戏。尽管像Infinity这样的2D引擎也有着相当久远的历史,从《博德之门》(Baldur’s Gate)系列到《异域镇魂曲》(Planescape:Torment)、《冰风谷》(Icewind Dale)直至今年夏天将要发布的《冰风谷2》,但它的应用范围毕竟局限于“龙与地下城”风格的角色扮演游戏,包括颇受期待的《夜在绝冬城》(Neverwinter Nights)所使用的Aurora引擎,它们都有着十分特殊的使用目的,很难对整个引擎技术的发展起到推动作用,这也是为什么体育模拟游戏、飞行模拟游戏和即时策略游戏的引擎很少进入授权市场的原因,开发者即便使用第三方引擎也很难获得理想的效果,采用《帝国时代2》(Age of Empires)引擎制作的《星球大战:银河战场》(Star Wars:Galactic Battleground)就是一个最好的例子。

    二、游戏引擎的总概

    1. Tools/Data (工具/数据)

        在开发过程中,你总是需要一些数据,但不幸的是这并不象写文本文件或是定义一个立方体那么简单。至少,你得需要3d模型编辑器,关卡编辑器,以及图形程序。你可以通过购买,也可以在网上找一些免费的程序满足你的开发要求。不幸的是你可能还需要一些更多的工具可你却根本无法获得(还不存在呢),这时你只得自己动手去写。最终你很可能要自行设计编写一个关卡编辑器,因为你更本不可能获得你所需。你可能也会编写一些代码来为大量的文件打个包,整天面对应付成百上千个文件倒是非常痛苦的。你还必须写一些转换器或是插件将3d模型编辑器的模型格式转换成你自己的格式。你也需要一些加工游戏数据的工具,譬如可见度估算或是光线贴图。

        一个基本的准则是,你可能要为设计工具而置入比游戏本身等量甚至更多的代码。开始你总能找到现成的格式和工具,但是经过一段时间以后你就能认识到你需要你的引擎有很大的特性,然后你就会放弃以前的撰写方式。

        也许目前非常流行利用的第3方工具辅助开发,所以你必须时刻注意你的设计。因为一旦当你将你的引擎发布为opensouce或是允许修改,那也许在某天中会有某些人来应用你的开发成果,他们将其扩展或者做某些修改。

        或许你也应该花大量时间去设计美术,关卡,音效,音乐和实体模型,这就和你设计撰写游戏,工具以及引擎一样。

    2. System (系统)

        系统(system)是引擎与机器本身做通信交互的部件。一个优秀的引擎在待平台移植时,它的系统则是唯一需要做主要更改(扩加代码)的地方。我们把一个系统分为若干个子系统,其中包括:图形(Graphics)、输入(Input)、声音(Sound)、记时器(Timer)、配置(Configuration)。主系统负责初始化、更新、以及关闭所有的子系统。

        图形子系统(Graphics Sub-System)在游戏里表现得非常直观,如果想在屏幕上画点什么的话,它(图形子系统)便干这事儿。大多数来讲,图形子系统都是利用OpenGL,Direct3D, Glide或是软件渲染(software rendering)实现。如果能更理想一些,你甚至可以把这些API都给支持了,然后抽象出一个“图形层”并将它置与实现API之上,这将给了客户开发人员或是玩家更多的选择,以获取最好的兼容性、最佳的表现效果。

        输入子系统(Input Sub-System)需要把各种不同输入装置(键盘、鼠标、游戏板[Gamepad],游戏手柄[Joystick])的输入触发做统一的控制接收处理。(透明处理) 比方说,在游戏中,系统要检测玩家的位置是否在向前移动,与其直接地分别检测每一种输入装置,不如通过向输入子系统发送请求以获取输入信息,而输入子系统才在幕后真正地干活(分别检测每一种输入装置),这一切对于客户开发人员都是透明的。用户与玩家可以非常自由地切换输入装置,通过不同的输入装置来获取统一的行为将变的很容易。

        声音子系统(sound system)负责载入、播放声音。该子系统功能非常简洁明了,但当前很多游戏都支持3D声音,实现起来会稍许复杂一些。

        3D游戏引擎中很多出色的表现都是基于“时间系统”(time)的。因此你需要一段时间来为时间子系统(Timer sub-system)好好构思一番。即使它非常的简单,(游戏里)任何东西都是通过时间触发来做移动变化,但一份合理的设计将会让你避免为实现而一遍又一遍地撰写大量雷同的控制代码……

        配置系统(Configuration)位于所有子系统的顶端。它负责读取配置记录文件,命令行参数,或是实现修改设置(setup)。在系统初始化以及运行期间,所有子系统都将一直与它保持通讯。切换图象解析度(resolution),色深(color depth),定义按钮(key bindings),声音支持选项(sound support options),甚至包括载入游戏,该系统将这些实现显得格外的简单与方便。把你引擎设计得更为可设置化一些,这将为调试与测试带来更大的方便;玩家与用户也能很方便地选择他(她)们喜欢的运行方式。

    3. Console (控制台)

        所有人可能都乐意去跟风做一个象Quake那样的控制台(console)系统。但这的确是一个非常好的想法。通过命令行变量与函数,你就能够在运行时改变你的游戏或是引擎的设置,而不需要重启。开发期间输出调试信息它将显得非常的有效。很多时间你都需要测试一系列变量的值,将这些值输出到控制台上要比运行一个debugger速度显然要快得多。你的引擎在运行期间,一旦发现了一个错误,你不必立即退出程序;通过控制台,你可以做些非常轻便的控制,并将这个错误信息打印出来。假如你不希望你的最终用户看见或是使用该控制台,你可以非常方便地将其disable,我想没人能看得见它。

    4. Support (支持)

        支持系统(Support)在你引擎中任何地方都将被使用到。该系统包含了你引擎中所有的数学成分(点,面,矩阵等),(内)存储管理器,文件载入器,数据容器(假如你不愿自己写,也可以使用STL)。该模块任务显得非常基础与底层,或许你会将它复用到更多别的相关项目中去。

    5. Renderer/Engine Core (渲染/引擎 内核)

        可能所有的人都热爱3D图象渲染!因为这边有着非常多的不同种类的3D世界渲染方式,可要为各类拥有不同工作方式的3D图形管道做出一个概要描述也是几乎不可能的。不管你的渲染器如何工作,最重要的是将你的渲染器组件制作得基化(based)与干净(clean)。首先可以确定的是你将拥有不同的模块来完成不同的任务,我将渲染器拆分为以下几个部份:可见裁减(Visibility)、碰撞检测与反馈(Collision Detection and Response)、摄像器(Camera)、静态几何体(Static Geometry)、动态几何体(Dynamic Geometry)、粒子系统(Particle Systems)、布告板(Billboarding)、网格(Meshes)、天空体(Skybox)、光线(Lighting)、雾(Fogging)、节点阴影(Vertex Shading)和输出(Output)。

    6. Game Interface (游戏介质)

        一个3D(游戏)引擎很重要的部分便是------它是一个游戏引擎。但这并不是一个游戏。一个真正的游戏所需的一些组件永远不要将它包含到游戏引擎里。引擎与游戏制作之间的控制介质能使代码设计变得更清晰,应用起来也会更舒服。这虽是一些额外的代码,但它能使游戏引擎具有非常好重用性,通过设计架够游戏逻辑(game logic)的脚本语言(scripting language)也能使开发变的更方便,也可以将游戏代码置入库中。如果你想在引擎本身中嵌入你的游戏逻辑系统设计的话,大量的问题与大量修改一定会让你打消复用这个.

    展开全文
  • 根据川南地区震旦系灯影组气田的异常压力成因机制及地质特征,以现今实测压力为约束,利用流体压实耦合方法恢复了震旦系灯影组气田主要储层的压力演化史。威远和资阳气田压力演化比较一致,早期的古油藏形成过程未引起...
  • 作者|Tyler McGinnis ...我最喜欢的一个站点叫做 BerkshireHathaway.com,它非常简单、高效,从 1997 年创建以来它一直都能很好完成自己的任务。尤其值得注意的是,在过去的 20 年间,这个站点...

    作者|Tyler McGinnis
    译者|张卫滨

    本文以实际样例阐述了异步 JavaScript 的发展过程,介绍了每种实现方式的优势和不足,能够帮助读者掌握相关技术的使用方式并把握技术发展的脉络。

    我最喜欢的一个站点叫做 BerkshireHathaway.com,它非常简单、高效,从 1997 年创建以来它一直都能很好地完成自己的任务。尤其值得注意的是,在过去的 20 年间,这个站点从来没有出现过缺陷。这是为什么呢?因为它是静态的,从建立到现在的 20 年间,它几乎没有发生过什么变化。如果你将所有的数据都放在前面的话,搭建站点是非常简单的。但是,如今大多数的站点都不会这么做。为了弥补这一点,我们发明了所谓的“模式”,帮助我们的应用从外部抓取数据。同其他大多数事情一样,这些模式都有一定的权衡,并随着时间的推移在发生着变化。在本文中,我们将会分析三种常用模式的优劣,即回调(Callback)、Promise 和 Async/Await,并从历史发展的维度讨论一下它们的意义和发展。

    我们首先从数据获取模式的最原始方式开始介绍,那就是回调。

    回调

    在这里我假设你对回调一无所知,如果事实并非如此的话,那么你可以将内容稍微往后拖动一下。

    当我第一次学习编程的时候,它就帮助我形成了一种思考方式,那就是将功能视为一种机器。这些机器能够完成任何你希望它能做到的事情,它们甚至能够接受输入并返回值。每个机器都有一个按钮,如果你希望这个机器运行的话,就按下按钮,这个按钮也就是 ()。

    function add (x, y) {
      return x + y
    }
    
    add(2,3) // 5 - 按下按钮,运行机器。

    事实上,不仅我能按下按钮,你 也可以,任何人 按下按钮的效果都是一样的。只要按下按钮,不管你是否愿意,这个机器就会开始运行。

    function add (x, y) {
     return x + y
    }
    
    const me = add
    const you = add
    const someoneElse = add
    
    me(2,3) // 5 - 按下按钮,运行机器。
    you(2,3) // 5 - 按下按钮,运行机器。
    someoneElse(2,3) // 5 - 按下按钮,运行机器。

    在上面的代码中,我们将add函数赋值给了三个不同的变量:me、you和someoneElse。有很重要的一点需要注意,原始的add和我们创建的每个变量都指向的相同的内存点。在不同的名字之下,它们实际上是完全相同的内容。所以,当我们调用me、you或someoneElse的时候,就像调用add一样。

    如果我们将add传递给另外一台机器又会怎样呢?需要记住,不管谁按下这个“()”按钮,它都会执行。

    function add (x, y) {
      return x + y
    }
    
    function addFive (x, addReference) {
      return addReference(x, 5) // 15 - 按下按钮,运行机器。
    }
    
    addFive(10, add) // 15

    你可能会觉得这有些诡异,但是这里没有任何新东西。此时,我们不再是在add上“按下按钮”,而是将add作为参数传递给addFive,将其重命名为addReference,然后我们“按下按钮”或者说调用它。

    这里涉及到了 JavaScript 的一些重要概念。首先,就像可以将字符串或数字以参数的形式传递给函数一样,我们还可以将函数的引用作为参数进行传递。但我们这样做的时候,作为参数传递的函数被称为回调函数(callback function),而接收回调函数传入的那个函数则被称为高阶函数(higher order function)。

    因为术语非常重要,所以对相同功能的代码,我们进行变量的重命名,使其匹配它们所要阐述的概念:

    function add (x,y) {
      return x + y
    }
    
    function higherOrderFunction (x, callback) {
      return callback(x, 5)
    }
    
    higherOrderFunction(10, add)

    这种模式看上去应该是非常熟悉的,它到处可见。如果你曾经用过 JavaScript 的 Array 方法,那么你所使用的就是回调。如果你用过 lodash,那么你所使用的就是回调。如果你用过 jQuery,那么你所使用的也是回调。

    [1,2,3].map((i) => i + 5)
    
    _.filter([1,2,3,4], (n) => n % 2 === 0 );
    
    $('#btn').on('click', () =>
      console.log('Callbacks are everywhere')
    )

    一般而言,回调有两种常见的使用场景。首先,也就是我们在.map和 _.filter样例中所看到的,对于从一个值转换成另一个值的场景,这是一种非常好的抽象。我们可以说“这里有一个数组和一个函数。基于我给你的函数得到一个新的值”。其次,也就是我们在 jQuery 样例中所看到的,将函数的执行延迟至一个特定的时间。“这里有一个函数,当 id 为btn的元素被点击时,执行这个函数”。我们接下来会主要关注第二个使用场景,“将函数的执行延迟至一个特定的时间”。

    现在,我们只看到了同步操作的样例。正如我们在本文开始时提到的那样,我们所构建的大多数应用都不会将数据预先准备好,而是用户在与应用进行交互时,按需抓取外部的数据。通过上面的介绍,我们很快就能判断得出这个场景非常适合使用回调,因为它允许我们“将函数的执行延迟至一个特定的时间”。我们能够顺理成章的将这句话应用到数据获取的情景中。此时不再是将函数的执行延迟到一个特定的时间,而是将函数的执行延迟至我们得到了想要的数据之后。jQuery 的getJSON方法可能是这种模式最常见的样例:

    // updateUI 和 showError 的内容无关紧要。
    // 假定它们所做的工作与它们的名字相同。
    
    const id = 'tylermcginnis'
    
    $.getJSON({
      url: `https://api.github.com/users/${id}`,
      success: updateUI,
      error: showError,
    })

    在获取到用户的数据之前,我们是不能更新应用的 UI 的。那么我们是怎么做的呢?我们可以说,“这是一个对象。如果请求成功的话,那么调用success,并将用户的数据传递给它。如果请求没有成功的话,那么调用error并将错误对象传递给它。你不用关心每个方法是做什么的,只需要确保在应该调用它们的时候,去进行调用就可以了。这个样例完美地阐述了如何使用回调进行异步请求。

    到此为止,我们已经学习了回调是什么以及它如何为同步代码和异步代码带来收益。我们还没有讨论回调的阴暗面。看一下下面的代码,你能告诉我它都做了些什么吗?

    // updateUI、showError 和 getLocationURL 的内容无关紧要。
    // 假定它们所做的工作与它们的名字相同。
    
    const id = 'tylermcginnis'
    
    $("#btn").on("click", () => {
      $.getJSON({
        url: `https://api.github.com/users/${id}`,
        success: (user) => {
          $.getJSON({
            url: getLocationURL(user.location.split(',')),
            success (weather) {
              updateUI({
                user,
                weather: weather.query.results
              })
            },
            error: showError,
          })
        },
        error: showError
      })
    })

    [如果你需要帮助的话,可以参考一下这些代码的在线版本] (https://codesandbox.io/s/v06mmo3j7l)。

    注意一下,我们只是多添加了几层回调。首先,我们还是声明,如果不点击 id 为btn的元素,那么原始的 AJAX 请求就不会发送。一旦点击了按钮,我们会发起第一个请求。如果请求成功的话,我们会发起第二个请求。如果第二个请求也成功的话,那么我们将会调用updateUI方法,并将两个请求得到的数据传递给它。不管你一眼是否能够明白这些代码,客观地说,它要比之前的代码更加难以阅读。这也就涉及到所谓的“回调地狱”。

    作为人类,我们习惯于序列化的思考方式。如果在嵌套回调中依然还有嵌套回调的话,它会强迫我们背离自然的思考方式。当代码的阅读方式与你的思考方式脱节时,缺陷也就难以避免地出现了。

    与大多数软件问题的解决方案类似,简化“回调地狱”问题的一个常见方式就是对你的代码进行模块化。

    function getUser(id, onSuccess, onFailure) {
      $.getJSON({
        url: `https://api.github.com/users/${id}`,
        success: onSuccess,
        error: onFailure
      })
    }
    
    function getWeather(user, onSuccess, onFailure) {
      $.getJSON({
        url: getLocationURL(user.location.split(',')),
        success: onSuccess,
        error: onFailure,
      })
    }
    
    $("#btn").on("click", () => {
      getUser("tylermcginnis", (user) => {
        getWeather(user, (weather) => {
          updateUI({
            user,
            weather: weather.query.results
          })
        }, showError)
      }, showError)
    })

    [如果你需要帮助的话,可以参考一下这些代码的在线版本] (https://codesandbox.io/s/m587rq0lox)。

    好了,函数的名称能够帮助我们理解到底会发生什么,但客观地说,它真的“更好”了吗?其实也没好到哪里去。我们只是给回调地狱这个问题上添加了一块创可贴。问题依然存在,也就是我们会自然地按照顺序进行思考,即便有了额外的函数,嵌套回调也会打断我们顺序思考的方式。

    回调方式的另外一个问题与 控制反转(inversion of control) 有关。当你编写回调的时候,你会假设自己将回调交给了一个负责任的程序,这个程序会在(并且仅会在)应该调用的时候调用你的回调。你实际上将控制权交给了另外一个程序。当你在处理 jQuery、lodash 这样的库,甚至普通 JavaScript 时,可以安全地假设回调函数会在正确的时间以正确的参数进行调用。但是,对于很多第三方库来说,回调函数是你与它们进行交互的接口。第三方库很可能有意或无意地破坏与你的回调进行交互的方式。

    function criticalFunction () {
      // 这个函数一定要进行调用,并且要传入正确的参数
    }
    
    thirdPartyLib(criticalFunction)

    因为你不是调用criticalFunction的人,因此你完全无法控制它何时被调用以及使用什么参数进行调用。大多数情况下,这都不是什么问题,但是一旦出现问题的话,就不是什么小问题。

    Promise

    你有没有不预约就进入一家繁忙餐厅的经历?在这种情况下,餐厅需要有一种方式在出现空桌时能够联系到你。过去,他们只会把你的名字记录下来并在出现空桌的时候呼喊你的名字。随后,他们自然而然地寻找更有意思的方案。有一种方式是他们不再记录你的名字,而是记录你的电话号码,当出现空桌的时候,他们就可以为你发送短信。这样一来,你就可以离开最初的呼喊范围了,但是更重要的是,这种方式允许他们在任何时候给你的电话发送广告。听起来很熟悉吧?应该是这样的!当然也可能并非如此。这种方式可以用来类比回调。将你的电话号码告诉餐厅就像将你的回调函数交给第三方服务一样。你期望餐厅在有空桌的时候给你发送短信,同样我们也期望第三方服务在合适的时候以它们承诺的方式调用我们函数。但是,一旦电话号码或回调函数交到了他们的手里,我们就完全失去对它的控制了。

    幸好,还有另外一种解决方案。这种方案的设计允许你保留所有的控制权。你可能之前见过这种方式,那就是他们会给你一个蜂鸣器,如下所示。

    1447238-20181115151154120-1523819218.png

    如果你之前没有用过的话,它的想法其实非常简单。按照这种方式,他们不会记录你的名字或电话号码,而是给你一个这样的设备。当这个设备开始嗡嗡作响和发光时,就意味着有空桌了。在等待空桌的时候,你可以做任何你想做的事情,但此时你不需要放弃任何的东西。实际上,恰恰相反,是 他们 需要给 你 东西,这里没有所谓的控制反转。

    蜂鸣器一定会处于如下三种状态之一:pending、fulfilled或rejected。

    pending:默认状态,也是初始态。当他们给你蜂鸣器的时候,它就是这种状态。

    fulfilled:代表蜂鸣器开始闪烁,你的桌子已经准备就绪。

    rejected:如果蜂鸣器处于这种状态,则代表出现了问题。可能餐厅要打烊,或者他们忘记了晚上有人要包场。

    再次强调,你作为蜂鸣器的接收者拥有完全的控制权。如果蜂鸣器处于fulfilled状态,你就可以就坐了。如果它进入fulfilled状态,但是你想忽略它,同样也可以。如果它进入了rejected状态,这非常糟糕,但是你可以选择去其他地方就餐。如果什么事情都没有发生,它会依然处于pending状态,你可能吃不上饭了,但是同时也没有失去什么。

    现在,你已经掌握了餐厅蜂鸣器的事情,接下来,我们将这个知识用到其他重要的地方。

    如果说将电话号码告诉餐厅就像将回调函数交给他们一样的话,那么接受这个蜂鸣器就像我们所谓的“Promise”一样。

    像以往一样,我们首先从 为什么 开始。Promise 为什么会存在呢?它的出现是为了让异步请求所带来的复杂性更容易管理。与蜂鸣器非常类似,Promise会处于如下三种状态中的某一种: pending、fulfilled或rejected。但是与蜂鸣器不同,这些状态代表的不是饭桌的状态,它们所代表的是异步请求的状态。

    如果异步请求依然还在进行,那么Promise的状态会是pending。如果异步请求成功完成的话,那么Promise会将状态转换为fulfilled。如果异步请求失败的话,Promise会将状态转换为rejected。蜂鸣器的比喻非常贴切,对吧?

    理解了 Promise 为什么会存在以及它们的三种不同状态之后,我们还要回答三个问题:

    如何创建 Promise?

    如何改变 Promise 的状态?

    如何监听 Promise 状态变化的时间?

    #### 1)如何创建 Promise?
    这个问题非常简单,你可以使用new创建Promise的一个实例:

    const promise = new Promise()

    ####2)如何改变 Promise 的状态?
    Promise的构造函数会接收一个参数,这个参数是一个(回调)函数。该函数会被传入两个参数resolve和reject。

    resolve:一个能将 Promise 状态变为fulfilled的函数;

    reject:一个能将 Promise 状态变为rejected的函数;

    在下面的代码中,我们使用setTimeout等待两秒钟然后调用resolve,这样会将 Promise 的状态变为fulfilled:

    const promise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve() // 将状态变为“fulfilled”
      }, 2000)
    })

    分别用日志记录刚刚创建之时和大约两秒钟之后resolve已被调用时的 Promise,我们可以看到状态的变化了:

    <iframe height=500 width=800 src="https://wx4.sinaimg.cn/mw690/72776b8cgy1fx3ekzi7scg20vs0k2nph.gif"></iframe>

    请注意,Promise 从变成了。

    3)如何监听 Promise 状态变化的时间?

    我认为,这是最重要的一个问题。我们已经知道了如何创建 Promise 和改变它的状态,这非常棒,但是如果我们不知道如何在状态变化之后做一些事情的话,这其实是没有太大意义的。

    我们还没有讨论的一件事就是 Promise 到底是什么。当我们通过new Promise创建 Promise 的时候,你实际创建的只是一个简单的 JavaScript 对象,这个对象可以调用两个方法then和catch。这是关键所在,当 Promise 的状态变为fulfilled的时候,传递给.then的函数将会被调用。如果 Promise 的状态变为rejected,传递给.catch的函数将会被调用。这就意味着,在你创建 Promise 的时候,要通过.then将你希望异步请求成功时调用的函数传递进来,通过.catch将你希望异步请求失败时调用的函数传递进来。

    看一下下面的样例。我们依然使用setTimeout在两秒钟(2000 毫秒)之后将 Promise 的状态变为fulfilled:

    function onSuccess () {
      console.log('Success!')
    }
    
    function onError () {
      console.log('?')
    }
    
    const promise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve()
      }, 2000)
    })
    
    promise.then(onSuccess)
    promise.catch(onError)

    如果你运行上述代码,会发现大约两秒钟之后,将会在控制台上打印出“Success!”。出现这样的结果主要有两个原因。首先,当我们创建 Promise 的时候,会在大约 2000 毫秒之后调用resolve,这会将 Promise 的状态变为fulfilled。其次,我们将onSuccess函数传递给 Promise 的.then。通过这种方式,我们告诉 Promise 在状态变成fulfilled的时候(也就是大约 2000 毫秒之后)调用onSuccess。

    现在,我们假设发生了意料之外的事情,需要将 Promise 的状态变成rejected。这次,我们不再调用resolve,而是应该调用reject:

    function onSuccess () {
      console.log('Success!')
    }
    
    function onError () {
      console.log('?')
    }
    
    const promise = new Promise((resolve, reject) => {
      setTimeout(() => {
        reject()
      }, 2000)
    })
    
    promise.then(onSuccess)
    promise.catch(onError)

    这一次,调用的就不是onSuccess函数了,而是onError函数,这是因为我们调用了reject。

    现在,你已经掌握了 Promise API 相关的知识,现在我们开始看一下真正的代码。

    还记得我们之前看到的异步回调样例吗?

    function getUser(id, onSuccess, onFailure) {
      $.getJSON({
        url: `https://api.github.com/users/${id}`,
        success: onSuccess,
        error: onFailure
      })
    }
    
    function getWeather(user, onSuccess, onFailure) {
      $.getJSON({
        url: getLocationURL(user.location.split(',')),
        success: onSuccess,
        error: onFailure,
      })
    }
    
    $("#btn").on("click", () => {
      getUser("tylermcginnis", (user) => {
        getWeather(user, (weather) => {
          updateUI({
            user,
            weather: weather.query.results
          })
        }, showError)
      }, showError)
    })

    这里我们能用 Promise API 的方式替换回调吗?如果我们将 AJAX 请求包装到 Promise 中会怎么样呢?如果能这样的话,我们就可以根据请求执行的情况简单地调用resolve或reject。让我们从getUser入手:

    function getUser(id) {
      return new Promise((resolve, reject) => {
        $.getJSON({
          url: `https://api.github.com/users/${id}`,
          success: resolve,
          error: reject
        })
      })
    }

    非常好!请注意,getUser的参数发生了变化。从接收id、onSuccess和onFailure变成了只接收id。这里不再需要这两个回调函数了,因为我们不必再将控制权转移出去了。相反,我们在这里使用了 Promise 的resolve和reject函数。如果请求成功的话,将会调用resolve,如果出现错误的话,将会调用reject。

    接下来,我们重构getWeather。我们按照相同的策略,将onSuccess和onFailure回调函数替换为resolve和reject。

    function getWeather(user) {
      return new Promise((resolve, reject) => {
        $.getJSON({
          url: getLocationURL(user.location.split(',')),
          success: resolve,
          error: reject,
        })
      })
    }

    看上去非常不错!我们需要更新的最后一个地方就是点击处理器。需要记住,我们想要的处理流程如下所示:

    通过 Github API 获取用户的信息;

    使用用户的地理位置,通过 Yahoo Weather API 获取其天气情况;

    根据用户信息和天气信息更新 UI。

    我们从第一步开始:通过 Github API 获取用户的信息。

    $("#btn").on("click", () => {
      const userPromise = getUser('tylermcginnis')
    
      userPromise.then((user) => {
    
      })
    
      userPromise.catch(showError)
    })

    注意,getUser不再接收两个回调函数,它为我们返回的是一个 Promise,基于该 Promise,我们可以调用.then和.catch。如果调用.then的话,会将用户信息传递给它。如果调用.catch的话,会将错误信息传递给它。

    接下来,让我们实现第二步:使用用户的地理位置获取其天气。

    $("#btn").on("click", () => {
      const userPromise = getUser('tylermcginnis')
    
      userPromise.then((user) => {
        const weatherPromise = getWeather(user)
        weatherPromise.then((weather) => {
    
        })
    
        weatherPromise.catch(showError)
      })
    
      userPromise.catch(showError)
    })

    注意,我们采取了与第一步完全相同的模式,只不过调用getWeather的时候,我们将userPromise得到的user传递了进去。

    最后,在第三步中我们使用用户信息及其天气信息更新 UI。

    $("#btn").on("click", () => {
      const userPromise = getUser('tylermcginnis')
    
      userPromise.then((user) => {
        const weatherPromise = getWeather(user)
        weatherPromise.then((weather) => {
          updateUI({
            user,
            weather: weather.query.results
          })
        })
    
        weatherPromise.catch(showError)
      })
    
      userPromise.catch(showError)
    })

    在该 地址 有完整的源码,你可以进行尝试。

    我们的新代码已经好多了,但是依然可以做一些改善。但是在进行改善之前,你需要了解 Promise 的另外两个特性,那就是链接(chaining)以及从resolve中给then传递参数。

    链接

    .then和.catch都会返回一个新的 Promise。这看上去像是一个很小的细节,但其实是非常重要的,因为这意味着 Promise 能够链接起来。

    在下面的样例中,我们调用getPromise,它会返回一个 Promise,这个 Promise 会在 2000 毫秒之后进行resolve。从这里开始,因为.then也将返回一个 Promise,所以我们就可以将多个.then链接起来,直到我们抛出一个new Error,而这个错误将会被.catch方法捕获。

    function getPromise () {
      return new Promise((resolve) => {
        setTimeout(resolve, 2000)
      })
    }
    
    function logA () {
      console.log('A')
    }
    
    function logB () {
      console.log('B')
    }
    
    function logCAndThrow () {
      console.log('C')
    
      throw new Error()
    }
    
    function catchError () {
      console.log('Error!')
    }
    
    getPromise()
      .then(logA) // A
      .then(logB) // B
      .then(logCAndThrow) // C
      .catch(catchError) // Error!

    这样非常酷,但为什么它如此重要呢?还记得在讨论回调的章节中,我们讨论了回调的劣势之一就是它强迫我们背离自然、序列化的思考方式。当我们将 Promise 链接起来的时候,它不会再强迫我们背离自然的思考方式,因为链接之后的 Promise 是序列化的,也就是运行getPromise,然后运行logA,然后运行logB……

    我们看另外一个样例,这是使用fetch API 时很常见的场景。fetch将会为我们返回一个 Promise,它会解析为 HTTP 响应。为了得到实际的 JSON,我们还需要调用.json。因为这种链接的方式,我们可以按照序列化的方式进行思考:

    fetch('/api/user.json')
      .then((response) => response.json())
      .then((user) => {
        // user 现在已经准备就绪了。
      })

    现在我们已经明白了链接的方式,接下来我们使用它来重构之前使用的getUser/getWeather代码。

    function getUser(id) {
      return new Promise((resolve, reject) => {
        $.getJSON({
          url: `https://api.github.com/users/${id}`,
          success: resolve,
          error: reject
        })
      })
    }
    
    function getWeather(user) {
      return new Promise((resolve, reject) => {
        $.getJSON({
          url: getLocationURL(user.location.split(',')),
          success: resolve,
          error: reject,
        })
      })
    }
    
    $("#btn").on("click", () => {
      getUser("tylermcginnis")
        .then(getWeather)
        .then((weather) => {
          // 在这里我们同时需要 user 和 weather
          // 目前我们只有 weather
          updateUI() // ????
        })
        .catch(showError)
    })

    这看起来好多了,但是现在我们遇到了另外一个问题。你发现了吗?在第二个.then中,我们想要调用updateUI。这里的问题在于我们需要为updateUI同时传递user和weather。按照我们目前的做法,我们只能接收到weather,而没有user。我们需要想出一种办法,让getWeather在resolve时能够同时得到user和weather。

    问题的关键在于resolve只是一个函数。你传递给它的任何参数都会往下传递给.then所指定的函数。这意味着,在getWeather中,如果我们自行调用resolve的话,就可以同时将weather和user。然后,在链中的第二个.then方法中,就可以同时接收到weather和user。

    function getWeather(user) {
      return new Promise((resolve, reject) => {
        $.getJSON({
          url: getLocationURL(user.location.split(',')),
          success(weather) {
            resolve({ user, weather: weather.query.results })
          },
          error: reject,
        })
      })
    }
    
    $("#btn").on("click", () => {
      getUser("tylermcginnis")
        .then(getWeather)
        .then((data) => {
          // Now, data is an object with a
          // "weather" property and a "user" property.
    
          updateUI(data)
        })
        .catch(showError)
    })

    你可以在该 地址 查看最后的代码。

    在点击处理逻辑中,与回调方式进行对比,我们就能看出 Promise 的威力。

    // Callbacks ?
    getUser("tylermcginnis", (user) => {
      getWeather(user, (weather) => {
        updateUI({
          user,
          weather: weather.query.results
        })
      }, showError)
    }, showError)
    
    
    // Promises ✅
    getUser("tylermcginnis")
      .then(getWeather)
      .then((data) => updateUI(data))
      .catch(showError);

    此时逻辑感觉非常自然,因为它就是我们所习惯的序列化思考方式。getUser,然后getWeather,然后使用得到的数据更新 UI。

    显而易见,Promise 能够显著提升异步代码的可读性,但是有没有能让它更好的方式呢?假设你是 TC39 委员会的成员,拥有为 JavaScript 语言添加新特性的权力。那么,你认为下面的代码还能怎样进行优化?

    $("#btn").on("click", () => {
      getUser("tylermcginnis")
        .then(getWeather)
        .then((data) => updateUI(data))
        .catch(showError)
    })

    正如我们在前面所讨论的,这个代码已经非常好了。与我们大脑的思考方式相同,它是序列化顺序的。我们所遇到的问题就是需要将数据(users)从第一个异步请求一直传递到最后一个.then。这并不是什么大问题,但是需要我们修改getWeather才能往下传递users。如果我们想完全按照编写同步代码的方式来编写异步代码会怎样进行处理呢?如果我们真的能做到这一点,这个问题将会彻底消失,它看上去就完全是序列化的了。如下是可能的一种实现方式:

    $("#btn").on("click", () => {
      const user = getUser('tylermcginnis')
      const weather = getWeather(user)
    
      updateUI({
        user,
        weather,
      })
    })

    这看上去非常棒,我们的异步代码与同步代码完全相同。我们的大脑无需任何额外的步骤,因为这就是我们已经习以为常的思考方式。但令人遗憾的是,这样显然无法正常运行。我们都知道,user和weather仅仅是getUser和getWeather所返回的 Promise。但是不要忘记,我们现在是 TC39 的成员,有为语言添加任何特性的权力。这样的代码很难运行,我们必须教会 JavaScript 引擎区分异步函数调用和常规同步函数调用之前的差异。我们接下来添加几个关键字,让引擎运行起来更加容易。

    首先,我们添加一个关键字到主函数上。这会提示引擎,我们会在这个函数中添加一些异步的方法调用。我们使用async来达到这一目的。

    $("#btn").on("click", async () => {
      const user = getUser('tylermcginnis')
      const weather = getWeather(user)
    
      updateUI({
        user,
        weather,
      })
    })

    很好!这看上去是非常合理的。接下来,我们添加另外一个关键字,让引擎能够知道哪个函数调用是异步的,函数所返回的是 Promise。这里我们使用await,这就相当于说“嗨,引擎。这个函数是异步的并且会返回 Promise。你不能按照惯常的方式来执行,你需要等待 Promise 的最终值,然后才能继续运行”。在新的async和await就绪之后,代码将会变成下面的样子。

    $("#btn").on("click", async () => {
      const user = await getUser('tylermcginnis')
      const weather = await getWeather(user.location)
    
      updateUI({
        user,
        weather,
      })
    })

    这种方式相当有吸引力。我们有了一种合理的方式,让异步代码的外表和行为完全和同步代码一致。接下来,就该让 TC39 的人相信这是一个好办法。你可能已经猜到了,我们并不需要说服他们,因为这已经是 JavaScript 的组成部分之一了,也就是所谓的Async/Await。

    你还不相信吗?[该地址] (https://codesandbox.io/s/00w10o19xn) 展现了添加 Async/Await 之后的实际代码,你尽可以进行尝试。

    异步函数会返回 Promise

    现在,我们已经看到了 Async/Await 所能带来的收益。接下来,我们讨论几个更小的细节,掌握它们也是非常重要的。首先,只要你为函数添加async,它就会隐式的返回一个 Promise:

    async function getPromise(){}
    
    const promise = getPromise()

    尽管getPromise实际上空的,但是它依然会返回一个 Promise,因为它是一个async函数。

    如果async函数有返回值的话,它也将会包装到一个 Promise 中。这意味着,你必须要使用.then来访问它。

    async function add (x, y) {
      return x + y
    }
    
    add(2,3).then((result) => {
      console.log(result) // 5
    })

    不能将 await 用到非 async 的函数中

    如果你将await用到非async的函数中,那么将会出现错误。

    $("#btn").on("click", () => {
      const user = await getUser('tylermcginnis') // SyntaxError: await is a reserved word
      const weather = await getWeather(user.location) // SyntaxError: await is a reserved word
    
      updateUI({
        user,
        weather,
      })
    })

    关于这一点,我认为,当你将async添加到一个函数上的时候,它会做两件事,首先它会让这个函数本身返回一个 Promise(或者将返回的内容包装到 Promise 中),其次,它会确保你能够在这个函数中使用await。

    错误处理

    你可能发现,我们的代码有一点作弊。在原始的代码中,我们可以通过.catch捕获所有的错误。在切换到 Async/Await 之后,我们移除了那些代码。在使用 Async/Await 时,最常用的方式就是将你的代码包装到一个try/catch中,这样就能捕获错误了。

    $("#btn").on("click", async () => {
      try {
        const user = await getUser('tylermcginnis')
        const weather = await getWeather(user.location)
    
        updateUI({
          user,
          weather,
        })
      } catch (e) {
        showError(e)
      }
    })

    转载
    原文链接

    转载于:https://www.cnblogs.com/wenjuan-blog/p/9963621.html

    展开全文
  • 结合近年来对永夏煤田地质研究的成果以及地质资料,运用地质构造逐级控制理论,按照煤田所属关系分析了永夏煤田构造演化特征以及构造分布特征,研究了区域构造演化史对煤层瓦斯赋存的影响。研究表明:永夏矿区所在的鲁...
  • 为了研究沁水盆地构造热演化史,需要恢复不同地史时期的古地温梯度、煤系古地温和古埋深。在分析研究各种已有方法优缺点的基础上,根据沁水盆地的实际,总结出了一套较为完善的恢复地热史的方法:即以现今地温梯度将今论...
  • 在系统研究黔西滇东地区构造特征、含煤地层发育及受热背景等基础上,运用BasinMod盆地模拟技术,探讨了格目底向斜及恩洪盆地含煤地层的埋藏、热演化史,揭示出含煤地层成熟演化的阶段性。研究表明:黔西滇东地区煤变质...
  • 依据区域地质调查成果、煤田地质勘查报告以及钻孔等资料,采用回剥技术分析了研究区中生代以来的沉降及构造演化特征。沉降模拟结果表明:侏罗纪缓慢沉降,至早白垩世快速大幅沉降,燕山运动导致晚白垩世整体隆升,...
  • 由于区域性地史演化较为稳定,油藏在成藏之后没有发生大的破坏,所以油藏保存条件普遍较好,该因素对油藏的分布不起主要控制作用。根据上述油藏控制因素分析认为,富县探区中南部2个区块不但砂体发育,而且西邻长7段烃源...
  • 本文做的是一维双色元胞自动机,可以简单阐述为,给个一维数组,按照某种演化规则,可以得到这个一维数组进行一次演化、二次演化后得到的一维数组,代码要实现的就是实现初始一维数组的演化,得到演化后的数组。...

    背景

    元胞自动机

    元胞自动机在很多方面有应用,这是非常有趣的地方,世界上很多的看似没有规律的东西,都可以用这个模型去模拟它潜在的规律。
    本文做的是一维双色元胞自动机,可以简单地阐述为,给个一维数组,按照某种演化规则,可以得到这个一维数组进行一次演化、二次演化后得到的一维数组,代码要实现的就是实现初始一维数组的演化,得到演化后的数组。
    具体的话可以看以下的文章,作者写的很好,一下子能看懂:
    元胞自动机基础知识
    另外,学院派一些,更为具体的理论知识可以看:
    元胞自动机理论
    还有元胞自动机应用实现的小游戏:
    生命游戏

    代码分析

    主要应用到的还是字符串的操作、比特位操作以及二维数组的定义、调用等操作。可以对这个模块进行复习,尤其字符串的操作有很多细节的东西容易忽略,比如说strcmp函数操作是遇到'\0'的时候停止的,如果直接定义一个装满字符的二维数组,字符串操作函数和输出函数都会遇到过度访问的问题。
    另外,二维数组的定义还有调用,也有很多要注意的地方,和一维数组操作相比,几乎有质的变化。
    除了代码上应该注意的问题,还有元胞自动机的模型上的问题要去注意。解决问题的思路:由物理模型抽象为数学模型,再由数学模型进一步分析成为离散数学模型,翻译成为代码语言很重要。对于问题透彻的理解以及对于模型的把握,有助于后期代码的改进、修正以及模型的复杂化。

    模型分析

    要得到一维元胞自动机系统,必须完整地描述这个系统:

    A={spacelengthdimension,state,radius,{Siradiust,...,Sit,...,Si+radiust},function} A=\{spacelength_{dimension}, state,radius,\{S^{t}_{i-radius},...,S^{t}_i,...,S^{t}_{i+radius}\},function\}
    spacelengthdimensionspacelength_{dimension}代表元胞空间的长度以及维度。64164_{1}表示一维数组长度为64
    statestate表示每个空间对应的细胞可能的状态,在本篇文章中设置为state={0,1}state=\{0,1\}radiusradius表示的是半径,即数组演化过程与周围多大半径的数组元素相关。我设置的是radius=1radius=1其中,{Siradiust,...,Sit,...,Si+radiust}\{S^{t}_{i-radius},...,S^{t}_i,...,S^{t}_{i+radius}\}表示的就是需要带入演化规则的数组元素的集合。
    用这样一个集合,即可完整地描述一个元胞空间以及对应地规则了。但同时还要考虑边界的问题,本文由于设置的半径为1,边界即数组的第0个以及最后一个,由于它们俩没有对应的左右关于半径对称的元素,所以,需要单独列出讨论。边界条件的设置主要有以下几种:周期型、反射型、定值型。具体可以看元胞自动机理论

    函数分析

    main函数

    初始化二维数组cellbuf,输入cellbuf[0][space];
    输入规则二进制编码;
    FOR time=0->time_length-1
    {
    	将原始cellbuf[time][space]带入演化函数,
    	得到cellbuf[time+1][space];
    }
    输出cellbuf[time][space]//用两个for循环输出
    

    演化函数

    BLOCK 1://讨论边界,取状态数组,为演化做准备
    situation_t[time][4]存储领域以及中心点状态值,边界分开讨论;
    //注意最后得加个'\0',不然后面用strcmp会出错
    END BLOCK 1;
    BLOCK 2://演化规则函数
    输入规则字符串choice_buf[9];
    根据获取的状态字符串数组进行判断,并根据规则输出下个时刻的字符串数组;
    END BLOCK 2;

    实现效果

    输入规则30输出数组如下:
    在这里插入图片描述

    主要问题

    字符串的操作

    定义和初始化数组:

    char c[10] = “China”;
    其实实际上是占6个字符长度的字符串,包括’\0’。所以当初始化的字符串大于或等于10个,将没有’0’这个字符串结束标志。那么在使用很多字符串操作函数的时候会出错。
    因为’\0’是程序中检测字符串结束的标志,而不是依靠字符串长度。

    字符数组的赋值与引用

    只能对字符数组的元素赋值,而不能用赋值语句对整个数组赋值。

    字符串处理函数

    很多字符串处理函数是以’\0’为结束标志的,所以字符串数组要特别注意长度问题。

    比特位操作

    如果不想在字符串上进行操作,也可以将数字字符串转化为二进制或者十进制处理,那么将用到strtol这个函数具体可以看这个函数的使用。

    二维数组的调用

    具体格式如下:
    需要注意,第二个参数的长度要标上。char test(char a[][5])或者char test(char [][5])也可以写成char test(*a)[5]
    因为从实参传递来的是数组的起始地址,在内存中按数组排列规则存放(按行存放),而并不区分行和列,如果在形参中不说明列数,则系统无法决定应为多少行多 少列,不能只指定一维而不指定第二维。

    展开全文
  • 我们相信,对于一家互联网公司来说,它的产品发展和演化史,会更忠实映射出它的发展和成长轨迹。 我们也相信,去了解一家互联网公司的产品发展、迭代和演化,会更有助于你理解互联网,理解产品。 2天前,...
  • 18世纪生物学家拉马克提出了生物上一个伟大的概念——用进废退,尽管该理论被认为不是最严谨的,但它为后来达尔文正式提出进化论提供了参考方向。进化论被提出来的时候很多人都持怀疑态度,慢慢大家都接受了,...
  • 一个程序员的成长

    2012-04-21 15:47:39
    一直以来我是这样一个自己,这样的不停为着梦想而努力着。 这是第一次,可是不是刚刚开始! 自报考结束以来,踏上了软件这一行已有近两年了。不得不说是阴差阳错的走上了自己喜欢的道路。且不说软件行业如今如何...
  • 因为个人喜好以及专业原因随手写了此文,还请各位大佬赐教与支持,权当抛砖引玉了...机载雷达从早期的简易对空搜索和测距功能演化到现在不仅要兼顾大区域范围内的搜索、跟踪以及火控制导甚至还要对测绘,sar成像等...
  • 结果表明:构造演化史直接控制着煤储层埋藏及生烃,是煤层气成藏主控因素;地下水水动力特征和围岩封闭特征决定了煤层气藏的保存条件,是煤层气成藏的关键;综合以上特征,可以得出2#煤煤层气藏为单斜-水动力封堵型...
  • 文中根据目前流体包裹体均一温度研究成藏期方法的特点,在塔里木盆地温演化过程分析的基础上,提出了流体包裹体样品埋深范围在150 m以内的选取原则,统计了典型井主要含油层位样品的流体包裹体均一温度,并以5℃的温度...
  • 进一步,结合人类发展和计算机世界演化史来考察软件工程的发展。 表2 软件工程过程模型 表2将软件工程的主要过程模型做了一个简要的汇总,并将所有的软件工程模型划分为三大领域和五个阶段,三大领域分别...
  • 并指出了人工合成烃类包裹体的发展趋势,即在结合烃源岩的生烃、构造演化史和储层演化史的基础之上,充分发挥试验分析的优势,着重对包裹体后期岩相学应变及再平衡进行研究,才能最大限度提高分析的准确性,从而提高...
  • 么学研地史任务究的是什。么学的地史内容研究是什。有体篇码是网掩位被A类子网地址的子划分一个0有用来。“组地层单位岩石。刺诗么季在什高含量营养盐的一般节最。先秦现高:度升溶解含量氧的一般随温。忠精...
  • 白垩纪放射虫最早是从西藏亚东古纳普古鲁普地区哲普尔组始新世砂页岩中报道的。 尽管保存不善,仍然鉴定了14种放射虫属的24种,清楚... 对这些放射成虫的地层成因的研究将为藏特提斯的古环境和较晚的演化史提供启示。
  • 结合实际资料,分别针对TDI计算过程中的去压实恢复、压实系数与地表孔隙度的求取、超压和地层剥蚀恢复等因素,进行了不同计算条件下埋藏史演化曲线和TDI值的分析比较。结果表明:如果考虑压实作用的影响,将会使TDI值...
  • 在东汤峪地区已有地质工作的基础上,进一步研究了区内地质构造特征,着重分析了山前断裂的构造特征及演化史,认为山前断裂经历了挤压和拉张交替作用的多期(次)活动过程;并探讨了该区温泉的构造控制因素,认为东汤峪...

空空如也

空空如也

1 2 3 4
收藏数 64
精华内容 25
关键字:

地史演化