使用TT来练习打字

作为一个程序员来说,指法一直认为很重要,看着别人在键盘上噼里啪啦敲代码,很赏心悦目,尤其佩服vi用户,无鼠标操作,写代码太舒服了,不过因人而异,不用刻意追求无鼠标操作,用一些流行的编辑器也能提高效率

虽说不是二指禅用户,可是指法一直不标准,打汉字比较多,可以实现很快的打字速度,可是代码并不能实现全盲打,尤其混杂着一些数字符号的时候错误率就更高了,一直想着纠正一下自己的指法(已经说了好几年了。。。),一直不能检查下来,总结了下原因:

  1. 肯定最大原因在自己,并没有持之以恒的练习
  2. 找的练习软件也并不能对我产生很大的吸引力,总觉得练习枯燥没什么效果(后来发现练习内容也有关系)

直到最近又把纠正指法的日程提上来了,这时候在逼乎上发现一个回答,让我知道了TT这个老牌的打字程序

详情见如何练习编程的手速?

由于我是mac环境,只能安装dosbox再启动tt程序

使用以后的感受:

  1. 虽然界面比较简陋,但是我觉得能够让我更专注,没有很多花里胡哨的东西影响
  2. 里面练习的内容一直在变,都是一些英文文章,26个字母能够很好的兼顾到,不像某些软件,只练习一些键会很枯燥
  3. 目前练习letter比较多一些,以后慢慢发展到all key,会有很长的煎熬时间
  4. 每次练习完会告诉用户你本次的WPM,菜鸡目前只有27WPM,希望练习到50以上WPM

这次给自己定了明确的目标,我想要达到什么结果,没有规定时间,哪怕是一年达到也可以,希望看到一年后的进步,目前每天练习20分钟,每天都会有一小点进步,还是很可观的。

Comments

确定有限自动机 DFA

计算理论中,确定有限状态自动机确定有限自动机(英语:deterministic finite automaton, DFA)是一个能实现状态转移的自动机。对于一个给定的属于该自动机的状态和一个属于该自动机字母表的字符,它都能根据事先给定的转移函数转移到下一个状态(这个状态可以是先前那个状态)。

以上是引用维基百科DFA的解释,DFA属于计算机理论,在上大学应该都有涉及到(实际上我都没印象了😅,理论课应该是有,只是当时完全听不懂,也没深入去研究)

最近在做leetcode题的时候,在题解中发现有人提到用DFA的思路去做,这才补了一下,对DFA有了一些了解

通俗点讲就是一个状态state1通过event转变为另外一个状态state2,而且这些state跟event是确定的,数量都是可以穷举的,主要是来解决这一类问题的。同样的也可以通过暴力解法,但是需要考虑特别多的情况,会有很多的if else,往往漏洞百出,无法覆盖所有的情况,这个时候DFA就显出它的优势了。

最开始是在做leetcode的这道题字符串转换整数 (atoi)的时候看题解有大佬提到DFA的,后来自己找了一道来练手有效数字,其实只是比字符串转整数的状态多了一些,稍微复杂一些,并且题目说明了陈诉的比较模糊,需要自己思考所有情况,所以在做的过程中有很多情况没有想到的以及跟测试用例想的不一样的,可以看到提交了很多次,后面的都是靠测试用例改进的

拿这道题来说,我们用DFA思想做的时候,首先要思考输入的字符都会有哪些情况,把这些转变为上面所说的event

  1. 空格 “ “
  2. 符号 “+” “-“ => sign
  3. 数字 0-9 => digit
  4. 点 “.” => dot
  5. “e” => e
  6. 其它 => other

我们可以设定几个状态

start 开始代表开始

sign 代表加入了符号

digit 代表加入数字

dot 代表加入小数点

e 代表指数

end 代表结束

这样我们就可以写出DFA表

space sign digit dot e other
start start sign digit dot end end
sign end end digit dot end end
digit end end digit digit e end
dot end end number end end end
e e sign digit end end end
end end end end end end end

实际上这不是第一版,自己没有考虑全所有的情况,导致最开始写的DFA表有些问题,后来都是看测试用例修改的,不用太纠结这个,考虑不全是很正常的,明白其中的道理就行

我们拿start这一行解释一下

如果碰见space,那么相当于还是start状态

+sign,就转变到sign状态,digit跟dot同理

e比较特殊了,在开始的时候是不可以跟e的,类似e15这样是错误的,所以直接end

其它行可以自己理解一下

最终的代码就是这样:

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
53
54
func isNumber(s string) bool {
s = strings.TrimSpace(s)
m := make(map[string][]string)
m["start"] = []string{"start", "sign", "digit", "dot", "end", "end"}
m["sign"] = []string{"end", "end", "digit", "dot", "end", "end"}
m["digit"] = []string{"end", "end", "digit", "digit", "e", "end"}
m["dot"] = []string{"end", "end", "digit", "end", "end", "end"}
m["e"] = []string{"e", "sign", "digit", "end", "end", "end"}
m["end"] = []string{"end", "end", "end", "end", "end", "end"}

state := "start"
usePoint := 0
useE := 0
for i := range s {
// 当前状态为digit的时候并且前面dot已经使用过,类似 6.5 这样数字后就不能再出现dot了,所以修改为end
if state == "digit" && usePoint == 1 {
m["digit"][3] = "end"
}
// 当前状态为digit的时候并且前面e已经使用过,类似 5e6 这样数字后就不能再出现e,并且不能再出现dot因为指数必须为整数,所以需要修改成end
if state == "digit" && useE == 1 {
m["digit"][3] = "end"
m["digit"][4] = "end"
}
next := m[state][getChar(s[i])]
state = next
// 如果出现了"."以及"e"需要改变下状态
if s[i] == '.' {
usePoint = 1
}
if s[i] == 'e' {
useE = 1
}
}
// 最后的判断条件 状态必须是digit才是true
return state == "digit"
}

// 对应我们上面说的6种event 对应map中字符串数组的下标0-5
func getChar(c byte) int {
switch c {
case ' ':
return 0
case '+', '-':
return 1
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
return 2
case '.':
return 3
case 'e':
return 4
default:
return 5
}
}

大家也可以自己去看题解

Comments

Redis Hash底层结构

redis的hash底层是由两种数据结构实现的

  1. hashtable

  2. ziplist

其中ziplist是redis的一种节省空间的数据结构,类似数组拥有连续空间,不同于数组的是,数组中的数据都是具有相同长度,ziplist中的每一个数据长度可以不同,具体的可以看下ziplist的实现

本篇文章看下我们在使用hash存数据的时候,redis是如何使用这两种数据结构的

1
2
3
4
5
# Hashes are encoded using a memory efficient data structure when they have a
# small number of entries, and the biggest entry does not exceed a given
# threshold. These thresholds can be configured using the following directives.
hash-max-ziplist-entries 512
hash-max-ziplist-value 64

也就是ziplist-entries的最大值不超过512(这里是指key-value对,也就是entry数据项为1024) 同时 ziplist-value(这里是指任意key或者value的长度)不超过64的情况下,redis内部会使用ziplist来进行存储,其中有一个超过以后就会变为hashtable,我们可以适当调整这个参数以满足我们具体业务需求,但是也不能无限制调大,因为虽然ziplist会节省空间,但是查询没有hashtable快,有利也有弊,注意权衡。

下面我们实际操作,来验证一下

1
2
3
4
5
6
7
8
redis:6379> hset key1 abcdefjhijklmnopqrstuvwxyzabcdefjhijklmnopqrstuvwxyz012345678964 lengthofkeyis64
(integer) 1
redis:6379> object encoding key1
"ziplist"
redis:6379> hset key1 abcdefjhijklmnopqrstuvwxyzabcdefjhijklmnopqrstuvwxyz0123456789645 lengthofkeyis65
(integer) 1
redis:6379> object encoding key1
"hashtable"

可以看到最开始key的长度为64的时候,底层使用ziplist的,当我们把长度改为65,就变为hashtable了

再验证下hash-max-ziplist-entries

1
2
3
4
[xxx@localhost]# redis-cli -h 127.0.0.1 -p 6379 -n 2 hgetall hashtest | wc -l
1024
redis:6379> object encoding hashtest
"ziplist"

再增加一对

1
2
3
4
[xxx@localhost]# redis-cli -h 127.0.0.1 -p 6379 -n 2 hgetall hashtest | wc -l
1026
redis:6379> object encoding hashtest
"hashtable"

我们在使用的时候合理的设置配置文件参数,选择合适的数据结构来存储。

Comments

基于位操作的角色权限设计

Comments

129. 求根到叶子节点数字之和

leetcode每日一题疑惑

今天的leetcode每日一题129. 求根到叶子节点数字之和

看完题以后想了种解法(比较繁琐,层序遍历,记录到根节点的所有数字,最后再求和),不要在意细节,看完题解发现自己写的太复杂了,此文不讨论算法,记录下其中的一个疑惑,希望有大佬给解答下。

先上代码

Read More

Comments

惊喜的一天

2020年10月26日,星期一,本是平凡的一天,枯燥的周一,没想到晚上来了点惊喜,十四五年没见的同学突然联系上了。

初中在离家挺远的安国上学,认识了好多朋友,但是初中毕业以后就回家上学了,好多人慢慢失去了联系,晚上几个常在安国本地的同学聚在一起,想起了我,其中一人留有我的联系方式,还是上大学加上的,给我开了视频,让我一个一个说名字,说实话,好多年了,名字突然叫不上来,但是看着面容都能认出来,还有原来的影子,最大的特点就是全部变胖了。

有太多的话题,激动的说不出话来,有点想哭的感觉,当时大家在一起玩的很好,关系都不错,只是没有联系了,再联系还是感觉很亲切,大家都喝多了,跟他们视频完脑海中一直浮现十四五年前的画面。

初中离家远,老师同学都很照顾我,很感激!

最后约定找时间一定见面好好叙叙旧,太想念大家了,初中的友谊真的深,大部分关系都很好,越往后年龄越大,交心的朋友越来越少了,尤其工作以后,在北京这个快节奏的城市中,大家忙忙碌碌,没有了生活,这一瞬间让人忘记了生活的烦恼,全是对过去美好的回忆,真的很好,无法入眠的一夜。

Comments

leetcode -- 前缀树

leetcode上的一道设计题,要求实现trie这种数据结构,包含插入、查找、前缀查找的操作
https://leetcode-cn.com/problems/implement-trie-prefix-tree/

首先定义Trie struct

1
2
3
4
5
type Trie struct {
children []*Trie
isWord bool //是否为单词
part byte
}

Read More

Comments

groupcache 源码系列(一)一致性hash算法

Hash

1
2
// Hash 在此代码中代表函数类型,返回的是无符号32位整数
type Hash func(data []byte) uint32

Read More

Comments

cache2go 源码解读

引用官方的说明

Concurrency-safe golang caching library with expiration capabilities.

是一个并行安全的golang缓存库,带有过期功能

go的开源缓存库,对go新手来说比较简单,看了一遍源码,对其中的源码做下解释记录,方便日后重温

代码中为了并行安全,在读写操作中都加了锁(除去一些不变的值,例如:CacheItem的key、data、createdOn)

Read More

Comments

2020年中的思考

Comments