背景
machine-learning.md
机器学习主要术语
1.监督式机器学习
机器学习系统通过学习如何组合输入信息来对从未见过的数据做出有用的预测。
2.标签
标签是我们要预测的事物,即简单线性回归中的 y 变量。标签可以是小麦未来的价格、图片中显示的动物品种、音频剪辑中的含义或任何事物。
3.特征
特征是输入变量,即简单线性回归中的 x 变量。简单的机器学习项目可能会使用单个特征,而比较复杂的机器学习项目可能会使用数百万的特征,按如下方式指定:
1 {x1, x2, ... xn}
4.样本
样本是指数据的特定实例:x。(采用粗体x表示它是一个矢量。)我们将样本分为以下两类:
- 有标签样本
- 无标签样本
有标签样本同时包括特征和标签。即:1
labeled examples: {features, label}: (x, y)
无标签样本包含特征,但不包含标签。即:1
unlabeled examples: {features, ?}: (x, ?)
在使用有标签样本训练了我们德 模型之后,我们会使用该模型来预测无标签样本的标签。
5.模型
模型定义了特征与标签之间的关系。模型生命周期的冷两个阶段:
- 训练表示创建或学习模型。也就是说,向模型展示有标签样本,让模型逐渐学习特征与标签之间的关系。
- 推断表示将训练后的模型应用于无标签的样本。也就是说,使用训练后的模型来做出有用的预测。
6.回归于分类
- 回归模型可预测连续值
- 分类模型可预测离散值
训练与损失
简单来说,训练模型表示通过让有标签样本来学习(确定)所有权重和偏差的理想值。在监督式学习中,机器学习算法通过以下方式构建模型:检查多个样本并尝试找出可最大限度地减少损失的模型;这一过程称为经验风险最小化。
损失是对糟糕预测的惩罚。也就是说,损失是一个数值,表示对于单个样本而言模型预测的准确程度。如果模型的预测完全准确,则损失为零,否则损失会较大。训练模型的目标是从所有样本中找到一组平均损失“较小”的权重和偏差。
平均损失(L2损失)
(observation - prediction(x))^2 = (y - y’)2
均方损失(MSE)
每个样本的平均平方损失
javascript-array-reduce
Javascript中,数据类型Array有一些非常实用而强大的原生方法,接下来对主要的四种方法:forEach, map, filter, reduce做一个使用总结,由于reduce方法用的少,而且该方法本身存在难点,所以会着重介绍reduce方法。
Array四大迭代利器
1.forEach()
forEach()方法接受一个回调函数。如果没有抛出异常,数组中的每个元素都将会执行一次该函数。
语法1
2
3arr.forEach(function callback(currentValue, index, array) {
//your iterator
}[, thisArg]);
参数
callback | 为每个元素执行的函数,接受三个参数 |
---|---|
currentValue | 数组中当前被处理的元素 |
index | 当前元素的索引 |
array | forEach()方法应用的数组 |
thisArg | (可选)执行回调时使用this(即引用对象)的值 |
返回值 | undefined |
注意点
forEach()将要处理的元素在callback函数第一次执行前就已经确定,所以在forEach()开始执行后添加到数组中的元素将不会被callback处理。
如果现有元素的值发生了改变,传到callback里的值是forEach()取到的值。
在取值前被删除的元素将不会被访问。如果被访问过的元素被删除了,之后的元素会被略过(索引发生了变化)。
没有办法停止或打断forEach()循环,除了抛出异常。
2.map()
map()方法会返回一个由提供的回调函数作用于数组中的每个元素后产生的新数组,“纯”方法,不会污染原数组。
语法1
var new_array = arr.map(callback[, thisArg]);
参数
callback | 产生新数组的函数,接受三个参数 |
---|---|
currentValue | 数组中当前被处理的元素 |
index | 当前元素的索引 |
array | forEach()方法应用的数组 |
thisArg | (可选)执行回调时使用this(即引用对象)的值 |
返回值 | 回调函数产生的新数组 |
Examples1
2
3
4
5// 字符串中每个字符的字符码
var map = Array.prototype.map;
var a = map.call('string', function(c) {
return c.charCodeAt(0);
});
1 | // 反转字符串 |
1 | // 字符串转整型 |
3.filter()
filter()方法返回一个由提供的回调函数将数组中的每个元素过滤后组成的新数组
语法1
var newArray = arr.filter(callback[, thisArg]);
参数
callback | 用来检测数组中的每个元素,返回true保留元素,false不保留,接受三个参数 |
---|---|
element | 数组中当前被处理的元素 |
index | 当前元素的索引 |
array | 被过滤的数组 |
thisArg | (可选)执行回调时使用this(即引用对象)的值 |
返回值 | 满足回调函数的新数组 |
Examples1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26// 筛选掉JSON数组中不满足条件的元素
var arr = [
{id: 15},
{id: -1},
{id: 0},
{id: 3},
{id: 12.2},
{},
{id: null},
{id: NaN},
{id: 'undefined'}
];
var invalidEntries = 0;
function isNumber(obj) {
return obj !== undefined && typeof(obj) === 'number' && !isNaN(obj);
}
function filterByID(item) {
if(isNumber(item.id)) {
return true;
}
invalidEntries++;
return false;
}
var arrByID = arr.filter(filterByID);
1 | // 数组元素查询 |
4.reduce()
官方定义:The reduce() method applies a function against an accumulator and each value of the array (from left-to-right) to reduce it to a single value.
语法1
arr.reduce(callback, [initialValue]);
参数
callback | 作用于数组中每个元素的方法,接受四个参数 |
---|---|
accumulator | 作为每次迭代的初始值,同时也作为上一次迭代的结果值,如果是第一次执行callback,并且给了initialValue,则为initialValue的值,否则为数组第一个元素的值 |
currentValue | 数组中当前被处理的元素 |
currentIndex | 当前元素的索引,如果给了initialValue,索引从0开始,否则从1开始 |
array | reduce的数组 |
initialValue | (可选)callback第一次调用时使用的第一个值,即初始值 |
返回值 | 返回最终的结果 |
描述
reduce()执行一个接受四个参数的回调函数。回调函数第一次调用时,如果有initialValue,accumulator的值为initialValue,currentValue的值为数组第一个元素的值。如果没有设置initialValue,accumulator的值为数组第一个元素的值,currentValue的值为数组第二个元素的值。
Note 如果没有提供initialValue,reduce将会从索引1开始执行callback,略过第一个索引。如果提供了,从索引0开始。
如果数组是空的,并且没有提供initialValue,将会抛出TyoeError。如果数组只有一个元素并且没有提供initialValue,或者数组是空的但提供了initialValue,reduce将会返回这一个孤独的值而不调用callback。
Examples1
2
3
4
5
6
7
8// 数组扁平化
var flattened = [[0, 1], [2, 3], [4, 5]].reduce(
function(a, b) {
return a.concat(b);
},
[]
);
// flattened is [0, 1, 2, 3, 4, 5]
1 | // 统计数组中的值的种类 |
1 | // 取出数组中的所有对象里的某一个属性的值,并返回一个包含这些值的新数组 |
svg_viewport&viewbox
学习svg,除了掌握基本的图形元素,更要清楚svg元素的大小与svg内部图形大小的关系,svg图像的 viewport 和 view box 共同设置了图像可见部分的尺寸。现在我们一起学习下 viewport 和 view box 的知识,也许清不清楚这些知识对svg元素的使用不会产生多大的影响,但涉及到复杂图形的时候,了解这些知识会有助于我们更快速、更精准的控制元素,并且减轻维护成本。
1. The Viewport and View Box
viewport 是创建svg元素时给它设置的 width 和 height,决定了svg图像的可视区域。逻辑上svg图像可以无限大,但每次只有特定的区域是可视的,这个可视区域就是 viewport。你可以通过svg元素的 width 和 height 属性来指定 viewport 的大小:如
1 | <svg width="500" height="300"></svg> |
这个例子定义了一个宽500单位、高300单位的viewport。
view box 是 svg的一个属性,设置了内部图形的坐标系大小。
更直观的解释是:viewport 就像是显示器,有固定的宽高,你只能在显示器屏幕的范围内观看影像,view box 设置了显示器里的影像通过何种比例显示出来。就像你看到一个大海,它也许只占了显示器里大小的1/4,但它实际的大小却是显示器的n倍。viewport 和 view box 指定了两个坐标系,内部的图形会根据这两个坐标系做适当的缩放来适应性的显示出来。
2. svg坐标系单位
如果你没有指定 width 和 height 属性的单位,那么单位会被默认为 px。你还可以使用 px 以外的单位,
单位 | 描述 |
---|---|
em | 相对单位:相对于父元素字体大小 |
ex | 相对单位:相对于字符 x 的高度(很少使用) |
px | 绝对单位,但相对于设备分辨率:像素 |
pt | 绝对单位:点(1/72英寸) |
pc | 绝对单位:(1/6英寸) |
cm | 绝对单位:厘米 |
mm | 绝对单位:毫米 |
in | 绝对单位:英寸 |
给svg元素设置的单位只会影响svg元素的大小(viewport)。svg图像里显示的svg图形元素的大小取决于你给每个图形设置的单位。如果没有指定单位,默认使用 px。
下面展示一个svg元素与svg图像里的图形设置不同单位的例子:
1 | <svg width="10cm" height="10cm"> |
svg图像单单位是cm,两个rect元素有自己的单位:第一个没有指明单位,默认px;第二个使用了mm。结果如下:
3. The View Box
你可以通过 viewBox 属性重新定义没有单位的坐标。注:view box属性不要带单位。
1 | <svg width="500" height="200" viewBox="0 0 50 20"> |
这个例子中我们创建了一个宽500px高200px的svg元素。viewBox属性有四个值 x y width height,这些值定义了svg元素的视区,x 和 y是视区的左上角坐标,width 和 height是视区的宽高。在这个例子中,视区从(0, 0)开始,宽高分别是50和20,这意味着宽500px、高200px的svg元素在内部使用的坐标系是从(0, 0)开始,到(50, 20)。换句话说,svg元素内部坐标系中的1单位相当于宽度500/50=10px、高度200/20=10px。这就是为什么x轴偏移20单位、y轴偏移10单位的矩形实际位置却是偏移到了(200px, 100px)处。因为内部坐标系中1单位相当于10px。
结果为:
4. Preserving Aspect Ratio
如果 viewport 和 view box 没有使用相同的长宽比(repect ratio),你需要指定浏览器如何显示svg图像,这时你需要svg元素的preserveAspectRatio属性。
preserveAspectRatio属性由两个被空格分隔的值组成。第一个值设置view box在viewport中如何对齐,这个值由两部分组成;第二个值设置如何保留长宽比(如果需要的话)。
第一个值由两部分组成:x轴的对其方式和y轴的对齐方式,下面是这两个值的列表:
值 | 描述 |
---|---|
xMin | 将 view box 和 viewport 左边对齐 |
xMid | 将 view box 和 viewport x轴中心对齐 |
xMax | 将 view box 和 viewport 右边对齐 |
yMin | 将 view box 和 viewport 上边对齐 |
yMid | 将 view box 和 viewport y轴中心对齐 |
yMax | 将 view box 和 viewport 下边对齐 |
这两个部分可以通过驼峰命名的方式组合起来:xMinYMin。
第二个值有三个值:
值 | 描述 |
---|---|
meet | 保留长宽比并缩放 view box 以适应 viewport |
slice | 保留长宽比并把超出 viewport 的部分裁剪掉 |
none | 不保留长宽比,缩放图像以将 view box 完全置入 viewport。长宽比被破坏 |
这两个值之间需要有一个空格:1
2preserveAspectRatio="xMidYMid meet"
preserveAspectRatio="xMinYMin slice"
接下来我们来看看不同的 preserveAspectRatio 属性值带来的效果。
(1).
1 | <svg width="500" height="75" viewBox="0 0 250 75" style="border: 1px solid #cccccc;"> |
这种不设置 preserveAspectRatio 的情况与 xMidYMid meet 的效果相同,由此可推测浏览器会默认让图像以最合适、最舒服的方式显示出来。
(2).
1 | <svg width="500" height="75" viewBox="0 0 250 75" preserveAspectRatio="xMinYMin meet" style="border: 1px solid #cccccc;"> |
这个例子中设置了 preserveAspectRatio 为 xMinYMin meet,这会确保长宽比是保留了的,view box 会调整大小来适应 viewport。所以,view box 根据两个长宽比中(500/250=2, 75/75=1)较小的比例进行缩放(width: 50*1=50, height: 50*1=50),xMinYMin 使图形左上对齐。
(3).
1 | <svg width="500" height="75" viewBox="0 0 250 75" preserveAspectRatio="xMinYMin slice" style="border: 1px solid #cccccc;"> |
这个例子中设置了 preserveAspectRatio 为 xMinYMin slice,同样保留了长宽比,但 view box 根据两个比例中(500/250=2, 75/75=1)较大的比例进行缩放(width: 50*2=100, height: 50*2=100),结果超过了 viewport 的大小,所以 slice 掉超出的部分。
(4).
1 | <svg width="500" height="75" viewBox="0 0 250 75" preserveAspectRatio="none" style="border: 1px solid #cccccc;"> |
这个例子中设置了 preserveAspectRatio 为 none,view box 将会填满整个 viewport,从而使图像失真,因为x轴和y轴的长宽比不同(x轴 2:1,y轴 1:1)。
css-centering
CSS居中总结:CSS居中是任何一个前端开发者必备的基础技能,实现居中不算很有技术难度的事情,但能实现居中的方式实在是太多了,这里对各种居中方法做一个总结,毕竟温故而知新嘛!
一.水平居中
1.inline or line-* 元素
在块级父元素内的行内元素可以通过设置 text-align:center 来进行水平居中。
1 | div { |
1 | <div class="parent">水平居中</div> |
1 | nav { |
1 | <nav role='navigation'> |
2.块级元素
如果该块级元素有具体的 width,可以通过设置块级元素的 margin-left 和 margin-right 的值为 auto 来居中。1
2
3
4
5
6
7
8
9
10
11
12
13.main {
background: white;
margin: 20px 0;
padding: 10px;
border: 1px solid red;
}
.center {
margin: 0 auto;
width: 200px;
padding: 20px;
color: #000000;
border: 1px solid #000000;
}
1 | <div class="main"> |
3.多个块级元素
如果你需要把两个或两个以上的块级元素在一行水平居中,你需要把这些块级元素的 display 设置为 inline-block,或者使用 flex 布局。
设置 display,需设置块级元素的宽度1
2
3
4
5
6
7
8
9
10.inline-block-center {
text-align: center;
border: 1px solid red;
}
.inline-block-center div {
max-width: 125px;
display: inline-block;
text-align: left;
border: 1px solid #000000;
}
1 | <div class="inline-block-center"> |
使用 flex 布局1
2
3
4
5
6
7
8.flex-center {
display: flex;
justify-content: center;
border: 1px solid red;
}
.flex-center div {
border: 1px solid #000000;
}
1 | <div class="flex-center"> |
二.垂直居中
1.inline or line-* 元素
单行
(1).设置相同的 padding-top 和 padding-bottom 值。1
2
3
4
5
6
7
8
9
10
11
12
13.single {
margin: 20px 0;
padding: 50px;
border: 1px solid red;
}
.single a {
background: black;
color: white;
padding: 40px 30px;
text-decoration: none;
border: 1px solid #000000;
}
1 | <div class="single"> |
(2).设置 line-height 与 父元素 height 相等1
2
3
4
5
6
7
8
9
10
11
12
13
14
15.single-lineheight {
background: white;
margin: 20px 0;
padding: 40px;
border: 1px solid red;
}
.single-lineheight div {
color: black;
height: 100px;
line-height: 100px;
padding: 20px;
width: 50%;
white-space: nowrap;
border: 1px solid #000000;
}
1 | <div class="single-lineheight"> |
多行
flex 布局1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16.flex-center-more {
width: 240px;
margin: 20px;
color: #000000;
display: flex;
flex-direction: column;
justify-content: center;
height: 200px;
overflow: auto;
border: 1px solid red;
}
.flex-center-more p {
margin: 0;
padding: 20px;
border: 1px solid #000000;
}
1 | <div class="flex-center-more"> |
javascript-methods
Javascript 包含了少量用在标准类型上的标准方法。
Array
array.concat(item…)
concat方法返回一个新数组,它包含array的浅复制,,并将一个或多个参数 item 附加在其后。如果参数是一个数组,那么它的每个元素会被分别添加。1
2
3
4var a = ['a', 'b', 'c'];
var b = ['x', 'y', 'z'];
var c = a.concat(b, true);
// c 是 ['a', 'b', 'c', 'x', 'y', 'z', true]
array.join(separator)
join方法把一个 array 构造成一个字符串。它将 array 中的每个元素构造成一个字符串,并且用一个 separator 作为分隔符把它们连接在一起。默认的 separator 是 ‘,’。为了实现无间隔的连接,我们可以使用空字符串作为 separator。
如果你想把大量的片段组装成一个字符串,把这些片段放到一个数组中并用 join 方法连接它们通常比用 + 元素运算符连接这些片段要快。1
2
3var a = ['a', 'b', 'c'];
var b = a.join('');
// b 是 'abc';
array.pop()
pop 和 push 方法使数组 array 像堆栈一样工作。pop 移除 array 中的最后一个元素并返回该元素。如果 array 是空的,它会返回 undefined。1
2
3var a = ['a', 'b', 'c'];
var b = a.pop();
// a 是 ['a', 'b'], b 是 'c'
array.push(item…)
push 方法将一个或者多个参数 item 附加到一个数组的尾部。不像 concat 方法那样,它会修改数组 array,如果参数 item 是一个数组,它会将参数数组作为整个添加到数组中。它返回这个数组 array 的新长度值。1
2
3
4var a = ['a', 'b', 'c'];
var b = ['x', 'y', 'z'];
var c = a.push(b, true);
// a 是['a', 'b', 'c', ['x', 'y', 'z'], true], c 是 5
array.reverse()
reverse 方法反转数组 array 中元素的顺序。它返回当前的 array1
2
3var a = ['a', 'b', 'c'];
var b = a.reverse();
// a 和 b 都是 ['c', 'b', 'a']
array.shift()
shift 方法移除数组 array 中的第一个元素。如果这个数组是空的,它会返回 undefined。shift 通常比 pop 慢得多。1
2
3var a = ['a', 'b', 'c'];
var b = a.shift();
// a 是 ['b', 'c'], b 是 'a'
array.slice(start, end)
slice 对 array 中的一段做浅复制。第一个被复制的元素是 array[start],它将一直复制到 array[end]为止。end 参数是可选的,并且默认值是该数组的长度。如果两个参数中的任何一个是负数,array.length 将和它们相加起来试图使它们成为非负数。如果 start 大于等于 array.length,得到的结果将是一个新的空数组。1
2var a = ['a', 'b', 'c'];
var b = a.slice(0, 1); // b 是 'a'
array.sort()
sort 方法对 array 进行适当的排序,它不能正确给一组数字排序,因为该方法假定排序的元素是字符串,所以会把数字转换为字符串,从而数字数组排序会得到一个“错误”的结果。但可以使用自己的比较函数。1
2
3
4
5
6
7
8
9
10
11
12var a = [10, 2, 4, 7];
// 从小到大排序
a.sort(function(a, b) {
return a - b;
});
// a 是 [2, 4, 7, 10]
// 从大到小排序
a.sort(function(a, b) {
return b - a;
});
// a 是 [10, 7, 4, 2]
上面的方法只能给数字排序,如果给任意简单值数组排序,则必须做更多的工作1
2
3
4
5
6
7
8
9
10
11var a = ['aa', 'bb', 'd', 23, 12, 1, 45];
a.sort(function(a, b) {
if(a === b) {
return 0;
}
if(typeof a === typeof b) {
return a < b ? -1 : 1;
}
return typeof a < typeof b ? -1 : 1;
});
// a 是 [1, 12, 23, 45, "aa", "bb", "d"]
给对象数组排序1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42// by 函数接受一个成员名字符串作为参数
// 并返回一个可以用来对包含该成员的对象数组进行排序的比较函数
var by = function(name) {
return function(o, p) {
var a, b;
if(typeof o === 'object' && typeof p === 'object' && o && p) {
a = o[name];
b = p[name];
if(a === b) {
return 0;
}
if(typeof a === typeof b) {
return a < b ? -1 : 1;
}
return typeof a < typeof b ? -1 : 1;
} else {
throw {
name: 'Error',
message: 'Expected an object when sorting by ' + name
};
}
};
}
var s = [
{first: 'Joe', last: 'Besser'},
{first: 'Moe', last: 'Howward'},
{first: 'Joe', last: 'DeRita'},
{first: 'Shemp', last: 'Howward'},
{first: 'Larry', last: 'Fine'},
{first: 'Currly', last: 'Howard'}
];
s.sort(by('first'));
/* s 是
[
{first: 'Currly', last: 'Howard'}
{first: 'Joe', last: 'Besser'},
{first: 'Joe', last: 'DeRita'},
{first: 'Larry', last: 'Fine'},
{first: 'Moe', last: 'Howward'},
{first: 'Shemp', last: 'Howward'},
];
*/
基于多个键值排序,可以修改 by 函数,让其可以接受第二个参数,当主要的键值产生一个匹配的时候,另一个 compare 方法将被调用以决出高下。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43// by 函数接受一个成员名字符串和一个可选的次要比较函数作为参数,
// 并返回一个可以用来对包含该成员的对象数组进行排序的比较函数。
// 当 o[name] === p[name] 时,次要比较函数被用来决出高下
var by = function(name, minor) {
return function(o, p) {
var a, b;
if(o && p && typeof o === 'object' && typeof p === 'object') {
a = o[name];
b = p[name];
if(a === b) {
return typeof minor === 'function' ? minor(o, p) : 0;
}
if(typeof a === typeof b) {
return a < b ? -1 : 1;
}
return typeof a < typeof b ? -1 : 1;
} else {
throw {
name: 'Error',
message: 'Expected an object when sorting by ' + name
};
}
}
}
var s = [
{first: 'Joe', last: 'Besser'},
{first: 'Moe', last: 'Howward'},
{first: 'Joe', last: 'DeRita'},
{first: 'Shemp', last: 'Howward'},
{first: 'Larry', last: 'Fine'},
{first: 'Currly', last: 'Howard'}
];
s.sort(by('last', by('first')));
/* s 是
[
{first: 'Joe', last: 'Besser'},
{first: 'Joe', last: 'DeRita'},
{first: 'Larry', last: 'Fine'},
{first: 'Currly', last: 'Howard'}
{first: 'Moe', last: 'Howward'},
{first: 'Shemp', last: 'Howward'},
];
*/
array.splice(start, deleteCount, item…)
splice 方法从 array 中移除 1 个或多个元素,并用新的 item 替换它们。参数 start 是从数组 array 中移除元素的开始位置。参数 deleteCount 是要移除元素的个数。如果有额外的参数那些 item 都将插入到所移除元素的位置上它返回一个包含被移除元素的数组。
splice 最主要的用处是从一个数组中删除元素。千万不要与 slice 混淆,slice 主要的用处是对数组的一段做浅复制。
array.unshift(item…)
unshift 方法像 push 方法一样用于将元素添加到数组中,但它是把元素插入到 array 的开始部分而不是尾部。它返回 array 的新的长度值。
Function
function.apply(thisArg, argArray)
apply 方法调用函数 function,传递一个将被绑定到 this 的对象和一个可选的参数数组。apply 方法被用在apply调用模式中。
Number
number.toExponential(fractionDigits)
toExponential 方法把这个 number 转换成一个指数形式的字符串。可选参数 fractionDigits 控制其小数点后的数字位数。它的值必须在 0 至 20 之间。
number.toFixed(fractionDigits)
toFixed 方法把这个 number 转换成为一个十进制数形式的字符串。可选参数 fractionDigits 控制其小数点后的数字位数。它的值必须在 0 至 20 之间。默认为 0.
number.toPrecision(precision)
toPrecision 方法把这个 number 转换成为一个十进制数形式的字符串。可选参数 precision 控制有效数字的位数。它的值必须在 0 至 21 之间。
number.toString(radix)
toString 方法把这个 number 转换成为一个字符串。可选参数 radix 控制基数。它的值必须在 2 和 36 之间。默认的 radix 是以 10 为基数的。radix 参数最常用的是整数,但它可以用任意的数字。
Object
object.hadOwnProperty(name)
如果这个 object 包含了一个名为 name 的属性,那么这个 hasOwnProperty 方法返回 true。原型链中的同名属性是不会被检查的。
RegExp
regexp.exec(string)
exec 方法是使用正则表达式的最强大(和最慢)的方法。如果它成功地匹配 regexp 和字符串 string,它会返回一个数组。数组中下标为 0 的元素将包含正则表达式 regexp 匹配的子字符串。下标为 1 的元素是分组 1 捕获的文本,下标为 2 元素是分组 2 捕获的文本,以此类推。如果匹配失败,那么它会返回 null。
如果 regexp 带有一个 g 标志(全剧标志),事情变得有点更加复杂了。查找不是从这个字符串的起始位置开始,而是从 regexp.lastIndex(它初始化为 0)位置开始。如果匹配成功,那么 regexp.lastIndex将被设置为该匹配后第一个字符串的位置。不成功的匹配会重置 regexp.lastIndex为 0。
这就允许你通过在一个循环中调用 exec 去查询一个匹配模式在一个字符串中发生几次。有两件事情需要注意。如果你提前退出了这个循环,再次进入这个循环前必须把 regexp.lastIndex 重置到 0。^ 因子也仅匹配 regexp.lastIndex为 0的情况。
regexp.test(string)
test 方法是使用正则表达式的最简单(和最快)的方法。如果该 regexp 匹配 string,它返回 true,否则,返回 false。不要对这个方法使用 g 标识。
String
string.charAt(pos)
charAt 方法返回在 string 中 pos 位置处的字符串。如果 pos 小于 0 或大于等于字符串的长度 string.length,它会返回空字符串。JavaScript 没有字符类型。这个方法返回的结果是一个字符串。
string.charCodeAt(pos)
charCodeAt 方法同 charAt 一样,只不过它返回的不是一个字符串,而是以整数形式表示的在 string 中的 pos 位置处的字符串的字符码位。
string.concat(string…)
concat 方法通过将其他的字符串连接在一起来构造一个新的字符串。它很少被使用,因为 + 运算符更为方便。
string.indexOf(searchString, position)
concat 方法在 string 内查找另一个字符串 searchString。如果它被找到,则返回第一个匹配字符的位置,否则返回 -1。可选参数 position 可设置从 string 的某个指定的位置开始查找。
string.lastIndexOf(searchString, position)
lastIndexOf 方法和 indexOf 方法类似,只不过它是从该字符串的末尾开始查找而不是从开头。
string.localeCompare(that)
localeCompare 方法比较两个字符串。如何比较字符串的规则没有详细说明。如果 string 比字符串 that 小,那么结果为负数。如果它们是相等的,那么结果为0。这类似于 array.sort比较函数的约定。
未完。。。
动态规划
动态规划算法的思路以及实现
介绍
动态规划(DP)是算法设计思想当中最难也是最有趣的部分了,动态规划适用于有重叠子问题和最优子结构性质的问题,是一种在数学、计算机科学和经济学中经常使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。使用动态规划方法解题有较高的时间效率,关键在于它减少了很多不必要的计算和重复计算的部分。
它的思想就是把一个大的问题进行拆分,细分成一个个小的子问题,且能够从这些小的子问题的解当中推导出原问题的解。同时还需要满足以下两个重要的性质才能进行动态规划:
- 最优子结构性:既所拆分的子问题的解是最优解。
- 子问题重叠性质:既在求解的过程当中,每次产生的子问题并不总是新问题,有些子问题会被重复计算多次。动态规划算法是利用了这种子问题的重叠性质,对每一个子问题只计算一次,然后将其计算结果保存在一个表格中,当再次需要计算已经计算过的子问题时,只是在表格中简单地查看一下结果,从而获得较高的解题效率。
示例
首先引用一道动态规划的经典问题最长不下降子序列
它的定义是:设有由n个不相同的整数组成的数列 b[n],若有下标 i1 < i2 < … < iL 且 b[i1] < b[i2] < … < b[iL]
则称存在一个长度为 L 的不下降序列。
例如
13, 7, 9, 16, 38, 24, 37, 18, 44, 19, 21, 22, 63, 15
那么就有 13 < 16 < 38 < 44 < 63 长度为5的不下降子序列。
但是经过观察实际上还有 7 < 9 < 16 < 18 < 19 < 21 < 22 < 63 长度为8的不下降子序列,那么是否还有更长的不下降子序列呢?请找出最长的不下降子序列。
输入格式
第一行为 n,表示 n 个数(n <= 100000),第二行为 n 个数的数值(数字之间用空格隔开且最后一个数字末尾不能留有空格)。
输出格式
一个整数,表示最长不下降序列的长度。
输入例子
4
1 3 1 2
输出例子
2
思路
假如要求得某一段的最优,就要想更小段的最优怎么求,再看看由最小段的最优能否扩大推广到最大段的最优。所以该问题存在最优子结构,而从小段的最优子结构到更大的最优子结构,所有子结构的求解问题是相同的,即满足动态规划的性质。
假设这么一个表:
序列下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
序列数值 | 13 | 7 | 9 | 16 | 38 | 24 | 37 | 18 | 44 | 19 | 21 | 22 | 63 | 15 |
序列长度 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
链接位置 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | -1 |
第三行表示该序列元素的所能连接的最长不下降子序列的长度,因为本身长度为1,所以初始值都为1。
第四行表示链接于哪个序列元素形成最长不下降子序列。
1.从后向前
先从倒数第二项 63 算起,在它的后面仅有一项,因此仅作一次比较,因为 63 > 15,所以从 63 出发,不作任何链接,长度还是为1。
再看倒数第三项 22,在它的后面有 2 项,因此必须要在这 2 项当中找出比 22 大,长度又是最长的数值作为链接,由于只有 22 < 63,所以修改 22 的长度为 2,即自身长度加上所链接数值的长度,并修改链接位置为 13,也就是 63 的下标。
再看倒数第四项 21,在它的后面有 3 项,因此必须要在这3项当中找出比 21 大,长度又是最长的数值作为链接(注意:是长度),很容易看出,数值 22 满足该条件,因此,修改 21 的长度为3,并修改链接位置为 12,即 22 的序列下标。
依次类推,最后结果如下表:
序列下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
序列数值 | 13 | 7 | 9 | 16 | 38 | 24 | 37 | 18 | 44 | 19 | 21 | 22 | 63 | 15 |
序列长度 | 7 | 8 | 7 | 6 | 3 | 4 | 3 | 5 | 2 | 4 | 3 | 2 | 1 | 1 |
链接位置 | 3 | 2 | 3 | 7 | 8 | 6 | 8 | 9 | 12 | 10 | 11 | 12 | -1 | -1 |
最终状态的转移方程为:f(i) = maxf(j) + 1 (bj > bi 且 i < j),时间复杂度为 O(n^2)
2.从前向后
序列下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
序列数值 | 13 | 7 | 9 | 16 | 38 | 24 | 37 | 18 | 44 | 19 | 21 | 22 | 63 | 15 |
序列长度 | 1 | 1 | 2 | 3 | 4 | 4 | 5 | 4 | 6 | 5 | 6 | 7 | 8 | 3 |
链接位置 | -1 | -1 | 1 | 2 | 3 | 3 | 5 | 3 | 6 | 7 | 9 | 10 | 11 | 2 |
最终状态的转移方程为:f(i) = maxf(j) + 1 (bj < bi 且 i > j),时间复杂度为 O(n^2)
代码
1 | process.stdin.setEncoding('utf8'); |
输入输出
1 | 14 |
SSL/TLS
SSL/TLS协议运行机制
互联网的通信安全,建立在 SSL/TLS 协议之上。
本文简要介绍 SSL/TLS 协议的运行机制。
一.作用
不使用 SSL/TLS 的 HTTP 通信,就是不加密的通信。所有信息明文传播,带来了三大风险:
1.窃听风险 2.篡改风险 3.冒充风险
SSL/TLS 协议就是为了解决这三大风险而设计的,希望达到:
1.所有信息都是加密传播,第三方无法窃听。 2.具有校验机制,一旦被篡改,通信双方会立刻发现。 3.具有身份证书,防止身份被冒充。
互联网是开放环境,通信双方都是未知身份,这为协议的设计带来了很大的难度。而且,协议还必须能够经受所有匪夷所思的攻击,这使得 SSL/TLS 协议变得异常复杂。
二.历史
互联网加密通信协议的历史,几乎与互联网一样长。
1994年,NetScape 公司设计了 SSL 协议1.0版本,但是未发布。 1995年,NetScape 公司发布了 SSL 2.0版,但很快发现有严重漏洞。 1996年,SSL 3.0 版问世,得到大规模应用。 1999年,互联网标准化组织 ISOC 接替 NetScape 公司,发布了 SSL 的升级版 TLS 1.0版。 2006年和2008年,TLS 进行了两次升级,分别是 TLS 1.1版和 TLS 1.2版。
目前,应用最广泛的是 TLS 1.0,接下来是 SSL 3.0。但是,主流浏览器都实现了 TLS 1.2的支持。
TLS 1.0 通常被标示为 SSL 3.1,TLS 1.1 为 SSL 3.2,TLS 1.2 为 SSL 3.3。
三.基本的运行过程
SSL/TLS 协议的基本思路是采用公钥加密法,也就是说客户端先向服务器端索要公钥,然后用公钥加密信息,服务器接收密文后,用自己的私钥解密。
但是,这里有两个问题:
1.如何保证公钥不被篡改? 解决方法:将公钥放在数字证书中。只要证书是可信的,公钥就是可信的。 2.公钥加密计算量太大,如何减少耗用的时间? 解决方法:每一次对话(session),客户端和服务器端都生成一个“对话密钥”(session key),用它来加密信息。 由于“对话密钥”是对称加密,所以运算速度非常快,而服务器公钥只用于加密“对话密钥”本身,这样就减少了加密运 算的消耗时间。
因此,SSL/TLS协议的基本过程是这样的:
1.客户端向服务器端所要并验证公钥。 2.双方协商生成“对话密钥”。 3.双方采用“对话密钥”进行加密通信。
上面过程的前两步,又称为“握手阶段”。
四.握手阶段的详细过程
“握手阶段”设计四次通信,我们一个个来看。需要注意的是,“握手阶段”的所有通信都是明文的。
4.1客户端发出请求
首先,客户端(通常是浏览器)先向服务器发出加密通信的请求,这被叫做 ClientHello 请求。
在这一步,客户端主要向服务器提供一下信息:
1.支持的协议的版本。 2.一个客户端生成的随机数,稍后用于生成”对话密钥“。 3.支持的加密方法。 4.支持的压缩方法。
这里需要注意的是,客户端发送的信息之中不包括服务器的域名。也就是说,理论上服务器只能包含一个网站,否则
会分不清应该向客户端提供哪一个网站的数字证书。这就是为什么通常一台服务器只能有一张数字证书的原因。
对于虚拟主机用户来说,这当然不方便。2006年,TLS协议加入了一个 Server Name Indication 扩展,允许客户
端向服务器提供它请求的域名。
4.2服务器回应
服务器收到客户端请求后,向客户端发出回应,这叫做 ServerHello。服务器的回应包含以下内容。
1.确认使用的加密通信协议版本。如果浏览器与服务器支持的版本不一致,服务器关闭加密通信。 2.一个服务器生成的随机数,稍后用于生成”对话密钥“。 3.确认使用的加密方法。 4.服务器证书。
除了上面这些信息,如果服务器需要确认客户端的身份,就会再包含一项请求,要求客户端提供”客户端证书“。比如,金融机构往往只允许认证客户端连入自己的网络,就会向正式客户提供USB密钥,里面包含了一张客户端证书。
4.3客户端回应
客户端收到服务器回应后,首先验证服务器证书。如果证书不是可信机构颁布,或者证书中的域名与实际域名不一致,或者证书已经过期,就会向访问者显示一个警告,由其选择是否还要继续通信。
如果证书没有问题,客户端就会从证书中取出服务器的公钥。然后,向服务器发送下面三项信息。
1.一个随机数。该随机数用于服务器公钥加密,防止被窃听。 2.编码改变通知,表示随后的信息都用双方商定的加密方法和密钥发送。 3.客户端握手结束通知,表示客户端的握手阶段已经结束。这一项同时也是前面发送的所有内容的 hash 值,用来 供服务器校验。
上面第一项的随机数,是整个握手阶段出现的第三个随机数,又称”pre-master key”。有了它以后,客户端和服务器就同时有了三个随机数,接着双方就用事先商定的加密方法,各自生成本次会话所用的同一把”会话密钥”。
至于为什么一定要用三个随机数,来生成”会话密钥”,解释:
“不管是客户端还是服务器,都需要随机数,这样生成的密钥才不会每次都一样。由于SSL协议中证书是静态的,因此十分有必要引入一种随机因素来保证协商出来的密钥的随机性。
对于 RSA 密钥交换算法来说,pre-master-key 本身就是一个随机数,再加上 hello 消息中的随机,三个随机数通过一个密钥导出器最终导出一个对称密钥。
pre master 的存在在于 SSL 协议不信任每个主机都能产生完全随机的随机数,如果随机数不随机,那么 pre master secret 就有可能被猜出来,那么仅适用 pre master secret 作为密钥就不合适了,因此必须引入新的随机因素,那么客户端和服务器加上 pre master secret 三个随机数一同生成的密钥就不容易被猜出了,一个伪随机可能完全不随机,可是是三个伪随机就十分接近随机了,每增加一个自由度,随机性增加的可不是一。”
此外,如果前一步,服务器要求客户端证书,客户端会在这一步发送证书及相关信息。
4.4 服务器的最后回应
服务器收到客户端的第三个随机数 pre-master key 之后,计算生成本次会话所用的”会话密钥”。然后,向客户端最后发送下面信息。
1.编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。 2.服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时也是前面发送的所有内容的 hash 值,用来 供客户端校验。
至此,整个握手阶段全部结束。接下来,客户端与服务器进入加密通信,就完全是使用普通的 HTTP 协议,只不过用”会话密钥”加密内容。
Google 搜索引擎技巧
Google 搜索引擎技巧
1.双引号
把搜索词放在双引号中代表完全匹配搜索。
2.减号
减号代表搜索不包含减号后面的词,使用这个指令减号前面必须是空格,减号后面没有空格。
3.星号
通配符
4.~
同时搜索近义词
5.inurl
用于搜索查询词出现在url中。支持中文。e.g. inurl:markdown
6.inanchor
用于搜索链接锚文字包含搜索词。
7.intitle
用于搜索页面title中包含关键词
8.alinurl
用于搜索多组查询词出现在url中
9.alintitle
用于搜索页面标题中包含多组关键词
10.filetype
用于搜索特定文件格式
11.site
用来搜索某个域名下的所有文件
12.linkdomain
只适用于雅虎,返回的是某个域名的反向链接。
13.related
返回的结果是与某个网站有关联的页面。
14.year..year
时间段。e.g. 2008..2010,搜索2008年至2010年之间的结果
15.define
快速查询字词定义。e.g. define:angary,查询angary的定义
e.g.
. inurl:gov 减肥
返回的是url中包含gov,页面中有“减肥”这个词的页面
团队中的git实践
为了获得软件质量和开发效率的双重收益,软件项目开发早已不再是一个人的事情了,而是一个团队(team)的事情。不但如此,现今的软件开发规模越来越大,所涉及的技术门类也越来越复杂,一个人难以独挑大梁,同时,一个人也难以在短周期内完成复杂项目。团队的意义不仅在于提高开发效率,更能保证开发质量,一人负责一块开发内容就可以用充足的精力和热情使这块内容做的更加完美。
说到团队开发,总是离不开项目管理和版本控制工具,这里我们将会讨论版本控制工具——git在团队中正确实践的姿势。
Git/Github是任何一个非小白程序员都知道或使用的工具/网站,随着软件开发学龄的深入,我自己的体会是越来越离不开这个东西了。一个人用git可以用得随心所欲,但团队中就是另外一回事了。一个人的项目,代码怎么改、怎么提交都不会有冲突,这样也会养成一些不良习惯,把这些习惯带到团队开发中,轻则让队友讨厌,重则酿成大祸(这不是危言耸听)。
1.习惯养成
如果一个团队在使用git时没有一些规范,那将是一场难以醒来的噩梦!然而,规范固然重要,但更重要的是个人素质,在使用git时需要自己养成良好的习惯。
2.提交
Commit Message格式
1 | <type>(<scope>): <subject> |
上面是一次提交后Message格式规范,分成标题、内容详情、结尾三个部分,各有各的用处。
头部即首行,是可以直接在页面中预览的部分,一共有三个部分
Type
1 | - feat: 新功能(feature) |
Scope
1 | 用来说明本次提交影响的范围,即简要说明修改会涉及的部分。 |
Subject
1 | 用来简要描述本次改动,概述就好了,因为后面还会在Body里给出具体信息。并且最好遵循 |
Body
1 | <body>里的内容是对上面subject里内容的展开,在此作更加详尽的描述,内容里应该包括修改动机和修改前后的对比。 |
Footer
1 | <footer>里主要放置不兼容变更和Issue关闭的信息。 |
Revert
1 | 此外如果需要撤销之前的提交,那么本次提交Message中必须以revert: 开头,后面紧跟前面描述的Header部分,格式不变。 |
在具体开发工作中主要需要遵守的原则就是[使每次提交都有质量],只要坚持做到以下几点:
- 提交时的粒度是一个小功能点或者一个bug fix,这样进行恢复等操作时能够将[误伤]减到最低;
- 用一句简练的话写在第一行,然后空一行稍微详细阐述提交所增加或修改的地方;
- 不要没提交一次就推送一次,多积攒几个提交后一次性推送,这样可以避免在进行一次提交后发现代码中还有小错误。
1 | 加入代码已经提交了,对这次提交的内容进行检查时发现里面有个变量单次拼错了或者其他失误,只要还没有推送到远程, |
3.推送
当自己一个人进行开发时,在功能完成之前不要急着创建远程分支。
4.拉取和合并
在将其他分支的代码合并到当前分支时,如果那个分支是当前分支的父分支,为了保持图表的可读性和可追踪性,
可以考虑用 git rebase 来代替 git merge;反过来或者不是父子关系的两个分支以及互相已经 git merge 过的分支,
就不要采用 git rebase 了,避免出现重复的冲突和提交节点。
最后献上一句话
日拱一卒,功不唐捐。