编程原理—如何用javascript代码解决一些问题
关于编程,我最喜欢的就是解决问题。我不相信有谁天生具有解决问题的能力。这是一种通过反复锻炼而建立并维持的能力。像任何练习一样,有一套指导方针可以帮助你更有效地提高解决问题的能力。我将介绍5个最重要的软件设计原则,这些原则指导了我的解决问题的过程,并向您展示如何应用这些原则来解决实际问题。
1. Keep It Simple
如果您有任何需要从这篇文章中获得的信息,那就是“保持简单”的原则。这个原则通常被称为KISS,它代表“Keep it simple,stupid”。
仅仅因为我们必须解决的问题很复杂,并不意味着我们的解决方案必须很复杂。
一般来说,我们通过将复杂问题分解为简单的子问题,来开发解决复杂问题的简单解决方案,以便我们可以轻松解决这些问题。
来看一个简单的例子:
假设你要写一个函数,来检查一个字符串中间的括号的对称匹配。例如,只有一个’(‘ 将会返回 false,只有一个’)(‘ 将会返回 false,’()’ 将会返回true, 然后字符串’foo’将会返回 true。
所以,我们怎么做?
第一步将应用 KISS 原则, 为算法来写出一个概要。
给到一个字符串, 是由一些字符组成。
- 从最左边一个字符开始,然后查看后面的每一个字符。
- 记录每一个出现的 ( .
- 记录出现的 )来终止之前的 ( .
- 忽视那些不是 ( 或者 )的字符 .
- 如果只有一个 ) 而没有( , 返回 false.
- 一旦我们完成了最后一个字符的观测, 如果没有剩余单独的 ( 或者 ) ,返回 true.
2. Separation of Concerns (SoC)
我们的算法有三个逻辑域:
Continue or Stop? — 是否继续观测字符,或停止并返回真或假的逻辑。
Parentheses Tally —用于统计( 和 )字符出现的逻辑。
What to Look At Next? — 决定接下来观测什么的逻辑。
让我们将上面的六个步骤映射到以下三个逻辑域:
Continue or Stop? | Parentheses Tally | What to Look At Next |
---|---|---|
如果只有一个 ) 而没有( , 返回 false. | 记录每一个出现的 ( . | 从最左边一个字符开始,然后查看后面的每一个字符。 |
一旦我们完成了最后一个字符的观测, 如果没有剩余单独的 ( 或者 ) ,返回 true. | 记录出现的 )来终止之前的 ( | |
忽视那些不是 ( 或者 )的字符 |
我们刚刚做的是我们将算法分为三个逻辑域,每个域都处理特定的问题。每个领域特定的问题都可以由软件组件独立解决。这三个软件组件都是为完成特定任务而设计的,无需关心其他组件正在做什么。这表明了关注点分离或所谓SoC的原则。
SoC是开发具有许多变动部分的复杂应用程序或软件系统的关键。Web应用程序会分别介绍表示形式(网页的外观),业务逻辑(网页内容)和内容交付(通过JSON API访问资源,查询数据库等)之间的关系。
Poorly executed SoC
我见过设计不佳的软件,其中软件虽然分为不同的域,但关注点没有分离。考虑下面的例子:
Write a function foo that doubles then increments every number in an array of numbers.
1 |
// Helper Functions |
为什么这是一个 SoC 失败的案例 ?
好吧,double方法 和 increment方法 都涉及遍历数组来修改每个元素。注意 double方法 和 increment方法 函数看起来几乎完全相同除了它们如何修改数组元素的不一样之外。
这被称为 boilerplate 。
只要你的代码中有 boilerplate 文件,你就知道可以在关注分离点时有更好的提升空间。
这涉及到下一个原则:
3. Don’t Repeat Yourself (DRY)
不要重复自己(DRY)原则和 SoC 是共存的。 DRY原则旨在通过形成抽象来减少软件中的重复和boilerplate,我可以写出一篇关于抽象的单独文章,但这里是如何应用抽象来编写不重复的代码的关键点:
- 创建通用的软件模式的函数。我们称之为高阶函数。
- 使用高级函数来替换你代码中的boilerplate
高阶函数的一些例子包括:
map
- 根据给定规则修改数组中的每个元素。filter
- 获取通过给定条件的数组的子集reduce
- 根据给定的规则将数组中的所有内容组合在一起
让我们应用 DRY 原理并重写foo
以使用 map
高阶函数来抽取 boilerplate:
1 |
// Well executed separation of concerns and DRY code const double = elem => elem * 2 |
好的,这是一个漫长的弯路。我们可以回到hasBalancedParen问题吗?
是! 我们即将达成最终解决方案,但还有一个我们需要的原则:
4. Divide and Conquer (分治算法)
当您在算法设计的背景下听到“Divide and Conquer”的时候,请考虑递归。术语-递归-有数学基础,通常需要重复应用规则来更新自己,以创建一个无限序列,序列中的下一个事物依赖于序列中的上一个事物。例如,分形,斐波那契数列和帕斯卡三角形都是由递归构造。
对于算法中的分治,我们感兴趣的是重复应用规则,而不是构建一个序列,我们想要解构问题空间并最终返回最终解决方案。在如下代码中,分治算法的递归函数具有一个基本结构:
1 |
function recursiveFun(input, output) { |
重点是简化输入(参数)直到问题变得非常简单去解决那么我们就能立即在函数的最开始返回结果。这也是所谓的 base case .(个人理解为最低级的条件,最少也要满足的条件)。如果我们忘记给递归函数添加一个 base case 会如何? 很简单,内存爆炸。
注意,我们并不需要去使用递归函数来实现分治算法。我们可以使用一个循环以及一个可变数据结构。我并不是可变数据结构的爱好者,虽然它有时候效率更高。
让我们看看一个简单的例子关于使用递归函数和一个可变数据结构来实现分治:
Option 1: Recursive Function
1 |
function sum(arr) { function add(arr, sum) { |
Option 2: Mutable Data Structure
1 |
function sum(arr) { // Initial Values |
函数式编程和单行程序的爱好者将使用更高阶的函数reduce
执行如下,JavaScript为您提供Array原型的开箱即用功能:
const sum = (arr) => arr.reduce((res, elem) => res + elem, 0)
使用分治,我们有了解决最后一个重要问题的原则,来解决 hasBalancedParen 问题:
1 |
// Parentheses Tally |
编程原理—如何用javascript代码解决一些问题的更多相关文章
- JavaScript异步编程原理
众所周知,JavaScript 的执行环境是单线程的,所谓的单线程就是一次只能完成一个任务,其任务的调度方式就是排队,这就和火车站洗手间门口的等待一样,前面的那个人没有搞定,你就只能站在后面排队等着. ...
- 《浏览器工作原理与实践》<08>调用栈:为什么JavaScript代码会出现栈溢出?
在上篇文章中,我们讲到了,当一段代码被执行时,JavaScript 引擎先会对其进行编译,并创建执行上下文.但是并没有明确说明到底什么样的代码才算符合规范. 那么接下来我们就来明确下,哪些情况下代码才 ...
- 一步一步学Silverlight 2系列(22):在Silverlight中如何用JavaScript调用.NET代码
概述 Silverlight 2 Beta 1版本发布了,无论从Runtime还是Tools都给我们带来了很多的惊喜,如支持框架语言Visual Basic, Visual C#, IronRuby, ...
- 《浏览器工作原理与实践》<07>变量提升:JavaScript代码是按顺序执行的吗?
讲解完宏观视角下的浏览器后,从这篇文章开始,我们就进入下一个新的模块了,这里我会对 JavaScript 执行原理做深入介绍. 今天在该模块的第一篇文章,我们主要讲解执行上下文相关的内容.那为什么先讲 ...
- JS函数 编程练习 使用javascript代码写出一个函数:实现传入两个整数后弹出较大的整数。
编程练习 使用javascript代码写出一个函数:实现传入两个整数后弹出较大的整数. 任务 第一步: 编写代码完成一个函数的定义吧. 第二步: 我们来补充函数体中的控制语句,完成函数功能吧. 提示: ...
- 如果让莎士比亚、海明威编写JavaScript代码
本文作者Angus Croll是Twitter工程师.JavaScript迷.文学迷,并且非常喜欢作家海明威.他在梦中"梦见"一些名人编写JavaScript代码,不同的作家呈现出 ...
- 编写快速、高效的JavaScript代码
许多Javascript引擎都是为了快速运行大型的JavaScript程序而特别设 计的,例如Google的V8引擎(Chrome浏览器,Node均使用该引擎).在开发过程中,如果你关心你程序的内存和 ...
- [书籍翻译] 《JavaScript并发编程》 第二章 JavaScript运行模型
本文是我翻译<JavaScript Concurrency>书籍的第二章 JavaScript运行模型,该书主要以Promises.Generator.Web workers等技术来讲解J ...
- 新书《编写可测试的JavaScript代码 》出版,感谢支持
本书介绍 JavaScript专业开发人员必须具备的一个技能是能够编写可测试的代码.不管是创建新应用程序,还是重写遗留代码,本书都将向你展示如何为客户端和服务器编写和维护可测试的JavaScript代 ...
随机推荐
- sklearn KMeans聚类算法(总结)
基本原理 Kmeans是无监督学习的代表,没有所谓的Y.主要目的是分类,分类的依据就是样本之间的距离.比如要分为K类.步骤是: 随机选取K个点. 计算每个点到K个质心的距离,分成K个簇. 计算K个簇样 ...
- JavaSE--RMI初识
转自:http://blog.csdn.net/guyuealian/article/details/51992182 一.Java RMI机制: 远程方法调用RMI(Remote Me ...
- MySQL--OPTIMIZE TABLE碎片整理
参考:http://blog.51yip.com/mysql/1222.html BLOB和TEXT值会引起一些性能问题,特别是在执行了大量的删除操作时.删除操作会在数据表中留下很大的空洞,以后填入这 ...
- Python笔记_第三篇_面向对象_8.对象属性和类属性及其动态添加属性和方法
1. 对象属性和类属性. 我们之前接触到,在类中,我们一般都是通过构造函数的方式去写一些类的相关属性.在第一次介绍类的时候我们把一些属性写到构造函数外面并没有用到构造函数,其实当时在写的时候,就是在给 ...
- Python笔记_第一篇_面向过程_第一部分_5.Python数据类型之元组类型(tuple)
元组!在Python中元组是属于列表的一种延伸,也是一种有序集合,成为一种只读列表,即数据可以被查找,不能被修改,列表的切片操作同样适用于元组. 特点:1. 与列表非常相似. 2. 一旦初始化就不能修 ...
- python文件读写 文件修改
#设置一个变量f为文件对象,并打开文件#写文件#f = open('user.txt','w',encoding='utf-8') #f是一个文件对象f=open(r'c:\Users\PL\Desk ...
- 600E - Lomsat gelral(找子树多颜色问题)(入门)
题:https://codeforces.com/problemset/problem/600/E 题意:一棵树有n个结点,每个结点都是一种颜色,每个颜色有一个编号,求树中每个子树的最多的颜色编号的和 ...
- Kubernetes系列:Kubernetes Dashboard
15.1.Dashboard 作为Kube认得Web用户界面,用户可以通过Dashboard在Kubernetes集群中部署容器化的应用,对应用进行问题处理和管理,并对集群本身进行管理.通过Dashb ...
- cmake target_link_libraries() 中<PUBLIC|PRIVATE|INTERFACE> 的区别
如果目标的头文件中包含了依赖的头文件(源文件间接包含),那么这里就是PUBLIC 如果目标仅源文件中包含了依赖的头文件,那么这里就是PRIVATE 如果目标的头文件包含依赖,但源文件未包含,那么这里就 ...
- 论文翻译——Attention Is All You Need
Attention Is All You Need Abstract The dominant sequence transduction models are based on complex re ...