时空主站

嗨,我是时空,一名来自中国的开发者。

0%

Python学习

第一个Python程序

1
2
>>> 100+200
300

如果要让Python打印出指定的文字,可以用print()函数,然后把希望打印的文字用单引号或者双引号括起来,但不能混用单引号和双引号:
第一个hello world!

1
2
>>> print('hello, world')
hello, world

这种用单引号或者双引号括起来的文本在程序中叫字符串,今后我们还会经常遇到。

最后,用exit()退出Python,我们的第一个Python程序完成!唯一的缺憾是没有保存下来,下次运行时还要再输入一遍代码。

输入和输出

用print()在括号中加上字符串,就可以向屏幕上输出指定的文字。比如输出’hello, world’,用代码实现如下:

1
>>> print('hello, world')

输出

print()函数也可以接受多个字符串,用逗号“,”隔开,就可以连成一串输出:

1
2
>>> print('The quick brown fox', 'jumps over', 'the lazy dog')
The quick brown fox jumps over the lazy dog

print()也可以打印整数,或者计算结果:

1
2
3
4
>>> print(300)
300
>>> print(100 + 200)
300

因此,我们可以把计算100 + 200的结果打印得更漂亮一点:

1
2
>>> print('100 + 200 =', 100 + 200)
100 + 200 = 300

输入

现在,你已经可以用print()输出你想要的结果了。但是,如果要让用户从电脑输入一些字符怎么办?Python提供了一个input(),可以让用户输入字符串,并存放到一个变量里。比如输入用户的名字:

1
2
3
4
>>> name = input()
Michael
name = input()
print('hello,', name)

数据类型和变量

数据类型

整数 浮点数 字符串

字符串:’I'm "OK"!’表示的字符串内容是:I’m “OK”!

布尔值

布尔值和布尔代数的表示完全一致,一个布尔值只有True、False两种值,要么是True,要么是False,在Python中,可以直接用True、False表示布尔值(请注意大小写),也可以通过布尔运算计算出来:

1
2
3
4
5
6
7
8
>>> True
True
>>> False
False
>>> 3 > 2
True
>>> 3 > 5
False

布尔值可以用and、or和not运算

1
2
3
4
5
6
7
8
>>> True and True
True
>>> True and False
False
>>> False and False
False
>>> 5 > 3 and 3 > 1
True

not运算是非运算,它是一个单目运算符,把True变成False,False变成True:

1
2
3
4
5
6
>>> not True
False
>>> not False
True
>>> not 1 > 2
True

布尔值经常用在条件判断中,比如:

1
2
3
4
if age >= 18:
print('adult')
else:
print('teenager')

常量

所谓常量就是不能变的变量,比如常用的数学常数π就是一个常量。在Python中,通常用全部大写的变量名表示常量:

PI = 3.14159265359

最后解释一下整数的除法为什么也是精确的。在Python中,有两种除法,一种除法是/:

1
2
>>> 10 / 3
3.3333333333333335

除法计算结果是浮点数,即使是两个整数恰好整除,结果也是浮点数:

1
2
>>> 9 / 3
3.0

还有一种除法是//,称为地板除,两个整数的除法仍然是整数:

1
2
>>> 10 // 3
3

Python的字符串

1
2
>>> print('包含中文的str')
包含中文的str

对于单个字符的编码,Python提供了ord()函数获取字符的整数表示,chr()函数把编码转换为对应的字符:

1
2
3
4
5
6
7
8
>>> ord('A')
65
>>> ord('中')
20013
>>> chr(66)
'B'
>>> chr(25991)
'文'

要计算str包含多少个字符,可以用len()函数:

1
2
3
4
>>> len('ABC')
3
>>> len('中文')
2

len()函数计算的是str的字符数,如果换成bytes,len()函数就计算字节数:

1
2
3
4
5
6
>>> len(b'ABC')
3
>>> len(b'\xe4\xb8\xad\xe6\x96\x87')
6
>>> len('中文'.encode('utf-8'))
6

格式化

在Python中,采用的格式化方式和C语言是一致的,用%实现,举例如下:

1
2
3
4
>>> 'Hello, %s' % 'world'
'Hello, world'
>>> 'Hi, %s, you have $%d.' % ('Michael', 1000000)
'Hi, Michael, you have $1000000.'

如果你不太确定应该用什么,%s永远起作用,它会把任何数据类型转换为字符串:

1
2
>>> 'Age: %s. Gender: %s' % (25, True)
'Age: 25. Gender: True'

有些时候,字符串里面的%是一个普通字符怎么办?这个时候就需要转义,用%%来表示一个%:

1
2
>>> 'growth rate: %d %%' % 7
'growth rate: 7 %'

使用list和tuple

list

Python内置的一种数据类型是列表:list。list是一种有序的集合,可以随时添加和删除其中的元素。

比如,列出班里所有同学的名字,就可以用一个list表示:

1
2
3
>>> classmates = ['Michael', 'Bob', 'Tracy']
>>> classmates
['Michael', 'Bob', 'Tracy']

变量classmates就是一个list。用len()函数可以获得list元素的个数:

1
2
>>> len(classmates)
3

用索引来访问list中每一个位置的元素,记得索引是从0开始的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> classmates[0]
'Michael'
>>> classmates[1]
'Bob'
>>> classmates[2]
'Tracy'
>>> classmates[3]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
​```</module></stdin>

如果要取最后一个元素,除了计算索引位置外,还可以用-1做索引,直接获取最后一个元素:

​```python
>>> classmates[-1]
'Tracy'

以此类推,可以获取倒数第2个、倒数第3个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> classmates[-2]
'Bob'
>>> classmates[-3]
'Michael'
>>> classmates[-4]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
​```</module></stdin>

list是一个可变的有序表,所以,可以往list中追加元素到末尾:
​```python
>>> classmates.append('Adam')
>>> classmates
['Michael', 'Bob', 'Tracy', 'Adam']

要删除list末尾的元素,用pop()方法:

1
2
3
4
>>> classmates.pop()
'Adam'
>>> classmates
['Michael', 'Jack', 'Bob', 'Tracy']

也可以把元素插入到指定的位置,比如索引号为1的位置:

1
2
3
>>> classmates.insert(1, 'Jack')
>>> classmates
['Michael', 'Jack', 'Bob', 'Tracy', 'Adam']

list元素也可以是另一个list,比如:

1
2
3
>>> s = ['python', 'java', ['asp', 'php'], 'scheme']
>>> len(s)
4

tuple

另一种有序列表叫元组:tuple。tuple和list非常类似,但是tuple一旦初始化就不能修改,比如同样是列出同学的名字:

1
>>> classmates = ('Michael', 'Bob', 'Tracy')

现在,classmates这个tuple不能变了,它也没有append(),insert()这样的方法。其他获取元素的方法和list是一样的,你可以正常地使用classmates[0],classmates[-1],但不能赋值成另外的元素。

1
2
3
>>> t = (1, 2)
>>> t
(1, 2)

如果要定义一个空的tuple,可以写成():

1
2
3
>>> t = ()
>>> t
()

但是,要定义一个只有1个元素的tuple,如果你这么定义:

1
2
3
>>> t = (1)
>>> t
1

定义的不是tuple,是1这个数!这是因为括号()既可以表示tuple,又可以表示数学公式中的小括号,这就产生了歧义,因此,Python规定,这种情况下,按小括号进行计算,计算结果自然是1。

1
2
3
>>> t = (1,)
>>> t
(1,)

Python在显示只有1个元素的tuple时,也会加一个逗号,,以免你误解成数学计算意义上的括号。

最后来看一个“可变的”tuple:

1
2
3
4
5
>>> t = ('a', 'b', ['A', 'B'])
>>> t[2][0] = 'X'
>>> t[2][1] = 'Y'
>>> t
('a', 'b', ['X', 'Y'])

条件判断

1
2
3
4
age = 20
if age >= 18:
print('your age is', age)
print('adult')

当然上面的判断是很粗略的,完全可以用elif做更细致的判断:

1
2
3
4
5
6
7
age = 3
if age >= 18:
print('adult')
elif age >= 6:
print('teenager')
else:
print('kid')

循环

Python的循环有两种,一种是for…in循环,依次把list或tuple中的每个元素迭代出来,看例子:

1
2
3
names = ['Michael', 'Bob', 'Tracy']
for name in names:
print(name)

再比如我们想计算1-10的整数之和,可以用一个sum变量做累加:

1
2
3
4
sum = 0
for x in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]:
sum = sum + x
print(sum)

如果要计算1-100的整数之和,从1写到100有点困难,幸好Python提供一个range()函数,可以生成一个整数序列,再通过list()函数可以转换为list。比如range(5)生成的序列是从0开始小于5的整数:

1
2
3
4
sum = 0
for x in range(101):
sum = sum + x
print(sum)

使用dict和set

Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度。

举个例子,假设要根据同学的名字查找对应的成绩,如果用list实现,需要两个list:

1
2
3
4
5
6
7
8
9
names = ['Michael', 'Bob', 'Tracy']
scores = [95, 75, 85]

``
如果用dict实现,只需要一个“名字”-“成绩”的对照表,直接根据名字查找成绩,无论这个表有多大,查找速度都不会变慢。用Python写一个dict如下:
​```python
>>> d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
>>> d['Michael']
95

把数据放入dict的方法,除了初始化时指定外,还可以通过key放入:

1
2
3
>>> d['Adam'] = 67
>>> d['Adam']
67

由于一个key只能对应一个value,所以,多次对一个key放入value,后面的值会把前面的值冲掉:

1
2
3
4
5
6
>>> d['Jack'] = 90
>>> d['Jack']
90
>>> d['Jack'] = 88
>>> d['Jack']
88

要避免key不存在的错误,有两种办法,一是通过in判断key是否存在:

1
2
>>> 'Thomas' in d
False

要删除一个key,用pop(key)方法,对应的value也会从dict中删除:

1
2
3
4
>>> d.pop('Bob')
75
>>> d
{'Michael': 95, 'Tracy': 85}

和list比较,dict有以下几个特点:

1.查找和插入的速度极快,不会随着key的增加而增加;
2.需要占用大量的内存,内存浪费多。
而list相反:

1.查找和插入的时间随着元素的增加而增加;
2.占用空间小,浪费内存很少。

set

set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key。

要创建一个set,需要提供一个list作为输入集合:

1
2
3
>>> s = set([1, 2, 3])
>>> s
{1, 2, 3}

注意,传入的参数[1, 2, 3]是一个list,而显示的{1, 2, 3}只是告诉你这个set内部有1,2,3这3个元素,显示的顺序也不表示set是有序的。。

重复元素在set中自动被过滤:

1
2
3
>>> s = set([1, 1, 2, 2, 3, 3])
>>> s
{1, 2, 3}

通过add(key)方法可以添加元素到set中,可以重复添加,但不会有效果:

1
2
3
4
5
6
>>> s.add(4)
>>> s
{1, 2, 3, 4}
>>> s.add(4)
>>> s
{1, 2, 3, 4}

通过remove(key)方法可以删除元素:

1
2
3
>>> s.remove(4)
>>> s
{1, 2, 3}

set可以看成数学意义上的无序和无重复元素的集合,因此,两个set可以做数学意义上的交集、并集等操作:

1
2
3
4
5
6
>>> s1 = set([1, 2, 3])
>>> s2 = set([2, 3, 4])
>>> s1 & s2
{2, 3}
>>> s1 | s2
{1, 2, 3, 4}

不可变对象

对于可变对象,比如list,对list进行操作,list内部的内容是会变化的,比如:

1
2
3
4
>>> a = ['c', 'b', 'a']
>>> a.sort()
>>> a
['a', 'b', 'c']

而对于不可变对象,比如str,对str进行操作:

1
2
3
4
5
>>> a = 'abc'
>>> a.replace('a', 'A')
'Abc'
>>> a
'abc'

函数

调用函数

调用abs函数:

1
2
3
4
5
6
>>> abs(100)
100
>>> abs(-20)
20
>>> abs(12.34)
12.34

而max函数max()可以接收任意多个参数,并返回最大的那个:

1
2
3
4
>>> max(1, 2)
2
>>> max(2, 3, 1, -5)
3

数据类型转换

Python内置的常用函数还包括数据类型转换函数,比如int()函数可以把其他数据类型转换为整数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> int('123')
123
>>> int(12.34)
12
>>> float('12.34')
12.34
>>> str(1.23)
'1.23'
>>> str(100)
'100'
>>> bool(1)
True
>>> bool('')
False

函数名其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”:

1
2
3
>>> a = abs # 变量a指向abs函数
>>> a(-1) # 所以也可以通过a调用abs函数
1

定义函数

我们以自定义一个求绝对值的my_abs函数为例:

1
2
3
4
5
def my_abs(x):
if x >= 0:
return x
else:
return -x

返回多个值

比如在游戏中经常需要从一个点移动到另一个点,给出坐标、位移和角度,就可以计算出新的新的坐标:

1
2
3
4
5
6
import math

def move(x, y, step, angle=0):
nx = x + step * math.cos(angle)
ny = y - step * math.sin(angle)
return nx, ny

然后,我们就可以同时获得返回值:

1
2
3
>>> x, y = move(100, 100, 60, math.pi / 6)
>>> print(x, y)
151.96152422706632 70.0

函数的参数

位置参数

我们先写一个计算x*x的函数:

1
2
def power(x):
return x * x

当我们调用power函数时,必须传入有且仅有的一个参数x:

1
2
3
4
>>> power(5)
25
>>> power(15)
225

现在,如果我们要计算x3怎么办?可以再定义一个power3函数,但是如果要计算x4、x5……怎么办?我们不可能定义无限多个函数。

你也许想到了,可以把power(x)修改为power(x, n),用来计算xn,说干就干:

1
2
3
4
5
6
def power(x, n):
s = 1
while n > 0:
n = n - 1
s = s * x
return s

对于这个修改后的power(x, n)函数,可以计算任意n次方:

1
2
3
4
>>> power(5, 2)
25
>>> power(5, 3)
125

递归函数

举个例子,我们来计算阶乘n! = 1 x 2 x 3 x … x n,用函数fact(n)表示,可以看出:

fact(n) = n! = 1 x 2 x 3 x … x (n-1) x n = (n-1)! x n = fact(n-1) x n

于是,fact(n)用递归的方式写出来就是:

1
2
3
4
def fact(n):
if n==1:
return 1
return n * fact(n - 1)

上面就是一个递归函数。可以试试:

1
2
3
4
5
6
>>> fact(1)
1
>>> fact(5)
120
>>> fact(100)
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

上面的fact(n)函数由于return n * fact(n - 1)引入了乘法表达式,所以就不是尾递归了。要改成尾递归方式,需要多一点代码,主要是要把每一步的乘积传入到递归函数中:

1
2
3
4
5
6
7
def fact(n):
return fact_iter(n, 1)

def fact_iter(num, product):
if num == 1:
return product
return fact_iter(num - 1, num * product)

尾递归调用时,如果做了优化,栈不会增长,因此,无论多少次调用也不会导致栈溢出。

环境变量GNUPGHOME:C:\Program Files (x86)\GnuPG\bin\gpg.exe

1
2
3
4
5
git config --global gpg.program "C:\Program Files (x86)\GnuPG\bin\gpg.exe"

git config --global user.signingkey 你的公钥

git config --global commit.gpgsign true

敏捷的意思就是反应迅速,为什么要反应迅速? 看看那么多996公司就知道了,

市场变化越来越快,客户要求越来越高,为了满足用户的需求, 人家一个星期发一个版本, 我们仨月才能憋出一个来 ,那还不被打的满地找牙?

问题是如何才能反应迅速? 先来看一个场景:

1、残酷的现实

软件开发有一大难题就是客户脑子中的需求难于描述出来, 我们通常的应对方法是这样:

先花上几个月整理需求, 天天和客户座谈, 画出几百页的流程图, 写出上千页的文档, 最后把客户都快搞晕了。

img

然后是详细设计, 开发, 测试, 我们强悍的技术团队开始发动, 一切都严格按照计划进行, 一切看起来都很完美, 看来项目马上成功结束了!

但是客户的验收测试给了我们当头一棒: 这个界面怎么少了一个选项 ? 那个界面怎么不能跳转 , 那个功能需要给领导一个后门, 还有, 我的业务规则怎么不能改? 什么? 在代码中写死了? 唉,你们做的系统啊, 根本就不能用 !

每个人都很郁闷, 几个月的辛苦开发看来要付诸东流了。

从这个场景中能看出的是, 我们从客户那里得到的需求描述和需求文档, 其实离客户真正想要的软件还差的很远。

在瀑布式的开发模式下,验收测试发现的问题,要想改正代价是非常高昂的。

2、改进

一个想法自然而然就浮现出来: 为了避免到最后习惯性崩盘,能不能让客户经常性的做验收测试?

让他们经常性的去使用一个可以工作的软件, 从而告诉我们那些地方还有欠缺 ? 那些地方做错了? 这样我们可以迅速的修改, 这样我们就会轻松多了 !

我们可以把软件开发划分成一个个小的开发周期, 例如每个周期就两三周时间, 在这两周之内我们完成一个或几个功能, 然后就让用户去试用, 有问题立刻反馈,在下一个开发周期马上改掉。 这样就可以逐步逼近客户的最终目标。

这还带来了一个额外的好处, 不用花费巨长的时间来分析,整理冗长的需求文档了。

听起来很美是不是? 但是仔细想想这里边的问题很多。

1. 抛弃了冗长的需求文档, 但还是得描述需求啊

需要发明一个简单的、主要用来促进客户和开发团队沟通的描述形式, 这个新的形式叫做用户故事, 这里有个用户故事的例子:

img

这是一个卡片, 背面还会记录下针对需求的讨论和验收标准。

用户故事主要彰显的是: 谁做了什么事, 带来什么商业价值。

2. 怎么决定每个小开发周期(我们称之为迭代吧)要开发的东西?

用户故事得有估算, 得有大小, 太大了一个迭代开发不完 , 还得拆分一下。

我们需要对需求按照优先级进行排序, 按照优先级从高到低的原则来开发。

3. 不要架构设计了吗?

一上来就按优先级选择需求, 直接进入迭代开发, 把架构师撂在一边,合适吗?

架构工作肯定还是需要的,在正式的迭代周期开始之前需要架构设计, 但是和设计出面面俱到的架构设计不同, 我们更需要演进式的架构, 随着迭代的推进而进化。

4. 那详细设计怎么办?

在每个迭代开始的时候,团队在一起把这些用户故事给拆分成一个个小的任务, 这个拆分的过程就相当于详细设计了。 对于一些特别复杂的,例如算法, 当然可以写文档,帮助大家理解。

5. 由于是迭代式开发, 这个迭代周期修改上一个迭代周期的代码在所难免, 怎么保证不破坏原有的功能? 总不能每次都手工重测一遍吧?

这个绝对是一大难点, 答案就是自动化的回归测试, 包括单元测试和功能测试。

开发人员写代码的同时,也要写下自动化的单元测试, 测试人员需要开发自动化的功能测试, 这样一旦有了代码的修改,就可以运行它们, 检查现有功能有没有被破坏。

像持续集成这样的基础设施是必不可少的, 每天,每小时,甚至每次代码提交都会触发编译,打包、部署、测试这样的过程。

6. 这么短的开发周期, 测试人员怎么测试啊?

开发和测试需要同步进行, 当开发在澄清需求的时候, 测试需要参与, 当开发在编码的时候, 测试人员在编写测试用例,等到一个用户故事开发完,马上就可以投入测试。

7. 看来开发、测试之间需要紧密的协作, 它们之间怎么沟通?

肯定是面对面的沟通, 有问题就跑到对方的座位那里去问,大家的座位最好在一起, 扭头就可以讨论, 尽可能减少效率不高的电话、QQ/微信等工具的使用。

开发团队每天都开一个15分钟左右的站会, 展示自己的进展和计划, 让进度保持透明, 及时暴露问题,解决问题。

8. 客户什么时候可以做验收测试?

随时欢迎, 但是我们更倾向于迭代结束以后, 这时候功能会稳定下来, 我们会给客户做一个演示, 告诉他这个迭代完成的工作, 邀请他也测试一下软件, 给我们反馈。

当然客户可能会发现问题, 甚至提出新的需求, 我们表示欢迎, 我们要和客户合作,而不是对抗。

除了给客户演示之外,我们自己还会反思一下,看看有那些地方做的好,要继续保持; 那些地方做的不好, 要持续改进。

估计你也明白了,这种看起来很美的迭代化开发方法实施起来挺不容易的, 如果我们给它起个名字的话, 可以叫做:敏捷软件开发。

原作者:码农翻身

原文链接:http://mp.weixin.qq.com/s/tJzEQ87eaUdx7F2nR23oyw

码农翻身公众号由工作15年的前IBM架构师创建,分享编程和职场的经验教训。

码农翻身二维码:

img

原文:有没有已经被证实存在的超能力?有能够后天习得的超能力吗?

作者:张天一
链接:https://www.zhihu.com/question/32235004/answer/57412334
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

there is no spoon.
我回来补充了!!
———————————乱七八糟的分割线—————————

知乎上一位信基督的程序员朋友说,“就好比你玩游戏,不能因为在游戏里找不到职业为程序员的 NPC,又无法用游戏中的规则造一个程序员角色出来,就说程序员是假的,因为他有可能在游戏之外的某个维度 debug 。换个说法是,游戏是虚拟的,程序员的存在才是真实。”(侵删致歉)私以为说的非常好。
如果代入这个理论来看的话,世界就有意思了。
一神教和多神教的区别就是这个世界的程序是一位程序员写的还是多位程序员写的,不同教派的区别就是因为自己所处的数据片段不一样所以脑补的神的形象也不一样。
还记得Neo去见先知的那个片段吗?看见那几个孩子,他们每个人都做了违反物理规律的事情,那个意念弯曲勺子的小僧侣对着他说,不要掰它,要看透它。
他说,“There is no spoon.”
很巧的是魔兽争霸3有一个作弊码,There is no spoon,输入后你的英雄魔法是无限的。
如果他们有自己的意识的话他们会意识到世界的规律变化了,而对你而言,你也不过是在利用代码而已。再举一个例子如Minecraft这种沙盒游戏,你可以通过合理的资源配置来完成一些科技,组成与门或门非门来完成牌子电路或者红石电路,甚至做出CPU,而有些你作弊也可以。科学就像是你在摸索mc的世界规律来完成科技,开作弊码就像是直接利用了代码的后门,“看透”了数据的本质(其实并没有真正看透,譬如你使用作弊码只是知其然不知其所以然,只能算作“使用”而非“破解”)。
这就是科学力量和超自然力量的关系。更直接的说,一个是掰勺子,一个是看穿勺子。两者实际上都是在“使用”它,而所谓的唯物世界不过是按照规律运行的一组数据,你可以像在mc里做电路一样发展科技,完成很厉害的事,也可以理解程序破解程序直接的为自己谋福利,两者有区别吗?有。基础一样吗?基础都是这一组数据,问题的关键是,你要怎么去理解它,使用它。你没有玩过mc做不出电路,只知道作弊码也算不得破解程序,你要发展你要趋利避害你要往好的地方走你要活着,你就要学习规律。
你把世界当做一个沙盒游戏,很多的哲学问题都可以迎刃而解。
你面前的勺子,There is no spoon.

(你们再说地球OL我就要报警了!!!)
接下来要给大家介绍一位始祖级程序员,老子。
以及在宗教界独树一帜的宗教,道教。
把上面的话替换几个字比如把规律和程序换成“道”,就基本是道门的要旨了。
下面的话我的评论只涉及纯粹标准的各宗教徒,个人评价无攻击意图。第一,道教确实非常奇特,第二,我对道教了解较深,所以将以道教尤其是早期道教作为一种特异的哲学和宗教现象来讨论,并非由于我是道教徒。道门独树一帜的地方就在于,别的宗教学习世界系统的程序员,我们学习这个程序的运行原理。当然,所有的宗教都引人向善给人以内心的平静,仅仅从利益角度的狭义功利主义而言,撇开信仰不提,其他的宗教大多目的都是希望通过遵从使得程序员对他们有所偏好来获得特殊的权限,而道教敬天地人神,最尊敬的就是道。理解道,参悟道,学习道,使用道,道如同涛涛江河,他们学习水流的方向和规律顺着水流游的游刃有余左右逢源,这是道教徒的本意。亚伯拉罕一神系的三大宗教犹太教伊斯兰教基督教都是一神教,即使有主神和他们的小伙伴们的故事说到底拜的还是一个神,掌管万物;佛教在入中国之前也基本算是释迦牟尼一神主万世法;希腊、美洲、北欧、玛雅那是各自神系下的神话体系,不算宗教。
再回头看看道教就耐人寻味起来。道教没有统领一切的神,而诸神各有职司。耐人寻味之处就在于,绝大多数的神,都是由人而化神,很少有比如火气水气之精或天地交感化而为神。现在,我们回到那个有趣的理论基础上去,即是:
道教徒跪拜的不是设立世界程序的程序员,而是在这个程序里生出来的AI极高高到足以看透数据世界的本质并突破了数据限制的人,并且希望学习他们。
这种人被称为什么呢?神?仙?
都不是。道教徒叫他们为,【真人】。
interesting.
非常非常有趣啊。这些人被叫做真人,那么普通人是什么?
脑补一下Neo突破自己然后在最后飞起来的时候,普通人的表情。于此处黑客帝国全剧终,导演给他们的表情留了白。
当你突破这个世界的时候,不管你能在这个世界有多么厉害,摇山撼海夸神通,目上无尘目下空,你都不会再去珍视了,因为那于你而言不过是…盒子里的世界。
There is no spoon.

来源:知乎

一人我饮酒醉 醉把佳人成双对 两眼 是独相随只求他日能双归

娇女轻扶琴 燕嬉紫竹林 痴情红颜心甘情愿 千里把君寻.

说红颜我痴情笑 曲动琴声妙 我轻狂高傲懵懂无知只怪太年少

弃 江山 忘天下 斩断情丝无牵挂 千古留名传佳话 两年征战以白发

一生征战何人陪 谁是谁非谁相随 戎马一生为了谁 能爱几回恨几回

败 帝王 斗苍天 夺得皇位以成仙 豪情万丈天地间 续写另类帝王篇

红尘事我以斩断 久经战场人心乱 当年扬名又力万 是这一战我无遗憾

相思 我愁断肠 眼中我泪两行 我多年为君一统天下 戎马把名扬

烟雨 我平凡事 此生 我怀大志 我为了家人回眸一笑立下这毒誓

百花 我出芬芳 回首 我曲流觞 我回眸沧海一首忧歌感触梨花香

将军出征人在外 归来之日谁还在 兄弟把酒论豪迈 驰骋疆场求一败

这次走我何时归 寒 风起 心似灰 冷风吹起樱花飞 触景生情心伤悲

琴声悠悠在回荡 一首幽歌为你唱 沧海桑田难遗忘 酒醒燕归来无恙

百花绽放出芬芳 再次回首曲流伤 回眸感触昙花香 一首忧歌笑沧桑

仰望窗外烟雨朦胧 想你当初那笑容 佳人一别月下逢 牡丹花开百花丛

情 已过 义难断 提笔写下江山乱 孤枕难眠梦相伴 花飞花舞花飘散

紫金楼 风起玄 挥舞霸刀几万年 悠悠岁月心缠绵 如何忘记你容颜

恍然如梦烟雨间 雨碎落叶舞翩翩 烟雨飘渺断崖边 待我转世化成仙

为何你要说离别 留我一人寻彩蝶 浮生若梦三字决 留下三字这心结

这 情花 为你摘 一颗心门为你开 平凡事我了尘埃 寂寥之心谁能猜

一别红颜多年后 多年情 在守候 断情之曲谁人奏 思念红颜人消瘦

海誓山盟烟雨楼 苦苦相思两处愁 三声誓言的缘由 只因一生无所求

梦里梦外千百年 可曾记得那誓言 今生情 来世缘 难忘二字为红颜

千 百里 相思雨 相 思雨 落谷底 红尘往事梦一曲 期盼三字风云起

脑中浮现一幕幕 一幕幕 江湖路 谁懂我心痛苦处 三声誓言以颠覆

梦里 谁独相思 为何要寻一相知 梦外 我望明月 只为一人情难却

企业版

vs_enterprise.exe –layout c:\vs2017offline

专业版

vs_professional.exe –layout c:\vs2017offline

社区版

vs_community.exe –layout c:\vs2017offline

企业版 Enterprise:NJVYC-BMHX2-G77MM-4XJMR-6Q8QF

专业版 Professional:KBJFW-NXHK6-W4WJM-CRMQB-G3CDH

来自 OSCHINA

昨天,Gitlab.com发生了一个大事,某同学误删了数据库,这个事看似是个低级错误,不过,因为Gitlab把整个过程的细节都全部暴露出来了,所以,可以看到很多东西,而对于类似这样的事情,我自己以前也干过,而在最近的两公司中我也见过(Amazon中见过一次,阿里中见过至少四次),正好通过这个事来说说一下自己的一些感想和观点吧。我先放个观点:你觉得有备份系统就不会丢数据了吗?

事件回顾

整个事件的回顾Gitlab.com在第一时间就放到了Google Doc上,事后,又发了一篇Blog来说明这个事,在这里,我简单的回顾一下这个事件的过程。 首先,一个叫YP的同学在给gitlab的线上数据库做一些负载均衡的工作,在做这个工作时的时候突发了一个情况,Gitlab被DDoS攻击,数据库的使用飙高,在block完攻击者的IP后,发现有个staging的数据库(db2.staging)已经落后生产库4GB的数据,于是YP同学在Fix这个staging库的同步问题的时候,发现db2.staging有各种问题都和主库无法同步,在这个时候,YP同学已经工作的很晚了,在尝试过多个方法后,发现db2.staging都hang在那里,无法同步,于是他想把db2.staging的数据库删除了,这样全新启动一个新的复制,结果呢,删除数据库的命令错误的敲在了生产环境上(db1.cluster),结果导致整个生产数据库被误删除。(陈皓注:这个失败基本上就是 “工作时间过长” + “在多数终端窗口中切换中迷失掉了”) 在恢复的过程中,他们发现只有db1.staging的数据库可以用于恢复,而其它的5种备份机制都不可用,第一个是数据库的同步,没有同步webhook,第二个是对硬盘的快照,没有对数据库做,第三个是用pg_dump的备份,发现版本不对(用9.2的版本去dump 9.6的数据)导致没有dump出数据,第四个S3的备份,完全没有备份上,第五个是相关的备份流程是问题百出的,只有几个粗糙的人肉的脚本和糟糕的文档,也就是说,不但是是人肉的,而且还是完全不可执行的。(陈皓注:就算是这些备份机制都work,其实也有问题,因为这些备份大多数基本上都是24小时干一次,所以,要从这些备份恢复也一定是是要丢数据的了,只有第一个数据库同步才会实时一些) 最终,gitlab从db1.staging上把6个小时前的数据copy回来,结果发现速度非常的慢,备份结点只有60Mbits/S,拷了很长时间(陈皓注:为什么不把db1.staging给直接变成生产机?因为那台机器的性能很差)。数据现在的恢复了,不过,因为恢复的数据是6小时前的,所以,有如下的数据丢失掉了:

  • 粗略估计,有4613 的项目, 74 forks,  和 350 imports 丢失了;但是,因为Git仓库还在,所以,可以从Git仓库反向推导数据库中的数据,但是,项目中的issues等就完全丢失了。
  • 大约有±4979 提交记录丢失了(陈皓注:估计也可以用git仓库中反向恢复)。
  • 可能有 707  用户丢失了,这个数据来自Kibana的日志。
  • 在1月31日17:20 后的Webhooks 丢失了。

因为Gitlab把整个事件的细节公开了出来,所以,也得到了很多外部的帮助,2nd Quadrant的CTO – Simon Riggs 在他的blog上也发布文章 Dataloss at Gitlab 给了一些非常不错的建议:

  • 关于PostgreSQL 9.6的数据同步hang住的问题,可能有一些Bug,正在fix中。
  • PostgreSQL有4GB的同步滞后是正常的,这不是什么问题。
  • 正常的停止从结点,会让主结点自动释放WALSender的链接数,所以,不应该重新配置主结点的 max_wal_senders 参数。但是,停止从结点时,主结点的复数连接数不会很快的被释放,而新启动的从结点又会消耗更多的链接数。他认为,Gitlab配置的32个链接数太高了,通常来说,2到4个就足够了。
  • 另外,之前gitlab配置的max_connections=8000太高了,现在降到2000个是合理的。
  • pg_basebackup 会先在主结点上建一个checkpoint,然后再开始同步,这个过程大约需要4分钟。
  • 手动的删除数据库目录是非常危险的操作,这个事应该交给程序来做。推荐使用刚release 的 repmgr
  • 恢复备份也是非常重要的,所以,也应该用相应的程序来做。推荐使用 barman (其支持S3)
  • 测试备份和恢复是一个很重要的过程。

看这个样子,估计也有一定的原因是——Gitlab的同学对PostgreSQL不是很熟悉。 随后,Gitlab在其网站上也开了一系列的issues,其issues列表在这里 Write post-mortem (这个列表可能还会在不断更新中)

从上面的这个列表中,我们可以看到一些改进措施了。挺好的,不过我觉得还不是很够。

相关的思考

因为类似这样的事,我以前也干过(误删除过数据库,在多个终端窗口中迷失掉了自己所操作的机器……),而且我在amazon里也见过一次,在阿里内至少见过四次以上(在阿里人肉运维的误操作的事故是我见过最多的),但是我无法在这里公开分享,私下可以分享。在这里,我只想从非技术和技术两个方面分享一下我的经验和认识。

技术方面

人肉运维 一直以来,我都觉得直接到生产线上敲命令是一种非常不好的习惯。我认为,一个公司的运维能力的强弱和你上线上环境敲命令是有关的,你越是喜欢上线敲命令你的运维能力就越弱,越是通过自动化来处理问题,你的运维能力就越强。理由如下: 其一,如果说对代码的改动都是一次发布的话,那么,对生产环境的任何改动(包括硬件、操作系统、网络、软件配置……),也都算是一次发布。那么这样的发布就应该走发布系统和发布流程,要被很好的测试、上线和回滚计划。关键是,走发布过程是可以被记录、追踪和回溯的,而在线上敲命令是完全无法追踪的。没人知道你敲了什么命令。 其二,真正良性的运维能力是——人管代码,代码管机器,而不是人管机器。你敲了什么命令没人知道,但是你写个工具做变更线上系统,这个工具干了什么事,看看工具的源码就知道了。 另外、有人说,以后不要用rm了,要用mv,还有人说,以后干这样的事时,一个人干,另一个人在旁边看,还有人说,要有一个checklist的强制流程做线上的变更,还有人说要增加一个权限系统。我觉得,这些虽然可以work,但是依然不好,再由如下: 其一、如果要解决一个事情需要加更多的人来做的事,那这事就做成劳动密集型了。今天我们的科技就是在努力消除人力成本,而不是在增加人力成本。而做为一个技术人员,解决问题的最好方式是努力使用技术手段,而不是使用更多的人肉手段。人类区别于动物的差别就是会发明和使用现代化的工具,而不是使用更多的人力。另外,这不仅仅因为是,人都是会有这样或那样的问题(疲惫、情绪化、急燥、冲动……),而机器是单一无脑不知疲惫的,更是因为,机器干活的效率和速度是比人肉高出N多倍的。 其二、增加一个权限系统或是别的一个watch dog的系统完全是在开倒车,权限系统中的权限谁来维护和审批?不仅仅是因为多出来的系统需要多出来的维护,关键是这个事就没有把问题解决在root上。除了为社会解决就业问题,别无好处,故障依然会发生,有权限的人一样会误操作。对于Gitlab这个问题,正如2nd Quadrant的CTO建议的那样,你需要的是一个自动化的备份和恢复的工具,而不是一个权限系统。 其三、像使用mv而不rm,搞一个checklist和一个更重的流程,更糟糕。这里的逻辑很简单,因为,1)这些规则需要人去学习和记忆,本质上来说,你本来就不相信人,所以你搞出了一些规则和流程,而这些规则和流程的执行,又依赖于人,换汤不换药,2)另外,写在纸面上的东西都是不可执行的,可以执行的就是只有程序,所以,为什么不把checklist和流程写成代码呢?(你可能会说程序也会犯错,是的,程序的错误是consistent,而人的错误是inconsistent) 最关键的是,数据丢失有各种各样的情况,不单单只是人员的误操作,比如,掉电、磁盘损坏、中病毒等等,在这些情况下,你设计的那些想流程、规则、人肉检查、权限系统、checklist等等统统都不管用了,这个时候,你觉得应该怎么做呢?是的,你会发现,你不得不用更好的技术去设计出一个高可用的系统!别无它法。

关于备份

一个系统是需要做数据备份的,但是,你会发现,Gitlab这个事中,就算所有的备份都可用,也不可避免地会有数据的丢失,或是也会有很多问题。理由如下: 1)备份通常来说都是周期性的,所以,如果你的数据丢失了,从你最近的备份恢复数据里,从备份时间到故障时间的数据都丢失了。 2)备份的数据会有版本不兼容的问题。比如,在你上次备份数据到故障期间,你对数据的scheme做了一次改动,或是你对数据做了一些调整,那么,你备份的数据就会和你线上的程序出现不兼容的情况。 3)有一些公司或是银行有灾备的数据中心,但是灾备的数据中心没有一天live过。等真正灾难来临需要live的时候,你就会发现,各种问题让你live不起来。你可以读一读几年前的这篇报道好好感受一下《以史为鉴 宁夏银行7月系统瘫痪最新解析》 所以,在灾难来临的时候,你会发现你所设计精良的“备份系统”或是“灾备系统”就算是平时可以工作,但也会导致数据丢失,而且可能长期不用的备份系统很难恢复(比如应用、工具、数据的版本不兼容等问题)。 我之前写过一篇《分布式系统的事务处理》,你还记得下面这张图吗?看看 Data Loss 那一行的,在Backups, Master/Slave 和 Master/Master的架构下,都是会丢的。 所以说,如果你要让你的备份系统随时都可以用,那么你就要让它随时都Live着,而随时都Live着的多结点系统,基本上就是一个分布式的高可用的系统。因为,数据丢失的原因有很多种,比如掉电、磁盘损坏、中病毒等等,而那些流程、规则、人肉检查、权限系统、checklist等等都只是让人不要误操作,都不管用,这个时候,你不得不用更好的技术去设计出一个高可用的系统!别无它法。(重要的事,得再说一篇) 另外,你可以参看我的另一篇《关于高可用系统》,这篇文章中以MySQL为例,数据库的replication也只能达到 两个9。 AWS 的 S3 的的高可用是4个加11个9的持久性(所谓11个9的持久性durability,AWS是这样定义的,如果你存了1万个对象,那么丢一个的时间是1000万年),这意味着,不仅仅只是硬盘坏,机器掉电,整个机房挂了,其保证可以承受有两个设施的数据丢失,数据还是可用的。试想,如果你把数据的可用性通过技术做到了这个份上,那么,你还怕被人误删一个结点上的数据吗?

非技术方面

故障反思 一般说来,故障都需要反思,在Amazon,S2以上的故障都需要写COE(Correction of Errors),其中一节就是需要Ask 5 Whys,我发现在Gitlab的故障回顾的blog中第一段中也有说要在今天写个Ask 5 Whys。关于Ask 5 Whys,其实并不是亚马逊的玩法,这还是算一个业内常用的玩法,也就是说不断的为自己为为什么,直到找到问题的概本原因,这会逼着所有的当事人去学习和深究很多东西。在Wikipedia上有相关的词条 5 Whys,其中罗列了14条规则:

  1. 你需要找到正确的团队来完成这个故障反思。
  2. 使用纸或白板而不是电脑。
  3. 写下整个问题的过程,确保每个人都能看懂。
  4. 区别原因和症状。
  5. 特别注意因果关系。
  6. 说明Root Cause以及相关的证据。
  7. 5个为什么的答案需要是精确的。
  8. 寻找问题根源的步骤,而不是直接跳到结论。
  9. 要基础客观的事实、数据和知识。
  10. 评估过程而不是人。
  11. 千万不要把“人为失误”或是“工作不注意”当成问题的根源。
  12. 培养信任和真诚的气氛和文化。
  13. 不断的问“为什么”直到问题的根源被找到。这样可以保证同一个坑不会掉进去两次。
  14. 当你给出“为什么”的答案时,你应该从用户的角度来回答。

工程师文化 上述的这些观点,其实,我在我的以住的博客中都讲过很多遍了,你可以参看《什么是工程师文化?》以及《开发团队的效率》。其实,说白了就是这么一个事——如果你是一个技术公司,你就会更多的相信技术而不是管理。相信技术会用技术来解决问题,相信管理,那就只会有制度、流程和价值观来解决问题。 这个道理很简单,数据丢失有各种各样的情况,不单单只是人员的误操作,比如,掉电、磁盘损坏、中病毒等等,在这些情况下,你设计的那些流程、规则、人肉检查、权限系统、checklist等等统统都不管用,这个时候,你觉得应该怎么做呢?是的,你会发现,你不得不用更好的技术去设计出一个高可用的系统!别无它法。(重要的事得说三遍) 事件公开 很多公司基本上都是这样的套路,首先是极力掩盖,如果掩盖不了了就开始撒谎,撒不了谎了,就“文过饰非”、“避重就轻”、“转移视线”。然而,面对危机的最佳方法就是——“多一些真诚,少一些套路”,所谓的“多一些真诚”的最佳实践就是——“透明公开所有的信息”,Gitlab此次的这个事给大家树立了非常好的榜样。AWS也会把自己所有的故障和细节都批露出来。 事情本来就做错了,而公开所有的细节,会让大众少很多猜测的空间,有利于抵制流言和黑公关,同时,还会赢得大众的理解和支持。看看Gitlab这次还去YouTube上直播整个修复过程,是件很了不起的事,大家可以到他们的blog上看看,对于这样的透明和公开,一片好评。 (全文完)

来源:酷壳

阿里他爸?

旧金山——雅虎(Yahoo)或将自己的互联网业务以48亿美元的价格卖给威瑞森通讯公司(Verizon Communications),这笔交易的命运可能尚不确定。但是如果它成为现实,雅虎亦为公司剩下的业务做好了计划。

周一,该公司在一份监管备案文件中称,交易完成后,它将更名为Altaba。

此外,该公司目前半数以上的董事会成员将退出,包括首席执行官玛丽莎·梅耶尔(Marissa Mayer)在内。

为什么叫Altaba呢?

如果与威瑞森公司的交易能够完成,雅虎所剩的最大单笔资产就是在中国电子商务巨头阿里巴巴公司(Alibaba)所持的15%股份,Altaba这个名字其实就是在“阿里巴巴”的基础上稍作修改。Altaba还将持有雅虎日本35.5%的股份(雅虎的一名发言人拒绝作出评论)。

不过,Altaba当然是一个不同寻常的名字。它碰巧还很接近Al-Taba,那是巴基斯坦的一家剪刀制造公司的名字。

该公司在监管备案文件中称,更名后仍将留下的董事包括帮助推动公司改革的激进投资人杰弗里·史密斯(Jeffrey Smith);前投资银行家托尔·布雷厄姆(Tor Braham)和凯瑟琳·J·弗里德曼(Catherine J. Friedman);芯片制造公司博通(Broadcom)的前首席财务官埃里克· 勃兰特(Eric Brandt);以及媒体公司IAC的前首席财务官托马斯·麦金纳尼(Thomas McInerney)。

即将退位的董事包括梅耶尔;雅虎董事长梅纳德·韦伯(Maynard Webb);以及雅虎创始人之一大卫·费罗(David Filo)。韦伯将成为更名后的Altaba的荣退主席。

当然,所有这些变化取决于雅虎能否真的把自己最重要的互联网业务卖给威瑞森,要知道,雅虎曾两次遭遇黑客袭击,导致信息泄露,第二次袭击影响了10多亿用户的帐户。

威瑞森的高管曾公开表示,他们在权衡自己的选择,包括支付的金额可能会少于已达成一致的48亿美元。上周,威瑞森的产品创新和新业务主管玛妮·沃尔登(Marni Walden)在谈起这项交易的命运时说,“今天,我不能坐在这里确定地说会发生什么,因为我们还不知道。”

不过,隶属于威瑞森的美国在线(AOL)的首席执行官蒂姆·阿姆斯特朗(Tim Armstrong)在接受CNBC采访时称,他感到乐观。

“我依然对达成这项交易充满希望,我觉得,与此同时,我们会看看雅虎调查的结果,”他说。

来源:纽时中文

这篇文章来源于Quroa的一个问答《What are some time-saving tips that every Linux user should know?》—— Linux用户有哪些应该知道的提高效率的技巧。我觉得挺好的,总结得比较好,把其转过来,并加了一些自己的理解。 首先,我想告诉大家,在Unix/Linux下,最有效率技巧的不是操作图形界面,而是命令行操作,因为命令行意味着自动化。如果你看过《你可能不知道的Shell》以及《28个Unix/Linux的命令行神器》你就会知道Linux有多强大,这个强大完全来自于命令行,于是,就算你不知道怎么去做一个环保主义的程序员,至少他们可以让你少熬点夜,从而有利于你的身体健康和性生活。下面是一个有点长的列表,正如作者所说,你并不需要知道所有的这些东西,但是如果你还在很沉重地在使用Linux的话,这些东西都值得你看一看。 (注:如果你想知道下面涉及到的命令的更多的用法,你一定要man一点。对于一些命令,你可以需要先yum或apt-get来安装一下,如果有什么问题,别忘了Google。如果你要Baidu的话,我仅代表这个地球上所有的生物包括微生物甚至细菌病毒和小强BS你到宇宙毁灭)

基础

  • 学习 Bash 。你可以man bash来看看bash的东西,并不复杂也并不长。你用别的shell也行,但是bash是很强大的并且也是系统默认的。(学习zsh或tsch只会让你在很多情况下受到限制)

  • 学习 vim 。在Linux下,基本没有什么可与之竞争的编辑器(就算你是一个Emacs或Eclipse的重度用户)。你可以看看《简明vim攻略》和 《Vim的冒险游戏》以及《给程序员的Vim速查卡》还有《把Vim变成一个编程的IDE》等等。

  • 了解 ssh。明白不需要口令的用户认证(通过ssh-agent, ssh-add),学会用ssh翻墙,用scp而不是ftp传文件,等等。你知道吗?scp 远端的时候,你可以按tab键来查看远端的目录和文件(当然,需要无口令的用户认证),这都是bash的功劳。

  • 熟悉bash的作业管理,如: &, Ctrl-Z, Ctrl-C, jobs, fg, bg, kill, 等等。当然,你也要知道Ctrl+(SIGQUIT)和Ctrl+C (SIGINT)的区别。

  • 简单的文件管理 : ls 和 ls -l (你最好知道 “ls -l” 的每一列的意思), less, head, tail 和 tail -f, ln 和 ln -s (你知道明白hard link和soft link的不同和优缺点), chown, chmod, du (如果你想看看磁盘的大小 du -sk *), df, mount。当然,原作者忘了find命令。

  • 基础的网络管理: ip 或 ifconfig, dig。当然,原作者还忘了如netstat, ping, traceroute, 等

  • 理解正则表达式,还有grep/egrep的各种选项。比如: -o, -A, 和 -B 这些选项是很值得了解的。

  • 学习使用 apt-get 和 yum 来查找和安装软件(前者的经典分发包是Ubuntu,后者的经典分发包是Redhat),我还建议你试着从源码编译安装软件。

日常

  • 在 bash 里,使用 Ctrl-R 而不是上下光标键来查找历史命令。

  • 在 bash里,使用 Ctrl-W 来删除最后一个单词,使用 Ctrl-U 来删除一行。请man bash后查找Readline Key Bindings一节来看看bash的默认热键,比如:Alt-. 把上一次命令的最后一个参数打出来,而Alt-* 则列出你可以输入的命令。

  • 回到上一次的工作目录: cd – (回到home是 cd ~)

  • 使用 xargs。这是一个很强大的命令。你可以使用-L来限定有多少个命令,也可以用-P来指定并行的进程数。如果你不知道你的命令会变成什么样,你可以使用xargs echo来看看会是什么样。当然, -I{} 也很好用。示例:

1
2
3
find . -name *.py | xargs grep some_function

cat hosts | xargs -I{} ssh root@{} hostname
  • pstree -p 可以帮你显示进程树。(读过我的那篇《一个fork的面试题》的人应该都不陌生)

  • 使用 pgrep 和 pkill 来找到或是kill 某个名字的进程。 (-f 选项很有用).

  • 了解可以发给进程的信号。例如:要挂起一个进程,使用 kill -STOP [pid]. 使用 man 7 signal 来查看各种信号,使用kill -l 来查看数字和信号的对应表

  • 使用 nohup 或 disown 如果你要让某个进程运行在后台。

  • 使用netstat -lntp来看看有侦听在网络某端口的进程。当然,也可以使用 lsof。

  • 在bash的脚本中,你可以使用 set -x 来debug输出。使用 set -e 来当有错误发生的时候abort执行。考虑使用 set -o pipefail 来限制错误。还可以使用trap来截获信号(如截获ctrl+c)。

  • 在bash 脚本中,subshells (写在圆括号里的) 是一个很方便的方式来组合一些命令。一个常用的例子是临时地到另一个目录中,例如:

1
2
3
# do something in current dir
(cd /some/other/dir; other-command)
# continue in original dir
  • 在 bash 中,注意那里有很多的变量展开。如:检查一个变量是否存在: ${name:?error message}。如果一个bash的脚本需要一个参数,也许就是这样一个表达式 input_file=${1:?usage: $0 input_file}。一个计算表达式: i=$(( (i + 1) % 5 ))。一个序列: {1..10}。 截断一个字符串: ${var%suffix} 和 ${var#prefix}。 示例: if var=foo.pdf, then echo ${var%.pdf}.txt prints “foo.txt”.

  • 通过 <(some command) 可以把某命令当成一个文件。示例:比较一个本地文件和远程文件 /etc/hosts: diff /etc/hosts <(ssh somehost cat /etc/hosts)

  • 了解什么叫 “here documents” ,就是诸如 cat <<EOF 这样的东西。

  • 在 bash中,使用重定向到标准输出和标准错误。如: some-command >logfile 2>&1。另外,要确认某命令没有把某个打开了的文件句柄重定向给标准输入,最佳实践是加上 “</dev/null”,把/dev/null重定向到标准输入。

  • 使用 man ascii 来查看 ASCII 表。

  • 在远端的 ssh 会话里,使用 screen 或 dtach 来保存你的会话。(参看《28个Unix/Linux的命令行神器》)

  • 要来debug Web,试试curl 和 curl -I 或是 wget 。我觉得debug Web的利器是firebug,curl和wget是用来抓网页的,呵呵。

  • 把 HTML 转成文本: lynx -dump -stdin

  • 如果你要处理XML,使用 xmlstarlet

  • 对于 Amazon S3, s3cmd 是一个很方便的命令(还有点不成熟)

  • 在 ssh中,知道怎么来使用ssh隧道。通过 -L or -D (还有-R) ,翻墙神器。

  • 你还可以对你的ssh 做点优化。比如,.ssh/config 包含着一些配置:避免链接被丢弃,链接新的host时不需要确认,转发认证,以前使用压缩(如果你要使用scp传文件):

1
2
3
4
5
6
TCPKeepAlive=yes
ServerAliveInterval=15
ServerAliveCountMax=6
StrictHostKeyChecking=no
Compression=yes
ForwardAgent=yes
  • 如果你有输了个命令行,但是你改变注意了,但你又不想删除它,因为你要在历史命令中找到它,但你也不想执行它。那么,你可以按下 Alt-# ,于是这个命令关就被加了一个#字符,于是就被注释掉了。

数据处理

  • 了解 sort 和 uniq 命令 (包括 uniq 的 -u 和 -d 选项).

  • 了解用 cut, paste, 和 join 命令来操作文本文件。很多人忘了在cut前使用join。

  • 如果你知道怎么用sort/uniq来做集合交集、并集、差集能很大地促进你的工作效率。假设有两个文本文件a和b已解被 uniq了,那么,用sort/uniq会是最快的方式,无论这两个文件有多大(sort不会被内存所限,你甚至可以使用-T选项,如果你的/tmp目录很小)

1
2
3
4
5
cat a b | sort | uniq > c   # c is a union b 并集

cat a b | sort | uniq -d > c   # c is a intersect b 交集

cat a b b | sort | uniq -u > c   # c is set difference a - b 差集
  • 了解和字符集相关的命令行工具,包括排序和性能。很多的Linux安装程序都会设置LANG 或是其它和字符集相关的环境变量。这些东西可能会让一些命令(如:sort)的执行性能慢N多倍(注:就算是你用UTF-8编码文本文件,你也可以很安全地使用ASCII来对其排序)。如果你想Disable那个i18n 并使用传统的基于byte的排序方法,那就设置export LC_ALL=C (实际上,你可以把其放在 .bashrc)。如果这设置这个变量,你的sort命令很有可能会是错的。

  • 了解 awk 和 sed,并用他们来做一些简单的数据修改操作。例如:求第三列的数字之和: awk ‘{ x += $3 } END { print x }’。这可能会比Python快3倍,并比Python的代码少三倍。

  • 使用 shuf 来打乱一个文件中的行或是选择文件中一个随机的行。

  • 了解sort命令的选项。了解key是什么(-t和-k)。具体说来,你可以使用-k1,1来对第一列排序,-k1来对全行排序。

  • Stable sort (sort -s) 会很有用。例如:如果你要想对两例排序,先是以第二列,然后再以第一列,那么你可以这样: sort -k1,1 | sort -s -k2,2

  • 我们知道,在bash命令行下,Tab键是用来做目录文件自动完成的事的。但是如果你想输入一个Tab字符(比如:你想在sort -t选项后输入字符),你可以先按Ctrl-V,然后再按Tab键,就可以输入字符了。当然,你也可以使用$’t’。

  • 如果你想查看二进制文件,你可以使用hd命令(在CentOS下是hexdump命令),如果你想编译二进制文件,你可以使用bvi命令(http://bvi.sourceforge.net/ 墙)

  • 另外,对于二进制文件,你可以使用strings(配合grep等)来查看二进制中的文本。

  • 对于文本文件转码,你可以试一下 iconv。或是试试更强的 uconv 命令(这个命令支持更高级的Unicode编码)

  • 如果你要分隔一个大文件,你可以使用split命令(split by size)和csplit命令(split by a pattern)。

系统调试

  • 如果你想知道磁盘、CPU、或网络状态,你可以使用 iostat, netstat, top (或更好的 htop), 还有 dstat 命令。你可以很快地知道你的系统发生了什么事。关于这方面的命令,还有iftop, iotop等(参看《28个Unix/Linux的命令行神器》)

  • 要了解内存的状态,你可以使用free和vmstat命令。具体来说,你需要注意 “cached” 的值,这个值是Linux内核占用的内存。还有free的值。

  • Java 系统监控有一个小的技巧是,你可以使用kill -3 发一个SIGQUIT的信号给JVM,可以把堆栈信息(包括垃圾回收的信息)dump到stderr/logs。

  • 使用 mtr 会比使用 traceroute 要更容易定位一个网络问题。

  • 如果你要找到哪个socket或进程在使用网络带宽,你可以使用 iftop 或 nethogs。

  • Apache的一个叫 ab 的工具是一个很有用的,用quick-and-dirty的方式来测试网站服务器的性能负载的工作。如果你需要更为复杂的测试,你可以试试 siege。

  • 如果你要抓网络包的话,试试 wireshark 或 tshark。

  • 了解 strace 和 ltrace。这两个命令可以让你查看进程的系统调用,这有助于你分析进程的hang在哪了,怎么crash和failed的。你还可以用其来做性能profile,使用 -c 选项,你可以使用-p选项来attach上任意一个进程。

  • 了解用ldd命令来检查相关的动态链接库。注意:ldd的安全问题

  • 使用gdb来调试一个正在运行的进程或分析core dump文件。参看我写的《GDB中应该知道的几个调试方法

  • 学会到 /proc 目录中查看信息。这是一个Linux内核运行时记录的整个操作系统的运行统计和信息,比如: /proc/cpuinfo, /proc/xxx/cwd, /proc/xxx/exe, /proc/xxx/fd/, /proc/xxx/smaps.

  • 如果你调试某个东西为什么出错时,sar命令会有用。它可以让你看看 CPU, 内存, 网络, 等的统计信息。

  • 使用 dmesg 来查看一些硬件或驱动程序的信息或问题。

作者最后加了一个免责声明:Disclaimer: Just because you can do something in bash, doesn’t necessarily mean you should. ;) (全文完)

来源:酷壳

算法面试可能是微软搞出来的面试方法,现在很多公司都在效仿,而且我们的程序员也乐于解算法题,我个人以为,这是应试教育的毒瘤!我在《再谈“我是怎么招程序员”》中比较保守地说过,“问难的算法题并没有错,错的很多面试官只是在肤浅甚至错误地理解着面试算法题的目的。”,今天,我想加强一下这个观点——我反对纯算法题面试!(注意,我说的是纯算法题)

我再次引用我以前的一个观点——

能解算法题并不意味着这个人就有能力就能在工作中解决问题,你可以想想,小学奥数题可能比这些题更难,但并不意味着那些奥数能手就能解决实际问题。

好了,让我们来看一个示例(这个示例是昨天在微博上的一个讨论),这个题是——“找出无序数组中第2大的数”,几乎所有的人都用了O(n)的算法,我相信对于我们这些应试教育出来的人来说,不用排序用O(n)算法是很正常的事,连我都不由自主地认为O(n)算法是这个题的标准答案。我们太习惯于标准答案了,这是我国教育最悲哀的地方。(广义的洗脑就是让你的意识依赖于某个标准答案,然后通过给你标准答案让你不会思考而控制你)

功能性需求分析

试想,如果我们在实际工作中得到这样一个题 我们会怎么做?我一定会分析这个需求,因为我害怕需求未来会改变,今天你叫我找一个第2大的数,明天你找我找一个第4大的数,后天叫我找一个第100大的数,我不搞死了。需求变化是很正常的事。分析完这个需求后,我会很自然地去写找第K大数的算法——难度一下子就增大了。

很多人会以为找第K大的需求是一种“过早扩展”的思路,不是这样的,我相信我们在实际编码中写过太多这样的程序了,你一定不会设计出这样的函数接口—— Find2ndMaxNum(int* array, int len),就好像你不会设计出 DestroyBaghdad(); 这样的接口,而是设计一个DestoryCity( City& ); 的接口,而把Baghdad当成参数传进去!所以,你应该是声明一个叫FindKthMaxNum(int* array, int len, int kth),把2当成参数传进去。这是最基本的编程方法,用数学的话来说,叫代数!最简单的需求分析方法就是把需求翻译成函数名,然后看看是这个接口不是很二?!

(注:不要纠结于FindMaxNum()或FindMinNum(),因为这两个函数名的业务意义很清楚了,不像Find2ndMaxNum()那么二)

非功能性需求分析

性能之类的东西从来都是非功能性需求,对于算法题,我们太喜欢研究算法题的空间和时间复杂度了。我们希望做到空间和时间双丰收,这是算法学术界的风格。所以,习惯于标准答案的我们已经失去思考的能力,只会机械地思考算法之内的性能,而忽略了算法之外的性能

如果题目是——“从无序数组中找到第K个最大的数”,那么,我们一定会去思考用O(n)的线性算法找出第K个数。事实上,也有线性算法——STL中可以用nth_element求得类似的第n大的数,其利用快速排序的思想,从数组S中随机找出一个元素X,把数组分为两部分Sa和Sb。Sa中的元素大于等于X,Sb中元素小于X。这时有两种情况:1)Sa中元素的个数小于k,则Sb中的第k-|Sa|个元素即为第k大数;2) Sa中元素的个数大于等于k,则返回Sa中的第k大数。时间复杂度近似为O(n)。

搞学术的nuts们到了这一步一定会欢呼胜利!但是他们哪里能想得到性能的需求分析也是来源自业务的!

我们一说性能,基本上是个人都会问,请求量有多大?如果我们的FindKthMaxNum()的请求量是m次,那么你的这个每次都要O(n)复杂度的算法得到的效果就是O(n*m),这一点,是书呆子式的学院派人永远想不到的。因为应试教育让我们不会从实际思考了。

工程式的解法

根据上面的需求分析,有软件工程经验的人的解法通常会这样:

1)把数组排序,从大到小。

2)于是你要第k大的数,就直接访问 array[k]。

排序只需要一次,O(n*log(n)),然后,接下来的m次对FindKthMaxNum()的调用全是O(1)的,整体复杂度反而成了线性的。

其实,上述的还不是工程式的最好的解法,因为,在业务中,那数组中的数据可能会是会变化的,所以,如果是用数组排序的话,有数据的改动会让我重新排序,这个太耗性能了,如果实际情况中会有很多的插入或删除操作,那么可以考虑使用B+树。

工程式的解法有以下特点:

1)很方便扩展,因为数据排好序了,你还可以方便地支持各种需求,如从第k1大到k2大的数据(那些学院派写出来的代码在拿到这个需求时又开始挠头苦想了)

2)规整的数据会简化整体的算法复杂度,从而整体性能会更好。(公欲善其事,必先利其器)

3)代码变得清晰,易懂,易维护!(学院派的和STL一样的近似O(n)复杂度的算法没人敢动)

争论

你可能会和我有以下争论,

  • 如果程序员做这个算法题用排序的方式,他一定不会像你想那么多。是的,你说得对。但是我想说,很多时候,我们直觉地思考,恰恰是正确的路。因为“排序”这个思路符合人类大脑处理问题的方式,而使用学院派的方式是反大脑直觉的。反大脑直觉的,通常意味着晦涩难懂,维护成本上升。

  • 就是一道面试题,我就是想测试一下你的算法技能,这也扯太多了。没问题,不过,我们要清楚我们是在招什么人?是一个只会写算法的人,还是一个会做软件的人?这个只有你自己最清楚。

  • 这个算法题太容易诱导到学院派的思路了。是的这道“找出第K大的数”,其实可以变换为更为业务一点的题目——“我要和别的商户竞价,我想排在所有竞争对手报价的第K名,请写一个程序,我输入K,和一个商品名,系统告诉我应该订多少价?(商家的所有商品的报价在一数组中)”——业务分析,整体性能,算法,数据结构,增加需求让应聘者重构,这一个问题就全考了。

  • 你是不是在说算法不重要,不用学?千万别这样理解我,搞得好像如果面试不面,我就可以不学。算法很重要,算法题能锻炼我们的思维,而且也有很多实际用处。我这篇文章不是让大家不要去学算法,这是完全错误的,我是让大家带着业务问题去使用算法。问你业务问题,一样会问到算法题上来。

小结

看过这上面的分析,我相信你明白我为什么反对纯算法面试题了。原因就是纯算法的面试题根本不能反应一个程序的综合素质

那么,在面试中,我们应该要考量程序员的那些综合素质呢?我以为有下面这些东西:

  1. 会不会做需求分析?怎么理解问题的?
  2. 解决问题的思路是什么?想法如何?
  3. 会不会对基础的算法和数据结构灵活运用?

另外,我们知道,对于软件开发来说,在工程上,难是的下面是这些挑战:

  • 软件的维护成本远远大于软件的开发成本。
  • 软件的质量变得越来越重要,所以,测试工作也变得越来越重要。
  • 软件的需求总是在变的,软件的需求总是一点一点往上加的。
  • 程序中大量的代码都是在处理一些错误的或是不正常的流程。

所以,对于编程能力上,我们应该主要考量程序员的如下能力:

  1. 设计是否满足对需求的理解,并可以应对可能出现的需求变化。
  2. 程序是否易读,易维护?
  3. 重构代码的能力如何?
  4. 会不会测试自己写好的程序?

所以,这段时间,我越来越倾向于问应聘者一些有业务意义的题,而且应增加或更改需求来看程序员的重构代码的能力,写完程序后,让应聘者设计测试案例。

比如:解析加减乘除表达式,字符串转数字,洗牌程序,口令生成器,通过ip地址找地点,英汉词典双向检索……

总之,我反对纯算法面试题!

(全文完)

来源:酷壳