Hexo点击切换文字

最近在工作中,有幸发现到了C# Aspose.Cells.dll Excel操作总结这篇文章。
觉得页面的鼠标点击效果还不错,故F12查看了一下源码,迁移到了个人博客网站上面,下面简单介绍一下实现步骤。

希望原作者不要太介意哈~

正文

在目录themes\next\source\js\src下新建click_show_text.js文件,内容如下

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
44
45
46
47
48
//单击显示随机文字
var a_idx = 0;
jQuery(document).ready(function($) {
$("body").click(function(e) {
var a = new Array(
"若是不专一,生活把你欺",
"活得有点二,倾慕大白菜",
"爱好只有三,辣和甜和酸",
"生日九月四,吃鱼得吐刺",
"广州年有五,喜欢吃排骨",
"绰号为十六,断烟不断肉",
"年龄二十七,就懂零和一",
"身高一米八,身边缺个她",
"体重一百九,木有女朋友",
"二五得一十,爱就得一世",
"智商不够百,不怕有人怼",
"弱水有三千,一瓢可成仙",
"打赏没过万,可悲又可叹",
"赚它一个亿,回家去种地"
);
var $i = $("<span/>").text(a[a_idx]);
a_idx = (a_idx + 1) % a.length;
var x = e.pageX,
y = e.pageY;
$i.css({
"z-index": 5,
"top": y - 20,
"left": x,
"position": "absolute",
"font-weight": "bold",
"color": "#FF69B4"
});
$("body").append($i);
$i.animate({
"top": y - 180,
"opacity": 0
},
3000,
function() {
$i.remove();
});
});
setTimeout('delay()', 2000);
});

function delay() {
$(".buryit").removeAttr("onclick");
}

具体要闪烁的文字,请自行替换。
然后在themes\next\layout\_layout.swig文件中 ****前添加以下代码

1
2
<!--单击显示文字-->
<script type="text/javascript" src="/js/src/click_show_text.js"></script>

然后hexo clean ,hexo g ,hexo d 即可看到点击效果。

注意:该事件可能和其余鼠标点击效果冲突。

总结

这是我自行做的页面优化,个人感觉还挺不错。


参考资料

Hexo宠物插件

参考了hexo 增添宠物这篇文章,但是其内容有相应的遗漏,故写下该文章,以作补充。

完整步骤

在终端切换到你的博客的路径里,然后输入如下代码:

1
npm install -save hexo-helper-live2d

然后打开选择下面一个萌宠或萌妹子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
live2d-widget-model-chitose
live2d-widget-model-epsilon2_1
live2d-widget-model-gf
live2d-widget-model-haru/01 (use npm install --save live2d-widget-model-haru)
live2d-widget-model-haru/02 (use npm install --save live2d-widget-model-haru)
live2d-widget-model-haruto
live2d-widget-model-hibiki
live2d-widget-model-hijiki
live2d-widget-model-izumi
live2d-widget-model-koharu
live2d-widget-model-miku
live2d-widget-model-ni-j
live2d-widget-model-nico
live2d-widget-model-nietzsche
live2d-widget-model-nipsilon
live2d-widget-model-nito
live2d-widget-model-shizuku
live2d-widget-model-tororo
live2d-widget-model-tsumiki
live2d-widget-model-unitychan
live2d-widget-model-wanko
live2d-widget-model-z16

如果你选择的是live2d-widget-model-wanko
则需要在命令行执行

1
npm install -save live2d-widget-model-wanko

参考文章中没有以上这一步。
然后再在 hexo 的 _config.yml中添加参数:

1
2
3
4
5
6
7
8
9
10
11
live2d:
enable: true
scriptFrom: local
model:
use: live2d-widget-model-wanko
display:
position: right
width: 140
height: 260
mobile:
show: true

然后hexo clean ,hexo g ,hexo d 即可

参考资料

欢乐斗地主出牌方式统计

集合S取出n个元素其和为K个数统计这篇文章中提到了笛卡尔积的这种解法,但是以笛卡尔积的方式求解的时候,我中午点击运行,我晚上回来的时候才输出了168条牌型组合,然后一直没有响应,后来粗略估算了一下,需要在1.44E+18条左右数据中进行筛选,而选出总手牌数为20条相当于大海捞针,所以需要重新对问题进行思考。
先将问题明确定义——“在N张手牌中,可能的出牌方式有多少种?”

牌型分析

以地主20张手牌为例,20张手牌可能1个炸弹也没有,最多有5个炸弹。所以20张手牌,炸弹的取值范围的取值范围是[0,5];
而3张相同的牌带1张单牌也是四张牌,所以20张手牌,出三张相同的牌带一张单牌的次数也是[0,5];
如果手牌中存在66667777这样的四张牌,6667和7776这种打法和一个6炸和7炸是两种打牌的方式。
同样就算是以6,6,6,6,7,7,7,7这样的8张单牌一次打出去,欺负下家或上家手里只有3,4,5了也不是不可以。
所以66667777这样8张牌在不考虑输赢的情况下也是有很多种打法的。而巧的是,我们讨论的是不考虑输赢的情况。

20张手牌有几种打出去的方式呢?

最多以20次单牌的形式打出去。
最多以10次对子的形式打出去。
最多以3次三连对的形式打出去。
依次类推,有了以下的表格:

可能的牌型组合

具体实现

请下载C#版源代码,放到对应的控制台去执行即可。
这里列举几个示例结果:
手牌数为1

1
2
3
手牌数为1时,可能的打法情况为:
1: 牌型为:单张,每次消耗1,出了1次,共消耗掉1张牌
所有牌型列举完成

手牌数为2

1
2
3
4
5
手牌数为2时,可能的打法情况为:
1: 牌型为:王炸,每次消耗2,出了1次,共消耗掉2张牌
2: 牌型为:对子,每次消耗2,出了1次,共消耗掉2张牌
3: 牌型为:单张,每次消耗1,出了2次,共消耗掉2张牌
所有牌型列举完成

手牌数为3

1
2
3
4
5
6
手牌数为3时,可能的打法情况为:
1: 牌型为:3张牌,每次消耗3,出了1次,共消耗掉3张牌
2: 牌型为:单张,每次消耗1,出了1次,共消耗掉1张牌 牌型为:王炸,每次消耗2,出了1次,共消耗掉2张牌
3: 牌型为:单张,每次消耗1,出了1次,共消耗掉1张牌 牌型为:对子,每次消耗2,出了1次,共消耗掉2张牌
4: 牌型为:单张,每次消耗1,出了3次,共消耗掉3张牌
所有牌型列举完成

手牌数为4

1
2
3
4
5
6
7
8
9
10
手牌数为4时,可能的打法情况为:
1: 牌型为:炸弹,每次消耗4,出了1次,共消耗掉4张牌
2: 牌型为:3张牌带1张,每次消耗4,出了1次,共消耗掉4张牌
3: 牌型为:对子,每次消耗2,出了1次,共消耗掉2张牌 牌型为:王炸,每次消耗2,出了1次,共消耗掉2张牌
4: 牌型为:对子,每次消耗2,出了2次,共消耗掉4张牌
5: 牌型为:单张,每次消耗1,出了1次,共消耗掉1张牌 牌型为:3张牌,每次消耗3,出了1次,共消耗掉3张牌
6: 牌型为:单张,每次消耗1,出了2次,共消耗掉2张牌 牌型为:王炸,每次消耗2,出了1次,共消耗掉2张牌
7: 牌型为:单张,每次消耗1,出了2次,共消耗掉2张牌 牌型为:对子,每次消耗2,出了1次,共消耗掉2张牌
8: 牌型为:单张,每次消耗1,出了4次,共消耗掉4张牌
所有牌型列举完成

根据以上结果中的示例2和示例5可知,就算是7778也可以以777和8分两次打出去。
手牌数为5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
手牌数为5时,可能的打法情况为:
1: 牌型为:5张牌的顺子,每次消耗5,出了1次,共消耗掉5张牌
2: 牌型为:3张牌带1对,每次消耗5,出了1次,共消耗掉5张牌
3: 牌型为:3张牌,每次消耗3,出了1次,共消耗掉3张牌 牌型为:王炸,每次消耗2,出了1次,共消耗掉2张牌
4: 牌型为:对子,每次消耗2,出了1次,共消耗掉2张牌 牌型为:3张牌,每次消耗3,出了1次,共消耗掉3张牌
5: 牌型为:单张,每次消耗1,出了1次,共消耗掉1张牌 牌型为:炸弹,每次消耗4,出了1次,共消耗掉4张牌
6: 牌型为:单张,每次消耗1,出了1次,共消耗掉1张牌 牌型为:3张牌带1张,每次消耗4,出了1次,共消耗掉4张牌
7: 牌型为:单张,每次消耗1,出了1次,共消耗掉1张牌 牌型为:对子,每次消耗2,出了1次,共消耗掉2张牌 牌型为:王炸,每次消耗2,出了1次,共消耗掉2张牌
8: 牌型为:单张,每次消耗1,出了1次,共消耗掉1张牌 牌型为:对子,每次消耗2,出了2次,共消耗掉4张牌
9: 牌型为:单张,每次消耗1,出了2次,共消耗掉2张牌 牌型为:3张牌,每次消耗3,出了1次,共消耗掉3张牌
10: 牌型为:单张,每次消耗1,出了3次,共消耗掉3张牌 牌型为:王炸,每次消耗2,出了1次,共消耗掉2张牌
11: 牌型为:单张,每次消耗1,出了3次,共消耗掉3张牌 牌型为:对子,每次消耗2,出了1次,共消耗掉2张牌
12: 牌型为:单张,每次消耗1,出了5次,共消耗掉5张牌
所有牌型列举完成

限于篇幅,不再一一列举,根据程序列举出来的可能性如下图所示:

手牌为N时,可以的出牌方式统计

可知有17张手牌时,可以有1729种出牌方式。
可知有20张手牌时,可以有4815种出牌方式。
也许基于此,就有了赌徒说搏一搏,单车变摩托的说法。

注意,20张手牌的出牌方式并不能达到4815种,一个12张牌的顺子是12张牌,而再来两个炸弹8张牌,加起来也是20张牌。但是去掉3~A的12张牌,只能算出”2222”一个炸弹了。即”12张牌的顺子和2个炸弹”虽说符合代码的逻辑,但是并不符合现实中的牌型。换句话说,参考资料中的代码并没有考虑这种情况。

问题是还有哪些牌型组合在一起能满足总手牌数,但是不符合现实中的牌型呢?

总结

本以为在计算{1,2,3,4}中取出任意个数,与取出顺序无关时使用笛卡尔积的方式是很好的方式;结果在该问题中待处理数据竟达到了1.44E+18(1.44乘以10的18次方)条之多。
由此可知,问题并不是想当然就能够得到解决的,实践永远是检验真理的标准。
如果在每种牌型设置其对应的权重,所以在AI中的斗地主,如果能检测出手牌是10连对,或333444555666777带789JQ类型,得赶紧明牌不是么?

参考资料

集合S取出n个元素其和为K个数统计

最近csrediscore的创作者在制作一个斗地主的机器人,他探讨性地给我出了一个问题——怎么样去统计手牌组合的可能性?
该问题算是比较复杂的,在不考虑癞子的情况下有火箭、炸弹、单牌、对牌、连对、三张牌、三带一、单顺、双顺、三顺、飞机带翅膀、四带二等等牌型。
以地主20张手牌为例:

  • 20张手牌中能打出火箭次数在[0,1]中取值。
  • 20张手牌中能打出炸弹次数在[0,5]中取值。
  • 20张手牌中能打出单牌次数在[0,20]中取值——在不考虑其他玩家的情况下,最多出20次单牌。
  • 依次类推…………
    所以问题转换为每种牌型中选取任意次数构成N张手牌的情况有多少种?

问题分解一

为了更加简单一点描述问题,我继续对问题进行了简化。
从集合{1,2,3,4}中,取出一个元素和为10的个数统计
比如集合为:{1,2,3,4},和值为10;其中取法1,2,3,4和4,3,2,1等价。
因和值固定,且都为正数,所以每个元素的取出次数有限,可以得出结论如下图
1,2,3,4可以取出次数分析

问题分解二

1
2
3
4
5
6
从集合
{0,1,2,3,4,5,6,7,8,9,10},
{0,2,4,6,8,10},
{0,3,6,9},
{0,4,8}
中各取出一个元素组成新集合S1,S1求和为10的个数统计。

由图可知,可以取出的元素组合情况为: 11 * 6 * 4 * 3=792种。

最终只需要在这792种方案中选取出和为10的记录。
罗列792种方案的可行性的过程叫做求笛卡尔积,以下给出代码实现C#版代码实现过程。

代码实现

求笛卡尔积扩展类

1
2
3
4
5
6
7
8
9
10
11
12
13
public static class EnumerableEx
{
/// <summary>
/// 求集合的笛卡尔积
/// </summary>
public static IEnumerable<IEnumerable<T>> Cartesian<T>(this IEnumerable<IEnumerable<T>> sequences)
{
IEnumerable<IEnumerable<T>> tempProduct = new[] {Enumerable.Empty<T>()};
return sequences.Aggregate(tempProduct,
(current, sequence) =>
(from accseq in current from item in sequence select accseq.Concat(new[] {item})));
}
}

创建数字集合类

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
/// <summary>
/// 数字集合
/// </summary>
public class DigitGroup
{
/// <summary>
/// 元素
/// </summary>
public int Value;

/// <summary>
/// 次数
/// </summary>
public int Times;

/// <summary>
///
/// </summary>
public int Sum;

public DigitGroup(int value, int times)
{
this.Value = value;
this.Times = times;
this.Sum = value * times;
}

public override string ToString()
{
return string.Format("{0}个{1},和为{2}", Times, Value, Sum);
}
}

控制台Program类

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
44
45
46
47
48
49
50
51
52
class Program
{
static void Main(string[] args)
{
List<int> numbers = new List<int> {1, 2, 3, 4};
int Sum = 10;
var digitGroupList = GetChooseList(numbers, Sum);

var result = digitGroupList.Cartesian();
result = result.Where(chooses => chooses.Sum(choose => choose.Sum) == Sum);
PrintResult(result);

Console.ReadKey();
}

private static IEnumerable<IEnumerable<DigitGroup>> GetChooseList(List<int> intList, int target)
{
List<List<DigitGroup>> newList = new List<List<DigitGroup>>();
foreach (var beichushu in intList)
{
List<DigitGroup> temp = new List<DigitGroup>();
var count = target / beichushu;
for (int i = 0; i <= count; i++)
{
temp.Add(new DigitGroup(beichushu, i));
}

newList.Add(temp);
}

return newList;
}

private static void PrintResult(IEnumerable<IEnumerable<DigitGroup>> result)
{
int index = 0;
foreach (var list in result)
{
index += 1;
Console.Write(index + ": ");
foreach (var choose in list)
{
if (choose.Sum != 0)
{
Console.Write(" " + choose + " ");
}
}

Console.WriteLine();
}
}
}

最终运行结果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1:  23,和为6    14,和为4
2: 12,和为2 24,和为8
3: 22,和为4 23,和为6
4: 32,和为6 14,和为4
5: 52,和为10
6: 11,和为1 33,和为9
7: 11,和为1 12,和为2 13,和为3 14,和为4
8: 11,和为1 32,和为6 13,和为3
9: 21,和为2 24,和为8
10: 21,和为2 12,和为2 23,和为6
11: 21,和为2 22,和为4 14,和为4
12: 21,和为2 42,和为8
13: 31,和为3 13,和为3 14,和为4
14: 31,和为3 22,和为4 13,和为3
15: 41,和为4 23,和为6
16: 41,和为4 12,和为2 14,和为4
17: 41,和为4 32,和为6
18: 51,和为5 12,和为2 13,和为3
19: 61,和为6 14,和为4
20: 61,和为6 22,和为4
21: 71,和为7 13,和为3
22: 81,和为8 12,和为2
23: 101,和为10

总结

我现在依旧不知道20张手牌,各种牌型组合的可能性有多少种,但是将问题转化成{1,2,3,4}求和为10的这种方式已经将问题做了一个很好的分解。
只需要将DigitGroup再做一下相应的替换就可以计算出可能的牌型有多少种,该问题的求解已经不在该文的范畴类,有兴趣的读者可以尝试解决一下。

参考资料

Hexo站点统计

问题

页脚的总访问人数和单页访问人数显示不正常。

解决方案

查找

1
<script async src="//dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>

替换成

1
<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>

即可

参考资料

第一个AI应用

我的第一个AI应用是参考实战:从0搭建完整 AI 开发环境写出第一个 AI 应用AI应用开发实战 - 从零开始配置环境两篇文章进行的实施,故以该文做出相应的补充。

遇到的问题

‘python’ 不是内部或外部变量

命令行执行python –version提示’python’ 不是内部或外部变量,在命令行中输入

1
set PATH="C:\Program Files (x86)\Microsoft Visual Studio\Shared\Python36_64";%PATH%

之后,再输入

1
python --version

输出版本为3.6.5
但是关闭命令行之后,再输入python –version时,依旧提示 ‘python’ 不是内部或外部变量
故在计算机→属性右键→高级系统设置→环境变量→选择PATH→点击新建→将C:\Program Files (x86)\Microsoft Visual Studio\Shared\Python36_64添加→确定,之后再运行python –version,显示正常。

saved_model.pb路径不对

单独参考第一篇文章进行配置时,发现没有samples-for-ai\export\saved_model.pb这个路径。
原因是没有启动examples\tensorflow\TensorflowExamples.sln这个解决方案,将MNIST项目设置为启动项目并运行,则会有samples-for-ai\export\saved_model.pb这个文件了。

命令行无响应

安装scipy-1.1.0mxnet_cu90-1.2.0时,命令行一直无响应,解决方案是到scipy-1.1.0mxnet-cu90 1.2.0下载指定的文件,然后通过pip3命令来执行安装,其余问题也可以通过类似命令来解决。

1
2
pip3 install D:\scipy-1.1.0-cp36-none-win_amd64.whl
pip3 install D:\mxnet_cu90-1.2.0-py2.py3-none-win_amd64.whl

通过下载的文件可以得知,文件较大,命令行无法及时完成下载,所以需要有一定的耐心等待

注意需要将C:\Program Files (x86)\Microsoft Visual Studio\Shared\Python36_64\Scripts添加到PATH变量中。

cudnn版本不对

现在官网https://developer.nvidia.com/cudnn提供的cudnn版本是7.4.1,而微软示例代码中的cudnn版本是7.0.3,高版本的cudnn也会导致编译失败,需要找低版本的7.0.的cudnn替换到*C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\bin\cudnn64_7.dll

运行结果

解决了这部分问题之后,能正常展示winform界面,运行结果如下:
正确识别
错误识别
该部分涉及到训练模型是否足够多的问题,该文不做深入的研究。

总结

参考资料中提及的两篇文章都已经是做手写识别非常好的入门资料,该文仅仅是对这两篇文章做一个相应的补充,以作备忘。
另外我希望早日掌握以下技能

识别开心消消乐的游戏界面,然后通过能够确定执行的最佳下一步,达到这个目的,我觉得我对人工智能的了解和我的AI编程就进入了新的层次了。

#参考资料

我的简历

个人基本信息

  • 姓名 刘石丰
  • 性别
  • 学历 本科
  • 专业 电子信息工程
  • 院校 中南林业科技大学
  • 政治面貌 人民群众
  • 所在城市 广东广州
  • 移动电话 18664891357
  • 电子邮件 417144452@qq.com
  • Github @ddabb
  • 个人博客 https://ddabb.github.io
  • 求职状态 在职,无离职意向

工作经历

广州市睿明计算机科技有限公司
中国(安踏)有限公司
广州市铂铱柯信息科技有限公司
北京和信融慧信息科技有限公司

擅长领域

业务:

  • RPA系统的设计开发
  • CRM系统的设计开发
  • ESCM系统的设计开发

框架:

  • 基于CRM框架的 Web 系统设计开发
  • 对于前端框架 Inspina、BootStrap、Jquery、Layui等有使用经验

技术:

  • webform项目重构及性能调优
  • winform项目重构及性能调优
  • webapi,webservice
  • .net core和gensql结合开发
  • IDE :VS code,Visual Studio 2017,SSMS

其他:

  • DB: 主要使用sqlserver为主,对mysql,PostgreSQL,redis等数据库有基本的了解
  • 消息队列: RabbitMq,Redis
  • 开发语言: 主要使用C#和sql为主,用java、vb、python等语言做过基本功能。
  • 版本管理: 主要使用SVN、Git、VSS
  • 反编译工具: dnspy/ILSpy,JetBrains dotPeek,.Net Reflector,Telerik JustDecompile

开发经历

ISSUE管理系统

  • 时间: 一周
  • 描述: 统计公司项目的问题,统计分析问题进展情况。
  • 职责: 全栈
  • 角色: 全栈
  • 状态: 成功上线
  • 团队规模: 1人
  • 总结
    该系统基于webform开发。
    该系统实现了多人多项目的状态管理,实现了ISSUE单据的assign,日报,周报,发送邮件通知等功能,对公司的项目管理质量有明显的提升,是个轻巧灵活的ISSUE管理系统。
    通过该系统掌握了window定时任务,发送邮件等基本功能。
    随着工作经验的丰富,发现有tower,jira等工具可以使用,比自己写的ISSUE管理系统相对而言要健壮,美观一些。
    该系统算是敏捷开发的一次不错的尝试。

SqlSever迁移到Mysql项目

  • 时间: 一个月
  • 描述: 重构项目,目标是实现SqlServer数据库到Mysql数据库的迁移。
  • 职责: 全栈
  • 角色: 研发人员
  • 团队规模: 3人
  • 状态: 成功上线
  • 总结
    该系统基于winform开发。
    该项目的目标是底层数据库并行支持mysql或者sqlserver,项目目标是600多个sql server的sql语句需要支持mysql版本的实现。
    通过该项目对比总结了mysql和sqlserver的语法和字段类型的差异,并温习了策略模式。
    因项目时间有限等原因,故没有采取将原生sql重构成EF实现的这种方式,若采用EF实现,可以更加便捷地继续向PostgreSQL等数据库进行迁移。

搭建个人博客系统

  • 时间: 三个月的业余时间
  • 描述: 该项目的目标是基于.net core构建一个博客系统,该博客系统具备编辑,分类,标签,评论等功能。
  • 职责: 全栈
  • 角色: 全栈
  • 团队规模: 1人
  • 项目状态: 既有失败,又有成功
  • 总结
    该系统基于.netcore开发。
    该系统失败的定义是我没有实现基于.netcore搭建出来一套具备编辑,分类,标签,评论等功能的博客系统的这个目标。
    该系统成功的部分在于基于该目标,我接触了很多之前没有接触的知识点。比如markdown,.netcore+dbfirst,PostgreSQL开发等等。
    在摸索该目标的过程中发现了Hexo这一套可以自己选择主题定制化的博客系统,并基于此搭建了一个属于自己的博客站点——60分加油站
    该项目的经验是 **一定要记住项目立项的最重要的目标是什么。**,该项目的主要目标是一个博客系统,而创建一个博客系统的目标是对个人知识进行一个归纳整理,现在已经可以通过Hexo框架对个人知识进行归纳整理,就无需要再制造新的车轮了。

自我评价

  • 个人遵循**”Do not repeat yourself”**原则,想法设法将重复低效的事情交由系统执行。
  • 认可开源分享理念,混迹于github,oschina等站点,并认为开源促进社会进步。乐于总结和分享个人经验。座右铭——不接触,不了解;不分享,不成长。
  • 对算法以及AI等方向比较感兴趣
  • 做事专一,热衷于在一个领域持续积累经验并有所作为。

个人信息

个人基本信息

  • 姓名 刘石丰
  • 性别
  • 学历 本科
  • 专业 电子信息工程
  • 院校 中南林业科技大学
  • 政治面貌 人民群众
  • 所在城市 广东深圳
  • 移动电话 18664891357
  • 电子邮件 1011888891@qq.com
  • Github @ddabb
  • 个人博客 https://ddabb.github.io
  • 求职状态 在职,无离职意向
  • 个人网站 https://www.60points.com
  • 个人技能 微信小程序,C#

如无必要,勿增实体

初次接触到 如无必要,勿增实体 是在《西部世界》这部美剧中,今日早晨我在看贝叶斯定理的相关文章的时候,恰巧又温习到这句话,想聊一下该语句是怎么影响我的生活的。

以往

参加新的工作以后,很多东西都发生了变化。比如以前是B/S项目,现在是C/S项目。因为B/S的逻辑代码都存在于服务器端,基本上不用考虑源码被不良居心的人使用。而C/S架构存在源码泄露的风险,故需要对产品的Dll做混淆处理。
如果要制作一个安装包,避免不了以下这些步骤:

  • SVN签出最新代码
  • 重新生成解决方案
  • Release目录内的dll拷贝到混淆工具指定路径
  • 混淆结果拷贝到安装包制作路径
  • 生成安装包发布到指定路径

在window下的tortoisesvn 可视化操作下,选中指定目录签出文件,并不麻烦。
在visual studio 2017下,打开几十个项目重新编译通过就有点麻烦,毕竟项目越多,Visual Studio加载越慢。
在多个Release目录中,选中指定的待混淆文件拷贝对混淆工具的指定路径是重复低效事件。
再将混淆结果拷贝到安装包制作路径再制作安装包是重复低效事件。
再将安装包拷贝到阿里云的Ftp服务器是重复低效事件。

1
原目录和目标目录的不断切换,不同工具的开启和调用,整个过程操作下来,顺利则30分钟,不顺利则一个小时。

如果一天做两个不同版本的安装包,一天就一个多小时重复在安装包的制作之上。这并不能体现出我的工作价值。
鲁迅先生说过:

1
无端地空耗别人的时间,其实无异于谋财害命。

若不想被谋财害命,需要作出一些改变。

反思

若是一年做一次安装包,写一个安装包部署手册就好了。一年之后再制作安装包的时候,参考该部署手册就能很好的解决问题了。
但是一周2~3次的安装包制作(不做解释^_^),一周就有三四个小时用于制作安装包了。
《西部世界》好歹是一个机器人的世界,我公司又是做的RPA的产品,怎么自己还在做这种重复低效无意义的事情?

  • SVN可以通过命令行做签入签出的动作。
  • Visual Studio 2017 也可以通过命令行执行重新生成解决方案的动作。
  • 拷贝文件到指定目录的代码网上比比皆是。
  • ftp也可以通过命令行执行上传下载文件的动作。

我能不能制作一个工具,一键就把以上所有实现都给完成了?

现在

参考了一些资料,花了几天终于折腾出来了一个发布工具(Ftp命令行这部分未实现),虽说这个发布并比不上docker的一件部署。
但是于我个人而言,却是近期以来

1
如无必要,勿增实体

的最佳实践,基于此工具,制作一个安装包的时间已经省到一半的时间了,这样也是惜时如金的一种表现吧。
这个基于Hexo主题的网站搭建也是一个Do not repeat yourself的实践,没有试图再去从零开始搭建一个网站框架,创作一个新型的电子垃圾也是迷途知返吧;
再引用一句鲁迅的名言

1
2
3
4
总之,我们要拿来。我们要或使用,或存放,或毁灭。  
那么,主人是新主人,宅子也就会成为新宅子。
然而首先要这人沉着,勇猛,有辨别,不自私。
没有拿来的,人不能自成为新人,没有拿来的,文艺不能自成为新文艺。

没有拿来的,技术不能自成为新技术。不需要创造重复的轮子,但是手上也需要有适合自己的工具。

总结

奥卡姆剃刀原理也好,Dry原则也好,RPA也好,AI也好,甚至西部世界也好都说明了一个道理:
人从事重复简单的工作,能带来的是熟练程度的提升,但是在更年轻力壮的人或者机器人面前,他并不具备足够的竞争力。

参考资料