GithubHelp home page GithubHelp logo

qiwihui / blog Goto Github PK

View Code? Open in Web Editor NEW
43.0 1.0 2.0 12.11 MB

技术和思考,基于issues

Home Page: https://qiwihui.com

JavaScript 21.89% HTML 17.22% Just 2.64% CSS 42.72% Handlebars 15.53%
blog issue-blog python article analyze rust rust-lang hexo

blog's Introduction

blog

blog's People

Contributors

qiwihui avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

Forkers

ultranijia sun816

blog's Issues

机器学习可视化编辑器的一些比较

平台:

  1. Azure Machine Learning Studio
  2. EasyML
  3. BigQuant的量化投资平台
  4. 第四范式先知平台

比较方向:

  1. 方向:通用,专业;
  2. 使用体验:界面,操作流程(构建,训练,调优,部署);
  3. 算法/数据的支持,底层硬件,分布式;
  4. 开发支持:语言,接口,扩展;
  5. 开源,商用性(一站式);

其他:

Weka, knime.org, 阿里云机器学习PAI 等等

数据分析傻瓜话?

One problem at Haidian Book City

Today, I just came by Haidian Book City as usual at Haidian dist in
Beijing. and I found the problem hung on the wall nearby. It is very
interesting and I want to share it.

0x00 Problem

Here is one picture of it.

27-problem_at_haidian_book_city

A translation of that:

Solve this problem, then it's your domain:
{3, 13, 1113, 3113,..., the 8th number}.angelcrunch.com
(the QR code leads to the below link)
www.angelcrunch.com/jiemi

Once you finish it, you will get the second as below:

Guess a television series by the following numbers, and you will get an interview.
3113112211322112 / 311311

0x01 Solution

Yes, as you may guess, it is one look-and-say sequence(sequence A006715 in
OEIS.

In the sewuence, each member is genrated from the previous menber by
"reading" off the digits in it, counting rhe number of digits in groups of
the same digit. For example:

  • 3 is reading off as "one 3" or 13.
  • 13 is reading off as "one 1 one 3" or 1113.
  • 1113 is reading off as "three 1s, then one 3" or 3113.
  • and so on.

If we start with any digit d from 0 to 9 then d will remain
indefinitely as the last digit of the sequence. For d different from 1, the
sequence starts as follows:

d, 1d, 111d, 311d, 13211d, 111312211d, 31131122211d, ...

As example in the following table.

d Sloane sequence
1 A005150 1, 11, 21, 1211, 111221, 312211, 13112221, 1113213211, ...
2 A006751 2, 12, 1112, 3112, 132112, 1113122112, 311311222112, ...
3 A006715 3, 13, 1113, 3113, 132113, 1113122113, 311311222113, ...

Here, d equals 3.

So the first answer is 13211321322113.

For the second one, you need to know more about the sequence.
John Conway studied this sequence and found that the 8th
member and every member after it in the sequence is made up of one or more
of 92 "basic" non-interacting subsequences. The 92 basic subsequence shows
in the following table(from here.

The fouth column in the table says what other
subsequences the given subsequence evolves into. He also show that the
number of the digits in each member of the sequence grows a constant from
one member to the next. If Ln is the number of the digits in the
nth member in the sequence, then Ln+1/Ln to
a limitation when n to infinity. It is 1.303577269... , which we call it
as Conway Constant.

No. Subsequence Length Evolves Into
1 1112 4 (63)
2 1112133 7 (64)(62)
3 111213322112 12 (65)
4 111213322113 12 (66)
5 1113 4 (68)
6 11131 5 (69)
7 111311222112 12 (84)(55)
8 111312 6 (70)
9 11131221 8 (71)
10 1113122112 10 (76)
11 1113122113 10 (77)
12 11131221131112 14 (82)
13 111312211312 12 (78)
14 11131221131211 14 (79)
15 111312211312113211 18 (80)
16 111312211312113221133211322112211213322112 42 (81)(29)(91)
17 111312211312113221133211322112211213322113 42 (81)(29)(90)
18 11131221131211322113322112 26 (81)(30)
19 11131221133112 14 (75)(29)(92)
20 1113122113322113111221131221 28 (75)(32)
21 11131221222112 14 (72)
22 111312212221121123222112 24 (73)
23 111312212221121123222113 24 (74)
24 11132 5 (83)
25 1113222 7 (86)
26 1113222112 10 (87)
27 1113222113 10 (88)
28 11133112 8 (89)(92)
29 12 2 (1)
30 123222112 9 (3)
31 123222113 9 (4)
32 12322211331222113112211 23 (2)(61)(29)(85)
33 13 2 (5)
34 131112 6 (28)
35 13112221133211322112211213322112 32 (24)(33)(61)(29)(91)
36 13112221133211322112211213322113 32 (24)(33)(61)(29)(90)
37 13122112 8 (7)
38 132 3 (8)
39 13211 5 (9)
40 132112 6 (10)
41 1321122112 10 (21)
42 132112211213322112 18 (22)
43 132112211213322113 18 (23)
44 132113 6 (11)
45 1321131112 10 (19)
46 13211312 8 (12)
47 1321132 7 (13)
48 13211321 8 (14)
49 132113212221 12 (15)
50 13211321222113222112 20 (18)
51 1321132122211322212221121123222112 34 (16)
52 1321132122211322212221121123222113 34 (17)
53 13211322211312113211 20 (20)
54 1321133112 10 (6)(61)(29)(92)
55 1322112 7 (26)
56 1322113 7 (27)
57 13221133112 11 (25)(29)(92)
58 1322113312211 13 (25)(29)(67)
59 132211331222113112211 21 (25)(29)(85)
60 13221133122211332 17 (25)(29)(68)(61)(29)(89)
61 22 2 (61)
62 3 1 (33)
63 3112 4 (40)
64 3112112 7 (41)
65 31121123222112 14 (42)
66 31121123222113 14 (43)
67 3112221 7 (38)(39)
68 3113 4 (44)
69 311311 6 (48)
70 31131112 8 (54)
71 3113112211 10 (49)
72 3113112211322112 16 (50)
73 3113112211322112211213322112 28 (51)
74 3113112211322112211213322113 28 (52)
75 311311222 9 (47)(38)
76 311311222112 12 (47)(55)
77 311311222113 12 (47)(56)
78 3113112221131112 16 (47)(57)
79 311311222113111221 18 (47)(58)
80 311311222113111221131221 24 (47)(59)
81 31131122211311122113222 23 (47)(60)
82 3113112221133112 16 (47)(33)(61)(29)(92)
83 311312 6 (45)
84 31132 5 (46)
85 311322113212221 15 (53)
86 311332 6 (38)(29)(89)
87 3113322112 10 (38)(30)
88 3113322113 10 (38)(31)
89 312 3 (34)
90 312211322212221121123222113 27 (36)
91 312211322212221121123222122 27 (35)
92 32112 5 (37)

Those 92 subsequence is so basic that is constructs every member in the look-and-say
sequence. Just like 92 elements. Here
gives the periodic table of atoms associated with the look-and-say sequence
as named by Conway(1987). As we can see, 3113112211322112 links to Br, and
311311 links to Ba.

Breaking Bad. That is the answer.

0x02 More

That is perfect from the begining to the end. Many thanks to the problem
maker, and the screenwriters, also every
excellent actors in Breaking Bad.

Jenkins配置使用Docker自动编译

Jenkins配置使用Docker自动编译

大纲:

  1. jenkins介绍,以及相应的好处;
  2. 配置jenkins:
    • jenkins docker本身配置;
    • 配置jenkins使用docker;
    • 配置jenkins多worker;
  3. 项目编译构建docker化;
  4. 配置项目:
    • 项目权限;
    • 触发:github触发,定时触发;
    • 编译部署过程,分情况;
    • 完成之后:发邮件,修改github状态
  5. 总结注意点

在VirtualBox安装Arch Liux初步指南

简单地写一下在VirtualBox上安装Arch Linux的过程,以此为在PC上安装做准备。在PC上安装的过程和下面描述的基本一致。

##0x00 准备

  1. 下载iso文件:在Arch官网上下载最新的镜像,这里
    我选用了163.com节点的资源,下载archlinux-2015.01.01-dual.iso;

  2. 检查文件的完整性:在MAC中使用md5或者sha1检验文件的完整行,并和下载站点提供的值进行比较。

    $ openssl sha1 archlinux-2014.12.01-dual.iso 
    SHA1(archlinux-2014.12.01-dual.iso)= 86085153f97f0097fd0a02496e67cf85138c1ba5
    
    $ md5 archlinux-2014.12.01-dual.iso 
    MD5 (archlinux-2014.12.01-dual.iso) = 667ed3c5e935666edfd54a2271e05b72

##0x01 创建虚拟机

  1. 给虚拟机一个名字

  2. 内存:最小为256MB

  3. 创建新的虚拟磁盘

  4. 文件类型

  5. 动态分配

  6. 文件位置和大小,最小10GB

  7. 如果要安装桌面环境的话就勾选Enable 3D Acceleration

  8. 第一次启动虚拟机时,选取之前下载的ISO文件

##0x01 开始安装

  1. 选择32位或者64位Arch

    一旦看到如下提示,就可以开始进行配置了:

  2. 更改键盘布局和设置语言:

    默认键盘布局为us,非us布局可以用如下命令修改:

    # loadkeys layout

    layout可以是uk, dvorak等。设置语言:

  3. 磁盘分区

    先看一下磁盘状态:

    # ls /dev

    开始啦,一般创建四个分区:/, /boot, /home, swap

    # gdisk /dev/sda
    

    当出现下面命令时, 开始分区,一下以/boot分区为例:

    Command (? for help):
    

    a. 创建新分区:'n'
    b. 分区号码:回车默认从0开始递增
    c. "first sector": 回车默认从上一个分区结束处开始,初始为0
    d. "last sector": '+250MB'
    e. "hex code": 回车默认(8300 为"Linux File System"),swap分区输入8200, 见这儿

    依次给swap分配'+1G'(和分配的RAM一样大),/boot分配'+8G',/home分配'+1G',再次看一下磁盘的状态,可以看到已经分配好了,
    键入'w'并回车即可保存修改。

  4. 格式化分区

    再次回到命令行:

    root@archiso ~ #
    

    格式化分区:

    # mkfs -t ext4 /dev/sda1
    # mkfs -t ext4 /dev/sda3
    # mkfs -t ext4 /dev/sda4
    
    # mkswap /dev/sda2
    
  5. 挂载新分区

    # swapon /dev/sda2
    
    # mount /dev/sda3 /mnt
    # cd /mnt
    # mkdir boot home
    # mount /dev/sda1 boot
    # mount /dev/sda4 home
    
  6. 安装Arch

    # cd /
    # pacstrap /mnt base base-devel
    
  7. 生成fstab文件

    # genfstab -p /mnt >> /mnt/etc/fstab
    

    可以看看fstab里面的内容:

    # more /mnt/etc/fstab
    
  8. 初始化安装Boot Loader

    # pacstrap /mnt syslinux
    
  9. 配置安装

    运行以下命令:

    # arch-chroot /mnt
    

    得到:

    sh-4.2#
    

    这个shell很基础,用Bash也许更好些:

    # bash
    

    得到:

    [root@archiso /]#
    

    设置语言:

    # nano /etc/locale.conf

    添加:

    LANG="en_US.UTF-8"
    
    # nano /etc/locale.gen

    将下面两行前面#去除:

    en_US.UTF-8 UTF-8
    de_DE.UTF-8 UTF-8
    

    完成语言设置:

    # locale-gen
    

    设置时间:

    # ln -s /usr/share/zoneinfo/<your_state>/<your_city> /etc/localtime
    

    比如我设置的是:

    # ln -s /usr/share/zoneinfo/Asia/Chongqing /etc/localtime
    

    改hostname:

    # nano /etc/hostname
    
  10. 完成Bootloader安装

    # cd /boot/syslinux/
    

    打开syslinux.cfg文件,找到"comboot modules"一段:

    # more syslinux.cfg
    

    将其中列举的文件copy到本地,同时还要加上'libutil.c32':

    # cp /usr/lib/syslinux/bios/menu.c32 .
    # cp /usr/lib/syslinux/bios/vesamenu.c32 .
    # cp /usr/lib/syslinux/bios/chain.c32 .
    # cp /usr/lib/syslinux/bios/hdt.c32 .
    # cp /usr/lib/syslinux/bios/reboot.c32 .
    # cp /usr/lib/syslinux/bios/poweroff.c32 .
    # cp /usr/lib/syslinux/bios/libutil.c32 .
    

    一旦完成上述设置,

    # extlinux --install /boot/syslinux
    # dd conv=notrunc bs=440 count=1 if=/usr/lib/syslinux/bios/gptmbr.bin of=/dev/sda
    # mkinitcpio -p linux
    
  11. 完成安装

    最后,更改root密码:

    # passwd
    

    输入两次exit退回到:

    [root@archiso /]#
    

    umount所有的分区:

    # umount /mnt/boot
    # umount /mnt/home
    # swapoff /dev/sda2
    # umount /mnt
    

    在重启之前最后一步,设置/boot分区的BIOS标识为'bootable':

    # sgdisk /dev/sda --attributes=1:set:2
    
  12. 重启Arch

    # reboot
    

    重启之后会再次进入CD启动,这时,去除安装CD,再次重启:

    Devices > CD/DVD Devices > Remove disk from virtual drive
    

    等待一小会:

    Congradulations!

  13. 后续工作

    链接网络:

    dhcpcd
    

    安装'sudo':

    # pacman -S sudo
    

    添加'sudoer':

    # nano /etc/sudoers
    
    ##
    ## User privilege specification
    ##
    root ALL=(ALL) ALL
    qiwihui ALL=(ALL) ALL
    

    保存,并log out:

    # exit
    

    以新的ID和密码重新登录。

    最后,每次登录的时候自动获取ip:

    # sudo systemctl enable [email protected]
    

这样最基本的Arch Linux就好了,Desktop Environment就不装了。

0x02 参考文档

树莓派启动时自动连接wifi

这篇文章的目的是为了配置树莓派,使其在启动时自动获取静态IP.

启动并连接树莓派

1. 启动树莓派并找到其IP地址

把树莓派用网线连接到路由器上,插上SD卡,打开树莓派电源,等大约90秒.
在Mac上打开命令行终端,输入arp -a命令,可以看到树莓派的ip地址为 192.168.199.199.
当然也可以从路由器后台看到这个IP地址.

$ arp -a
? (169.254.99.51) at (incomplete) on en0 [ethernet]
hiwifi.lan (192.168.199.1) at d4:ee:7:20:18:6e on en0 ifscope [ethernet]
raspberrypi.lan (192.168.199.199) at f0:f6:1c:af:7a:28 on en0 ifscope [ethernet]

2. 使用SSH连接树莓派

输入"ssh [email protected]", 根据要求输入密码,默认为raspberry.

$ ssh [email protected]
[email protected] password: 
Linux qiwihuisrpi 3.18.7+ #755 PREEMPT Thu Feb 12 17:14:31 GMT 2015 armv6l

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Fri Apr 17 14:45:28 2015 from 192.168.199.186

配置网络连接

1. 设置网络接口文件/etc/network/interfaces

编辑这个文件:

$ sudo nano /etc/network/interfaces

添加如下内容:

auto lo
iface lo inet loopback

auto eth0
allow-hotplug eth0
iface eth0 inet dhcp

auto wlan0
allow-hotplug wlan0
iface wlan0 inet manual
# iface wlan0 inet dhcp # 如果想自动获取ip
wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf

# 设置静态ip
iface wlan0 inet static
address 192.168.199.199
netmask 255.255.255.0
gateway 192.168.199.1

iface default inet dhcp

2. 设置wpa_supplicant.conf配置文件

编辑文件wpa_supplicant.conf设置连接的网络热点.

$ sudo nano /etc/wpa_supplicant/wpa_supplicant.conf

为:

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1

network={
    ssid="YOUR_NETWORK_NAME"
    psk="YOU_NETWORK_PASSWORD"
    proto=RSN
    key_mgmt=WPA-PSK
    pairwise=CCMP
    auth_alg=OPEN
}

其中:

  • proto 可以是 RSN (WPA2) 或者 WPA (WPA1).
  • key_mgmt 可以是 WPA-PSK (大部分) 或者 WPA-EAP (企业网络)
  • pairwise 可以是 CCMP (WPA2) 或者 TKIP (WPA1)
  • auth_alg 常为 OPEN, 其他可选为 LEAPSHARED

重启树莓派,之后就会自动连上wifi了.

理解python索引和切片

许多初次接触Python的人对于索引都会有同样的反应:这太奇怪了。在Python的列表,字符串和条件语句中都充斥着索引,但在我们习惯他们之前,
这些都会是我们程序的错误来源。因此,让我们硬着头皮上吧!

这片文章会写得很慢,为了解释一些基础和默认的东西。

我们会使用字符串切片举例,因为这事我们首先接触的,不过这对于列表切片和设定范围是一样的。我们有:

a = '0123456789'

其中第k个位置的字符为k。

我们使用如下方式对a进行切片:

b = a[start:stop:step]

或者直接地:

'0123456789'[start:stop:step]

在Python中,字符串和指向字符串的变量都是对象,所以都可以进行切片(事实上,Python中所有东西都是对象:数字,变量,字符串,函数,文件)。

有三件事情需要记住:

  1. start 是我们想要的第一项(当然)
  2. stop 是我们第一个不想要的项
  3. step 可以是正整数,也可以是负数,定义了向前(从第一个位置到向末尾)还是向后(从最后一个位置向开始位置)索引。

一个小提醒:但我们学习python之外其他语言的时候,stop的定义是Python索引和切片在熟悉其语言的程序员看来如此奇怪的原因之一。在大部分计算机
语言中,stop应该是“我们需要的最后一项”。不管这个定义是否比其他语言更好或者更坏,Python的确是不寻常的一种语言。

在索引中使用负数是python另一个奇怪的特性。在大部分C衍生的语言(C/C++/C#,Java,,Javascript等)中,负数索引是不合法的,因为索引表示从
字符串初始内存地址的偏移,所以负数索引会指向字符串开始位置之前的位置(更详细的请参见这篇博客:
为什么python中索引从0开始)。然而,Python并不是唯一使用负数索引的语言,比如,
Perl像python一样使用负数索引来表示从字符串末尾开始的位置;
R语言面向统计,A[-i]表示所有除第i位置的值。不过,只有很少的语言
在任意情况下使用负数索引。

现在让我们回到Python索引上。

** 使用正数和负数索引 **

我们可以使用正整数表示字符串中的位置,由字符串开头从0开始计数:

b = "my mistress' eyes are nothing like the sun"
     ^         ^                              ^
    b[0]      b[10]                          b[41]

我们用len()函数来获取一个字符串的长度。因此,b有42个字符,故len(b)=42。因为b最后一个字符是b[41],所以len(b)比b字符串最后位置索引多1。

有些时候这对从字符串末尾开始找字符是很有帮助的。所以我们用负数,从字符串末尾开始计数,即从-1(不是0):

b = "my mistress' eyes are nothing like the sun"
     ^         ^                              ^
   b[-42]   b[-32]                          b[-1]

从末尾看的最后一个字符串是b[-42],那么它前面的位置会是-len(b)-1 = -43。

所以,在这个例子中len(b) = 42

b[0]        = b[-len(b)] = b[-42] = 'm'
b[len(b)-1] = b[-1]      = b[41]  = 'n'
b[10]       = b[-32]              = 's'

并且一般地,

b[k]        = b[-len(b)+k]

hmm, 这很令人疑惑。我们需要了解索引的一般机制,但是,我们不需要记住这些边界值,这些是python中默认的。

默认值是引用一个变量的时候,我们没有指定明确的值。这就和我们“默认地”称呼一位女性为女士(Ms.)一样。如果我们没有被告知需要称呼她为太太(Mrs.)或者小姐(Miss),或者如果我们忽略了实际的尊称,我们降退回到“默认”值。Python对于很常用的start, stop and step都有默认值。

** 如果step是正数,我们向前移动(如果step值为空,则默认为+1)**

a[2:6] = '0123456789'[2:6:1] = '2345'

如上例,我们想要的第一个值在第二个位置,第一个不想要的在第6个位置。

更进一步,我们从字符串末尾开始看:

a[-8:-4] = '0123456789'[-8:-4:1] = '2345'

我们想要的第一个值是从后数第8个(2),第一个不想要的是从后数第4个(6)。

所以,对于任意正数step,我们有如下的默认值:

     |-> -> ->|
a = '0123456789'
     ^         ^
  start:0   stop: len(a), i.e, 超出了字符串的末尾的位置

所以:

a[:]    = a[0:len(a):1] = '0123456789' # a +1 step 默认
a[::2]  = a[0:len(a):2] = '02468'      # 所有偶数位置
a[1::2] = '13579'                      # 所有奇数位置
a[::3]  = '0369'                       # 所有3的倍数位置

所以,只要我们从字符串头部或者尾部使用切片,Python都会使用默认值。

** 如果step是负数,而我们从后往前数**

a[6:2:-1] = '0123456789'[6:2:-1] = '6543'

我们需要的第一个值在第六个位置,不需要的第一个值在第二个位置。

或者进一步,

a[-4:-8:-1] = '0123456789'[-4:-8:-1] = '6543'

我们想要的第一个值是从后数第4个(6),第一个不想要的是从后数第8个(2)。

注意到我们可以在索引中使用正数或者负数,以及从前或者从后搜寻字符串,所以我们甚至可以将它们混合起来使用:

a[6:-8:-1] = '6543'
a[-4:2:-1] = '6543'

有时候这样的混合式非常方便的:

url = '<a href="http://udacity.com">'[9:-2]
    = 'http://udacity.com'

我们需要记住的是使用负数索引不意味着我们就是向后移动,只是我们从字符串末尾开始索引。向前还是向后是仅由step变量的符号决定的。

为了向后移动,我们需要在我们的意识中反转这个字符串:

     |<- <- <-|
a = '0123456789'
    ^         ^
    ^      start:-1
stop:在字符串开始位置之前的位置

所以:

a[::-1] = a[-1::-1] = '9876543210' # 我们只是学习如何反转字符串
a[::-2] = a[-1:-len(a)-1:-2] = '97531'
a[::-3] = a[-1:-len(a)-1:-3] = '9630'

再一次,只要我们从字符串的头部或者尾部对字符串切片,我们可以使用空的start和stop变量,Python会使用默认值。

只用6个字符就反转了一个字符串,厉害吧!可惜的是这个只在Python中有用,许多其他的语言并不支持这种方式。这类问题只是用来是我们熟悉这种结构,不只是在Python中,也包括其他语言在内。因此,考虑需要反转字符串的这类问题(比如回文问题)可以让我们学习如何使用循环,索引,并且尝试不同切片来解决这些问题。这样,你就有两手准备了。


所以现在,我们已经掌握了Python的索引,应该能明白底下这些了:

'0123456789'[8:2:-2]   = '864'
'0123456789'[8:-8:-2]  = '864'
'0123456789'[-2:2:-2]  = '864'
'0123456789'[-2:-8:-2] = '864'

Good luck!

原文在这儿

年度总结 - 2018年全年复盘

2019年一月已经过半,本该在年底十二月完成的总结又到现在才开始着笔。2018年发生了很多事,从年初比特币大涨至最高到现在互联网寒冬已至,不断变化的是环境,不变的是每年一次的年终总结。(误)

技术

做为一个程序员,在保持自己技术水平同时,应当不断地学习,总结和思考新的技术,才能在这个行业不至于被淘汰。互联网寒冬来临之时,对于还不能掌握形式的我也只能先增强自身能力以期减少这个寒冬带来影响。

机器学习和深度学习

从2017年年底开始涉足机器学习深度学习,先是学习然后在工作项目中实践,至今一年有余。
机器学习和深度学习从吴恩达的《机器学习》《深度学习》课程开始,吴恩达的课程简洁易懂,逻辑清楚,虽然都是英文,但也没有太大关系。不过这两个课程注重算法胡实现,缺少项目,因此可以需要 fast.ai 的机器学习深度学习课程来巩固掌握。主要使用的框架是 Scikit-learnKerasTensorflow,这三个掌握不足。
之后在公司安全项目中使用了一些分析和算法,对于机器学习和深度学习的理解和应用也只能说是皮毛。

Python和Go

  • Python语法巩固;
  • Go语言熟悉了基本语法,能阅读代码,会简单编写;
  • 入门Rust。

iOS 开发

年初(2017年末)答应给朋友的电影推荐网站写一个 iOS App,结果朋友的站倒闭了也还没有开始。下定决心花了一个多月学习 《Beginning iOS 11 Programming With Swift 4.1》和练习,然后开始做一个基于机器学习的垃圾短信过滤App SMSFilters,功能模仿熊猫吃短信。刚开始就是一行代码写一天,一个Bug改一周,软件开发这种事情就是要不断实践才能对所学的知识掌握。

SMSFilters 使用 Jieba 分词处理短信,然后用词袋模型提取特征,最后用 LinearSVC 训练,写 SMSFilters 遇到的第一个难题就是集成调用 CppJieba,没有经验,只能查文档,查StackOverflow,经过两周尝试,终于解决,过程可以参见 Demo 项目 SwiftJiebaDemo 和博文在iOS-Swift项目中集成CppJieba分词。目前项目进展至使用模型进行垃圾短信判断。

开源和社区

9月底请假去上海参加了今年的 Google **开发者大会,Google 是一家伟大公司,也是一家令人向往的公司.。Google 对于技术的追求也是有目共睹的。虽然 Google 重返**进程一直很艰难,但不妨碍其技术传播。此次参加开发者大会让我体会最深的是 Google 对于技术的追求,以及用技术改变世界的理想。Google 分享的技术涉及 AI,Tensorflow,Android,Firebase,Flutter 以及 AR/VR 等,在用技术改变世界。

博客和资讯

日常的咨询获取基本靠自订阅的 RSS,以及自己搭建的 RSSHub 和使用 feed43.com 做的 feed,但是对于微信公众号,自从微广场停止之后,一直没有很好的获取工具。
去年的博客数量更新很少,而且质量也不高,都是一些 “How to” 文,以及只写了大纲的几篇文章。

任务管理

9月看到 limboys 用 Trello 管理和记录日常的 Board,很有条理,便开始使用 Trello 管理和记录包括看书,电影电视,以及项目,目前公开了两个 Board:Qiwihui's Life技术和思考。Trello 的体验轻便,方便梳理,是迄今用过的最舒服的项目管理工具。

其他

  1. Chrome扩展:Octo Previewer

Fork并修改了一个 Chrome 扩展:Octo Previewer,用来实时预览 Github 上的 PRs,Issues,Gists 的 Markdown 评论。

  1. 在macOS上基于docker构建透明代理

Trello 卡片 上记录这个过程。理论上是可行的,只是在有些工具上卡住了前进的路线。

  1. hiwifi-ss 项目

这个基于极路由的翻墙项目没有持续维护,一则自己对于 lua 和 前端不熟悉,开发起来困难,二则我自己的极路由在搬家之后就没有了,再者极路由似乎大势已去,所以这个项目基本也就三四个月更新一次。这个项目给我带来的最大感触就是维护开源项目真的不容易。前些天看到 kalasoo 的文章《开源即责任》也是有感触。

读书

2018年上半年忙与机器学习和项目,几乎没有读什么书,下半年(9月)开始入坑科幻小说。《三体》是一部好的科幻小说,但在国内《三体》造就的伪科幻迷群体以及随之而来的各种各种视为真理的概念着实令人难受,于是入坑科幻洗洗脑。科幻类基本都是名篇,看过之后,对于《三体》所带来的震撼减轻了不少,但是我更加佩服大刘了。

books

技术类

  • 《Node入门》:一本入门 node 的不错的简单教程
  • 《Beginning iOS 11 Programming With Swift 4.1》:出自 Appcoda 的 iOS 开发入门教程,対初学者十分友好,详细介绍了开发一个 App 所需要的各个细节。
  • 《python-data-structure-cn》:Python数据结构简单介绍和实践,主要边帮着校对边读了。
  • 《给iOS开发者的Sketch入门教程》:做图标。

科幻类

  • 《海伯利安》及《海伯利安的陨落》,丹·西蒙斯:太空歌剧,感叹于作者对七个朝圣者故事的精彩讲述,以及挖坑。
  • 《软件体的生命周期》,姜峯楠:一开始还以为是计算机的书,姜峯楠的中短篇非常有料,和之前看过的《你一生的故事》短篇集同样精彩。
  • 《银河帝国》基地三部曲,包括 《基地》《基地与帝国》《第二基地》,艾萨克·阿西莫夫:经典中经典。
  • 《银河帝国》机器人系列,包括 《我,机器人》、《钢穴》、《裸阳》、《曙光中的机器人》、《机器人与帝国》,艾萨克·阿西莫夫:对于机器人三大法则,作者一步步进行“推翻”补充的过程。
  • 《平面国——及正方形的多维世界历险记》,埃德温·A·艾勃特:你如何看待二维世界,以及四维世界如何看你。
  • 《童年的终结》,阿瑟·克拉克:“大规摸养猪场技术”,包括安抚,推荐。
  • 《与拉玛相会》,阿瑟·克拉克:小说对于 Rama 飞船的探索写的很详尽,以至于最后离开飞船后飞船的行动让我大气不敢出。

数学类

  • 《从一到无穷大:科学中的事实和臆测》:科普,讲的东西很多,第一章是数论,第二章是相对论,第三章是微观世界,第四章是宏观世界,对于我无太多感想,必竟很多都知道了,感觉又看了一遍高中书。
  • 《素数之恋—黎曼和数学中最大的未解之迷》:理论和计算结合。
  • 《Riemann 猜想漫谈》:通俗易懂。

关于素数的两本书,主要是因为菲尔兹奖与阿贝尔奖双料得主迈克尔·阿蒂亚爵士讲述他对黎曼猜想的证明,这次事件间引起了我详细了解黎曼猜想的兴趣,为此还专门购买了经典教材《复变函数论方法》,期待进一步了解。这两本书适合一起看,互相补充。不幸的是,迈克尔·阿蒂亚爵士,于 2019 年 1 月 11 日上午逝世,享年八十九岁。他最后的尝试,令人佩服!

「我一直在尝试理解事物运行的原因。我对不能理解背后原理的公式不感兴趣。我总是试图挖掘事物背后的原理,所以如果我有一个公式,我就会去理解它为什么是这样。理解是一个非常困难的概念。人们认为数学的开始是你写下一个定理并附带证明。这不是开始,这是结束。对我来说,数学的创造性在你动手在纸上写字之前,在你尝试写公式之前。你描绘不同的事物,在脑海中反复思考。你尝试的创造,就像音乐家试图创作音乐,或诗人写诗一样。这个过程没有可以遵循的规律,你必须找到自己的方法。但到了最后,就像作曲家必须写下乐谱一样,你必须把它写下来。但最重要的一步是理解。证明公式本身可能不能让你理解。你可能有一个很长的证明,但到最后却不知道它为何是这样。但为了理解,你必须找到类似于直觉的能力,你必须感受它。」

——迈克尔·阿蒂亚爵士

其他类

  • 《黑客(计算机革命的英雄)》:家酿计算机俱乐部以及上古黑客的事迹,看完这个可以看看 《黑镜:潘达斯奈基》,可以看到许多影子。
  • 《此间的少年》,江南:之前没看完补上的。

今年看的非技术书基本都是在上下班坐着公交看的,积少成多,也就多了。今年的体会就是读书如抽丝。多读书涨见识。

电影、电视剧

平常看电影电视有时候喜欢写着代码,可能也不太记得太多,讲讲喜欢的吧。

movies

  • 《碟中碟6:全面瓦解》:阿汤哥不老开挂。
  • 《dele ディーリー 人生删除事务所》:其实不喜欢,以删除人生的名义偷窥以至修改别人的人生。
  • 《知无涯者 The Man Who Knew Infinity》:印度小哥开启挂来连哈代都服。
  • 《超级科学伙伴》:和一群点了“超人”技能树的科学家一起拯救世界是一种怎样的体验。
  • 《我的三体》(第一季,第二季):《我的世界》+《三体》简直不要太开心,可能是唯一的视频画面质量以可见的速度在变化的动画。
  • 《神奇动物:格林德沃之罪》:挖坑之作,“英特纳雄耐尔,就一定要实现!”。
  • 《神秘博士》(第十一季):换了编剧之后十三姨没有自己的特点,期待圣诞之后的几集能好转。
  • 《邪不压正》:如果那个北京城是真的。
  • 《世界奇妙物语 2018年春季特别篇》:意外好看一些。
  • 《辛普森一家》(第三十季):小黄人一家的故事。
  • 《孤独的美食家》(第七季):“凭谁问,廉颇老矣,尚能饭否?”
  • 《深夜食堂》(第三季):看的不是菜,是人间百味。

游戏

年初买了 Nitendo Switch,不过没有太多时间花在玩游戏上,以至于《塞尔达传说:荒野之息》和《超级马里奥:奥德赛》都没有通关,只是偶尔用来玩玩 AoV,不过 NS 的体验确实非常不错,不论是个人还是联机,值得推荐。个人觉得游戏在于娱乐放松,若影响正常工作和情绪则视为不可,曾记得大学玩游戏还冲别人发过脾气也是太年轻。

《王者荣耀》 和 《Arena of Valor》

我在王者荣耀S13赛季又重新玩了一段时间,每天三五局,升个一星两星就停止,掉个一星两星也停止,就这样达到了个人历史最好成绩,不过这个过程给我带来的影响也不少。一个是我发现在白天的队友比较坑,而到了夜晚会好很多,估计是小学生都去睡觉了,所以我每到十一二点就开始玩,有时会因为连胜而玩到很晚,以致停止一段时间之后仍不能好好早睡,严重影响精神状态;二是一局结束又开一局,犹如赌博,赢则更想赢,输则不服气,往往计较于一城得失,实在是影响心气。这个游戏不能投入太多时间。

《塞尔达传说:荒野之息》 和 《超级马里奥:奥德赛》

zelda

很不错的游戏,只是都还没有玩通关,需要补上。

《旅行青蛙》

就和养儿子一样(虽然作者说的是丈夫),总是担心种种,吃没吃好,有没有被雨淋,有没有被欺负,路上还有钱吗。这就是为人父母的体验吧。

吃鸡

接触不多,不过和《王者荣耀》这种是一样的感受。看过一些游戏主播的视频,佩服一个LOL职业玩家转吃鸡的主播,在战场上沉稳,有判断。

Ingress

已经弃坑了,一则是因为满级了,并没有刷成就的习惯,同时也因为没有太多时间出门,二则官方一直没有太多的积极活动,每次就是换一个地方刷牌子,再则在手机升级到 iOS 11 之后,官方推出了 Ingress Prime,游戏体验万分糟糕,之前的版本却不升级,遂弃。Ingress玩了四五年,也疯狂过,但是还是离开吧。

生活

  1. 婚姻

今年一大事就是和老婆领证了,还没有办酒席,两人都商量着简办,请亲戚吃个饭就感觉已经很隆重了。参加了几次同学的婚礼,无非接亲,闹新郎,宣个誓言,在众亲友面前挥泪感恩,对于这样重复的婚礼,也没有太大的兴趣。梦想人生的婚礼应当刺激,可以在远山,或者,招待一群好友,准备一堆食物,准备一段给大家的表演,大家也可以上台表演,发表自己的感想。

  1. 旅行
  • 7月在云台山,看大山,打水战,舒畅。
  • 9月青龙峡,人生第一次蹦极,刺激。
  • 10月湖南长沙,看橘子洲头,品长沙臭豆腐,一般。
  1. 养猫

cat

11月,迎来家庭另一个成员,一只英国短毛蓝猫,取名“狗狗”。虽然有时会觉得这和取狗蛋差不多,也不知道会不会让其他猫看不起。第一次养猫,总是担心这担心那,生怕他生病了,像极了父母。久了我也发现他还是很粘人的,不知道年后给他找了女朋友之后会不会还是这么粘人。

  1. 关于亲戚和钱

十月,堂弟找我借钱周转,并承诺四天还,鉴于有承诺而且是亲戚,也就爽快借了。第一次到期没任何动静,还钱还是再接着一句话也没说,我问了才换来一句“明天,明天一定换”,接着第二次就是“最迟不会超过后天下午”,然后是到期还了一半,我没收,要全款,就继续拖着,一星期没动静,问了几句就变成在外地学习没开手机,“明天下午三点之前”,然后“再给一个小时”,“我手机没电了”。最后钱是还了,但是我对他的信任一点都没有了,直接拉黑!
这件事让伙我认识到一点:在钱面前,亲戚的嘴脸也是令人恶心的。我借钱是因为我信任,却不代表你可以践踏。想起一个村民中奖却被村里人借得倾家荡产,村里人却觉得他有钱为什么就不能借点,可怕。

展望2019

绝不是flag。

  1. 做一份关于Python的知识小集,不求多,但求记录下一些易错,有深度的小知识点。同时过一遍Python的参考文档,阅读一些框架源码。
  2. 维护现有开源翻墙项目。
  3. 每两周至少一篇博客,记录的内容不能太基础,需要有思考。
  4. 深入了解计算机基础,包括系统原理、数据结构和算法、网络基础和编译原理,这四个方面每一个都阅读一本书籍,理解并掌握常用内容。
  5. 在leetcode上刷50题。
  6. 机器学习:学完fast.ai的机器学习课程。
  7. 深度学习:学习fast.ai的深度学习课程,对应同步阅读花书。
  8. 减少游戏时间,不在十一点以后玩游戏。
  9. 学会五笔打字,速度至少能达到60字/分钟。
  10. 在App store上至少上架一个App。
  11. 了解投资相关内容。

写在最后

人生需要有目标,需要一个积极乐观的心态,和一群志同道合的同伴。

在 OS X 上使用 sed 命令的一些注意

在 OS X 上使用 sed 会和 GNU 上不太一致,在此记录。

  1. OS X sed 不可忽略备份扩展

不可忽略备份扩展

在 OS X 上进行文本替换时,必须要指定备份扩展,即使扩展可以为空。比如:

sed -i  's/foo/bar/g' target

上面这行代码,可以在 GNU 上运行,作用是将 foo 替换为 bar,并且直接修改目标文件(-i)。但是如果在 OS X 上,这行命令会报错:

$ sed -i 's/foo/bar/g' target 
sed: 1: "target": undefined label 'arget'

原因是在 OS X 上,sed 命令必须指定备份的扩展格式:

$ man sed

     -i extension
             Edit files in-place, saving backups with the specified extension.  If a zero-length extension is given, no backup will be saved.  It is not recommended to give a
             zero-length extension when in-place editing files, as you risk corruption or partial content in situations where disk space is exhausted, etc.

所以需要修改为

sed -i '' 's/foo/bar/g' target 

没有好的方法避免创建备份文件问题,以下的方法都做不到兼容:

  • sed -i -e ... - 在 OS X 上不起作用,会创建 -e 备份
  • sed -i'' -e ... - 在 OS X 10.6 不起作用,但在 10.9+ 可行
  • sed -i '' -e ... - 在 GNU 上不起作用

或者,在 OS X 使用 gnu-sed 代替 sed:

brew install gnu-sed
alias sed=gsed

又或者,使用其他命令:

perl -i -pe's/foo/bar/g' target

参考

使用 Git Hooks 实现项目自动部署

自动化部署解放双手,发展生产力,更重要的是可以减少部署过程中的错误操作。

之前使用git做为我博客的版本控制,使用Github Pages托管我的博客,所以部署方面都交给了github,
但是当我要部署另一个web应用时,显然要部署在自己的VPS上,把VPS做为git服务器的同时,每次push
代码到服务器上都要手动运行一次脚本更新服务,这样做简直劳神伤力。

幸运的是Git提供了Hook机制用来帮助我们实现自动部署。Hooks分为客户端和服务端,可以用来处理不同
的工作,这些hooks都被存储在 Git 目录下的hooks子目录中,
即大部分项目中的.git/hooks。 Git 默认会放置一些脚本样本在这个目录中,除了可以作为hooks使用,
这些样本本身是可以独立使用的,这些样本名都是以.sample结尾,必须重新命名。

这次主要用到服务端的hooks: post-receive。当用户在本地仓库执行git push命令时,服务器上运端
仓库就会对应执行git receive pack命令;在所有远程仓库的引用(ref)都更新后,这个钩子就会被调用。
与之对应的是pre-receive,这个会在更新之前被调用。

环境要求:

  1. 要求客户端和服务端都有git环境,而且服务端最好已经部署好了;
  2. 能连上服务器

0x01 实践

我们的实践过程会按照下边的过程实施:


  +------------------------+          +------------------------+
  |                        |          |                        |
  |  +-----------------+   |   push   |  +-------------------+ |
  |  |local repository |---+----------+->| remote repository | |
  |  +-----------------+   |          |  +-------------------+ |
  |                        |          |             |          |
  +------------------------+          |             |pull      |
                                      |             V          |
       local machine                  |  +-------------------+ |
                                      |  |     deployment    | |
                                      |  +-------------------+ |
                                      |                        |
                                      +------------------------+

                                               server

在server上初始化一个远程裸仓库:

$ cd ~
$ mkdir remoteRepo
$ cd remoteRepo
$ git init --bare webapp.git

在server上初始化一个本地仓库,做为web app的代码:

$ cd ~
$ mkdir deployment
$ cd deployment
$ git clone ~/remoteRepo/webapp.git webapp

为远程仓库添加hook:

$ cd ~/remoteRepo/webapp.git/hooks
$ vim post-receive
$ cat post-receive

post-receive中的命令:

#!/bin/sh
# Check the remote git repository whether it is bare
IS_BARE=$(git rev-parse --is-bare-repository)
if [ -z "$IS_BARE" ]; then
    echo >&2 "fatal: post-receive: IS_NOT_BARE"
    exit 1
fi

unset GIT_DIR
# current user is git
DeployPath=/home/git/deployment/webapp
if [ ! -d $DeployPath ] ; then
    echo >&2 "fatal: post-receive: DEPLOY_DIR_NOT_EXIST: \"$DeployPath\""
    exit 1
fi

cd $DeployPath
git add . -A && git stash
git pull origin master

post-receive添加可执行权限

chmod +x post-receive

为local machined的本地仓库添加远程仓库源:

cd <your-local-repository-folder>
$ git remote add deploy git@<server.ip>:/home/git/remoteRepo/webapp.git

# then you need to merge conflict between local changes and deploy/master before you push it.
# 'git merge remotes/deploy/master' or some other git commands.

$ git push deploy master

或者从头开始创建一个项目:

git init

这样,当我们在本地完成更新并push到server上时,这些代码就会被自动更新。

0x02 后来

改进1

可以在最初在server上创建裸仓库时使用local machine上的现有项目,即将local machine上
的项目仓库导出为裸仓库 — 即一个不包含当前工作目录的仓库:

$ git clone --bare my_project my_project.git

或者

$ cp -Rf my_project/.git my_project.git

然后将这个裸仓库移到server上

$ scp -r my_project.git git@<server.ip>:/home/git/remoteRepo

之后,其他人要进行更新时就可以clone这个项目了:

$ git clone git@<server.ip>:/home/git/remoteRepo/my_project.git

改进2

有一种情况是当本地更新了webapp,结果push到远程仓库后这个更新被reset了(虽然我觉得这个问题应该避免,
但是还是有可能发生),这是,简单地在hook中使用git push deploy master是无法完成这个过程的,因为
远端的代码版本低于deploy端的代码版本,再使用pull的时候就不能实现同步,这时就应该使用另一种方式
更新代码:

git fetch --all
git reset --hard origin/master

git reset把HEAD 指向了新下载的未合并的节点,也就是在local machine上reset之后的。

参考:git 放弃本地修改 强制更新

极路由+shadowsocks翻墙

翻墙折腾无止境.
使用路由器翻墙的一个好处在于,对于一些翻墙配置很繁琐的设备,只需要简单地连上路由wifi就可以实现翻墙,
来家里的朋友也可以不需要配置就可以一连翻墙.

一些背景

在旧版本的极路由已经有很不错的翻墙设置方式,感谢前人的大树:三流火的shadwosocks插件.在旧版本的极路由上
设置shadowsocks翻墙可已参考极路由Shadowsocks家庭无痛翻墙实践.

最近极路由更新了新版本,管理界面风格大变导致之前的插件不能使用,在网上找了一段时间也没有看到有人对之前的
插件进行更新,所以决定自己写一个(其实后来才知道stary.love也有可用的插件,比我修改的插件功能强大很多).
所谓的自己写也只是在前人的基础上修改为适应新的极路由后台管理假面. 这过程要感谢stary.love的帮助,为我提供了
早期插件的一些源代码,以及许多帮着测试使用的人.

一些方法

项目地址: qiwihui/hiwifi-ss, 现在插件的状态:

  1. 界面结构适应了新的hiwifi后台管理;

07-ss-settings

  1. 开启关闭翻墙功能和设置dns解析功能也都恢复;

07-ss-advance

  1. 新增加了最新的gfwlist列表(截止到2016年8月7日)的路由规则,解决了之前有部分网站无法访问的问题;

安装过程

(1). 开启极路由开发者模式

需要开发者模式才能安装. 网上有很多教程,不赘述.

(2). 登录路由器, 一键安装脚本.

极路由默认开启1022端口作为ssh端口,故使用ssh [email protected] -p 1022登录路由器,运行如下一键脚本:

cd /tmp && curl -k -o shadow.sh https://raw.githubusercontent.com/qiwihui/hiwifi-ss/master/shadow.sh && sh shadow.sh && rm shadow.sh

然后登录后台管理界面,在互联网菜单下的shadowsocks设置配置ss账号就可以了.

一些展望

未来要做的一些工作:

  1. 功能的改进: 包括但不限于ss版本的更新, 规则的更新, 流量混淆等;
  2. 可能支持更多种类的工具;
  3. 最重要的是: 开源. 包括底层的代码重写或者是找到之前的代码.

一些感想

  • "免费"是最贵的

怎么说呢, 我在最开始的时候, 寻找免费的vpn是获得翻墙的唯一方式, 这种方式的不好之处在于: vpn不稳定, 经常换,
而且花费在寻找上的精力和时间算下来不合算. 之后精力了地下铁路vpn的消失之后, 自己搭建翻墙才成为我的主要翻墙
方式. 一个月花费的费用不到10美元, 带来的时稳定的流量和方式. VPS+shadowsocks/v2ray就可以提供稳定持久的方式.

  • 风险

不怎么使用vpn(免费或者收费)以及一些其他的收费翻墙服务,一则担心不安全, 流量劫持或者流量分析都有可能,甚者蜜罐,
二则是重点观察对象, 服务失效的可能性还是存在的. 因此, 加密翻墙流量和混淆翻墙行为时十分重要的过程.

  • 技术人员获取资讯和信息的广度和及时性

因为GFW, 墙内封闭的环境使得获取技术知识的广度和及时性都受到了很严重的影响, 翻墙让搞技术的我们与世界更接近.

分享 @lepture的一个tweet:

「我的互联网,上谷歌维基搜知识,上Reddit看看头条,上YouTube学习和开眼界,上Twitter关注一些正在改变世界的人和事,
去Quora上看看好的问题和回答,去SlideShare上学习以及了解不同的想法和观点」

总结

翻墙在于不断折腾.

为什么python中索引从0开始

在Python中字符串索引从0开始而不是从1开始是合乎逻辑的选择,因为python是用C语言写的,做为C的主要数据结构,数组是从0开始索引的。这在C中
是很基础的,以至于如果改变索引从1开始将会需要大量的工作。

** 1) 那么下一个问题:为什么在C中索引从0开始?**

C语言中的主要数据结构是数组,数组时一些相同类型元素的集合。在C中,字符串时字符数组,如果你想存储字符串“HELLO”,C会在内存中寻找一块连
续的地方存储这些字符。比如,从内存地址7000开始存储,那么这个字符串在内存中的地址就是:

7000 'H'
7001 'E'
7002 'L'
7003 'L'
7004 'O'
7005 '/0'

你可能会问:最后一个'/0'是什么?这个不是字符串"HELLO"的一部分。这个称作空值终止字符串。我们知道字符串从7000开始,但是我们不知道在哪里
结束,因此C在字符串的末尾加了一个空值使得我们遍历字符串时知道它在哪里结束。让我们再回到原来的问题。

让我们认真看一下这些字符的地址,如果我们想要这个字符串的第一个字符,我们要做的就是得到这个字符串的初始内存地址。

'H' 地址在 7000 因为字符串从 7000 开始

如果我们要字符'E',只需要地址偏移加1:

'E' is at 7000+1

我们可以是用偏移来得到所以的字符:

'H' is at 7000 + 0
'E' is at 7000 + 1
'L' is at 7000 + 2
'L' is at 7000 + 3
'O' is at 7000 + 4

啊哈!看到了吗?我们很自然地会使得索引等于便宜,这样我们就可以找到数组中的所有元素。
如果我们赋值如下:

greeting = 'HELLO'

greeting[0] = 'H'
greeting[1] = 'E'
greeting[2] = 'L'
...

所以,这是我们问题的回答。字符从0开始索引因为这表示了相对于字符串开始位置的偏移。

** 2) 我还是认为数组的第一个元素应该从1开始,这样错了吗?**

不,一点也不。有很多语言都会设计成这样:字符串的第一个元素的位置必须为1。一个很常见的例子是Matlab,它的索引从1开始。在这个例子中,
Matlab是基于Fotran的,Fotran的数组索引从1开始,所以,改变时没有意义的。

看到趋势了吗?语言往往从他们的父辈中继承许多基本的特性。由C衍生出来的语言倾向于从0开始索引,比如C++,objective C,Java,Python, Perl,
Javascript和其它许多语言,看这。有Fortran衍生出来的语言则往往从1开始
所以,就像Matalb和SimScript一样。

当然,这些继承不是必须的。比如,相对于其他许多C衍生的语言,Python使用缩进来表示结构,而不是花括号。恕我直言,这很不寻常,但也不失为一个好选择,
因为为了清楚,结构里的语句也会缩进,从这点看,花括号或者其他分隔符都显得多余了。

** 3) 哪一个更好呢,从0开始还是从1开始?**

都不好。如果需要,使用另一个索引开始值也是相对简单的。然而,有一些算法自然是从0或者1开始的,没有其他的,所以对于这些情况下,在实现上略有不同。
比如,二叉查找树从1开始,所以,在Python中,我们可以使用一个从0开始的数组活着列表,然后忽略第一个元素。在这篇
博客中,其中描述了我们可以强制C中的数组
从1开始索引,以及C开发者社区是怎样收到一本趋势读者也这样做的书。

原文在这儿

Cocoapods 和 Carthage 使用笔记

Carthage 和 CoaoaPods 的区别

CoaoaPods 是一套整体解决方案,我们在 Podfile 中指定好我们需要的第三方库。然后 CocoaPods 就会进行下载,集成,然后修改或者创建我们项目的 workspace 文件,这一系列整体操作。

相比之下,Carthage 就要轻量很多,它也会一个叫做 Cartfile 描述文件,但 Carthage 不会对我们的项目结构进行任何修改,更不多创建 workspace。它只是根据我们描述文件中配置的第三方库,将他们下载到本地,然后使用 xcodebuild 构建成 framework 文件。然后由我们自己将这些库集成到项目中。Carthage 使用的是一种非侵入性的哲学。

另外 Carthage 除了非侵入性,它还是去中心化的,它的包管理不像 CocoaPods 那样,有一个中心服务器(cocoapods.org),来管理各个包的元信息,而是依赖于每个第三方库自己的源地址,比如 Github。

Cocoapods

安装

  1. (可选)使用 taobao ruby-china 源替换默认 gem 源: gem source blabla..

    $ gem sources -l
    *** CURRENT SOURCES ***
    
    https://rubygems.org/
    
    $ gem sources --remove https://rubygems.org/
    https://ruby.taobao.org/ removed from sources
    
    $ gem source -a https://gems.ruby-china.com/
    https://gems.ruby-china.com/ added to sources
    
    $ gem source -c
    *** Removed specs cache ***
    
    $ gem source -u
    source cache successfully updated
    
    $ gem sources -l
    *** CURRENT SOURCES ***
    
    https://gems.ruby-china.com/
  2. sudo gem install cocoapods

  3. (可选)切换 pod 源

    $ pod repo
    
    master
    - Type: git (master)
    - URL:  https://github.com/CocoaPods/Specs.git
    - Path: /Users/qiwihui/.cocoapods/repos/master
    
    $ pod repo remove master
    
    $ pod repo add master https://git.coding.net/CocoaPods/Specs.git
    
    $ pod repo update
    
    $ pod setup

    或者

    $ git clone https://git.coding.net/CocoaPods/Specs.git ~/.cocoapods/repos/master
    $ pod repo update

    切换回官方镜像

    $ pod repo remove master
    
    $ pod repo add master https://github.com/CocoaPods/Specs.git
    
    $ pod repo update
    Updating spec repo `master`
      $ /usr/local/bin/git -C /Users/qiwihui/.cocoapods/repos/master fetch origin --progress
      remote: Enumerating objects: 511, done.        
      remote: Counting objects: 100% (511/511), done.        
      remote: Compressing objects: 100% (134/134), done.        
      remote: Total 820 (delta 399), reused 449 (delta 367), pack-reused 309        
      Receiving objects: 100% (820/820), 99.24 KiB | 401.00 KiB/s, done.
      Resolving deltas: 100% (501/501), completed with 194 local objects.
      From https://github.com/CocoaPods/Specs
         5b04790953c..e3ba7ee3a29  master     -> origin/master
      $ /usr/local/bin/git -C /Users/qiwihui/.cocoapods/repos/master rev-parse --abbrev-ref HEAD
      master
      $ /usr/local/bin/git -C /Users/qiwihui/.cocoapods/repos/master reset --hard origin/master
      HEAD is now at e3ba7ee3a29 [Add] IOS_OC_BASIC 6.3
    
    CocoaPods 1.6.0.beta.2 is available.
    To update use: `sudo gem install cocoapods --pre`
    [!] This is a test version we'd love you to try.
    
    For more information, see https://blog.cocoapods.org and the CHANGELOG for this version at https://github.com/CocoaPods/CocoaPods/releases/tag/1.6.0.beta.2
  4. 如果Podfile文件中有

    source 'https://github.com/CocoaPods/Specs.git'
    

    也需要把它换成repo的源,否则依然是使用GitHub源

基础用法

  1. cd <project_folder>

  2. pod init

  3. 编辑 Podfile, example

    # 平台,必需
    platform :ios, '9.0'
    # 隐藏警告
    inhibit_all_warnings!
    
    target 'AlamofireDemo' do
        # Using Swift and want to use dynamic frameworks
        use_frameworks!
    
        # 项目 Pods
        pod 'Alamofire', '~> 4.5'
    
        target 'AlamofireDemoTests' do
            inherit! :search_paths
            # 测试 Pods
        end
    
    end
    

    版本支持:
    - >, >=, <, <=
    - ~>: up to next major | minor | patch
    - :path 本地绝对路径
    - :git git项目地址,还可使用 :branch, :tag, :commit

  4. pod install

  5. Always 打开项目下 *.xcworkspace 文件作为项目入口

pod install 和 pod update 区别

  • pod install [package_name]: 安装特定版本的 pods
  • pod update [package_name]: 升级 pods 到最新版本

Carthage

安装

brew install carthage

使用

  1. 编辑 Cartfile,比如 SwiftyJSON

    github "SwiftyJSON/SwiftyJSON"
    
  2. carthage update [--platform ios]

    $ carthage update
    *** Fetching SwiftyJSON
    *** Checking out SwiftyJSON at "4.2.0"
    *** xcodebuild output can be found in /var/folders/kl/g94q0k_571vdjtcwzzcv20s40000gn/T/carthage-xcodebuild.nN22hg.log
    *** Building scheme "SwiftyJSON iOS" in SwiftyJSON.xcworkspace
    *** Building scheme "SwiftyJSON watchOS" in SwiftyJSON.xcworkspace
    *** Building scheme "SwiftyJSON tvOS" in SwiftyJSON.xcworkspace
    *** Building scheme "SwiftyJSON macOS" in SwiftyJSON.xcworkspace
    
  3. Carthage 目录下:

    • Build(编译出来的.framework二进制代码库)
    • Checkouts(源码)
    $ tree -L 3 Carthage/
    Carthage/
    ├── Build
    │   ├── Mac
    │   │   ├── SwiftyJSON.framework
    │   │   └── SwiftyJSON.framework.dSYM
    │   ├── iOS
    │   │   ├── 22BD4B6C-0B26-35E1-AF5F-8FB6AEBFD2FD.bcsymbolmap
    │   │   ├── C862E8A1-24ED-398A-A8E9-A7384E34EDB1.bcsymbolmap
    │   │   ├── SwiftyJSON.framework
    │   │   └── SwiftyJSON.framework.dSYM
    │   ├── tvOS
    │   │   ├── 1ADB9C1F-36CA-3386-BF07-6EE29B5F8081.bcsymbolmap
    │   │   ├── SwiftyJSON.framework
    │   │   └── SwiftyJSON.framework.dSYM
    │   └── watchOS
    │       ├── A8A151AB-D15E-3A0B-8A17-BF1A39EC6AB4.bcsymbolmap
    │       ├── EA427A42-6D21-3FF4-919F-5E50BF8A5D7B.bcsymbolmap
    │       ├── SwiftyJSON.framework
    │       └── SwiftyJSON.framework.dSYM
    └── Checkouts
        └── SwiftyJSON
            ├── CHANGELOG.md
            ├── Example
            ├── LICENSE
            ├── Package.swift
            ├── README.md
            ├── Source
            ├── SwiftyJSON.podspec
            ├── SwiftyJSON.xcodeproj
            ├── SwiftyJSON.xcworkspace
            ├── Tests
            └── scripts
    
  4. 添加生成的文件: 项目 "General" -> "Linked Frameworks and Libraries" -> 将 Carthage/Build/iOS 中的 .framework 文件添加到项目中

  5. "Build Phases" -> "+" -> "New Run Script Phase"

    • /bin/sh
    • /usr/local/bin/carthage copy-frameworks
    • "Input Files": $(SRCROOT)/Carthage/Build/iOS/SwiftyJSON.framework
    • "Output Files": $(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/SwiftyJSON.framework

    添加这个 Run Script 的作用是为了让运行时能够找到这个动态库。
    还可以将 Carthage 所集成的第三方库生成的符号文件添加到项目中,这样我们在调试的时候,就可以步入第三方库内部的代码:Build Phrases -> New Copy Files Phrase,将 Carthage/Build/iOS 目录中的 SwiftyJSON.framework.dSYM 符号文件拖动进来

参考

生产级别 spark jobs 实践

大纲:

  1. spark app 结构

    • 方便引入第三方包
    • 打包任务为模块
    • spark streaming任务结构
    • 本地调试
    • 单元测试
  2. 运行参数及优化

  3. spark读取数据和输出

    • mysql
    • elasticsearch

基于机器学习的垃圾短信过滤应用

基于机器学习的垃圾短信过滤

  1. Scikit-Learn 短信过滤模型训练,

  2. iOS CoreML介绍

  3. 使用 coremltools 将 Scikit-Learn 的模型转为 CoreML 的 mlmodel

  4. iOS App功能实现,界面编写

    • 导入模型并使用
    • 界面UI
    • 自定义黑白名单:号码,关键词
    • 提交辅助样本
  5. 调试

  6. App发布

  7. 优化模型大小,App大小及性能

Making kernels for jupyter

一个内核是运行和解析用户代码的程序。IPython包含了一个运行和解析Python代码的内核,而且人们已经写了多种语言的内核。

当Jupyter开始一个内核的时候,它会传递它一个连接文件。它指定了如何与前端开始通信。

以下是实践:

安装环境

$ conda create -n py365400 python=3.6.5 jupyter ipykernel
$ conda activate py365

列出当前内核

在Unix系统中,可用的内核列在如下文件夹中(Kernel specs):

System:

  • /usr/share/jupyter/kernels
  • /usr/local/share/jupyter/kernels

Env:

  • {sys.prefix}/share/jupyter/kernels

User:

  • ~/.local/share/jupyter/kernels (Linux)
  • ~/Library/Jupyter/kernels (Mac)

用户位置的优先级高于系统级别的,忽略名字的大小写。因此不论系统是否大小写敏感,都可以以同样的烦噶事来获取内核。因为内核名字会在URL出现,因此内核名字需要是一个简单的,只使用ASCII字母,数字和简单的分隔符-._
如果设置了 JUPYTER_PATH 环境变量的话,也会搜索其他位置。

例如在我的Mac上,有两个个内核,一个是 python 3 的,另一个是 pyspark(python 2) 的。

$  jupyter kernelspec list
Available kernels:
  pyspark2    /Users/qiwihui/Library/Jupyter/kernels/pyspark2
  python3     /usr/local/miniconda3/envs/py365/share/jupyter/kernels/python3

在内核文件夹下,现在会使用三种类型的文件。kernel.json, kernel.js和log图片文件。目前,没有使用其他文件,但是将来可能会改变。

最重要的文件是 kernel.json,应该是一个json序列化的字典包含以下字段

  • argv: 用来启动内核的命令行参数列表。{connection_file} 将会被实际的连接文件的路径替换。
  • display_name: 在UI上展示的内核名字。不像在API中使用的内核名字,这里的名字可以包含任意字符。
  • language: 内核的语言名字。当载入notebook的时候,如果没有找到匹配的内核,那么匹配相应语言的内核将会被启动。这样允许一个写了任何Python或者julia内核的notebook可以与用户的Python或者julia内核合适的联系起来,即使它们没有在与用户内核同样的名字下。
  • interrupt_mode:可能是signal或者message指定了客户端如何在这个内核中停止单元运行。是通过发送一个信号呢,还是发送一个interrupt_request消息在control channel。如果没有指定,将默认使用signal模式。
  • env:为内核设置的环境变量。在内核启动前,会添加到当前的环境变量里。
  • metadata:关于这个内核的其他相关属性。帮助客户端选择内核。

比如:

$ cat /usr/local/miniconda3/envs/py365/share/jupyter/kernels/python3/kernel.json 
{
 "argv": [
  "/usr/local/miniconda3/envs/py365/bin/python",
  "-m",
  "ipykernel_launcher",
  "-f",
  "{connection_file}"
 ],
 "display_name": "Python 3",
 "language": "python"
}

当内核开始的时候将会传入一个连接文件的路径,这个文件只对当前用户可用,会包含类似下面的一个JSON字典。

{
  "control_port": 50160,
  "shell_port": 57503,
  "transport": "tcp",
  "signature_scheme": "hmac-sha256",
  "stdin_port": 52597,
  "hb_port": 42540,
  "ip": "127.0.0.1",
  "iopub_port": 40885,
  "key": "a0436f6c-1916-498b-8eb9-e81ab9368e84"
}

transport, ip 和设定了该使用 ZeroMQ 绑定的五个_port。比如 shell 套接字的地址应该是:tcp://127.0.0.1:57503。在每个内核开始的时候会指定随意的端口。signature_schemekey 用来加密信息,因此系统的其他用户不能发送代码来运行内核。

现在我需要自己定义一个内核,这个内核可以执行我们定义的逻辑。

添加新内核

这是简单的重用 IPython 的内核机制来实现这个新的内核。

步骤:

子类化ipykernel.kernelbase.Kernel,然后实现下面的方法和属性

class MyKernel

- implementation
- implementation_version
- banner
    Kernel info会返回的信息。Implementation指的是内核而不是语言,比如IPython而不是Python。banner是在控制UI上显示第一个提示符之前的东西。这些都是字符串

- language_info
    Kernel info会返回的信息字典。应该包含mimetype键,值是目标语言的mimetype,比如text/x-python。name键是实现的语言比如python,file_extension比如.py,而且也可能根据不同语言包含codemirror_mode和pygments_lexer

- do_execute(code, silent, store_history=True, user_expressions=None, allow_stdin=False)

    执行用户代码

        - code:要执行的代码
        - silent:是否展示输出
        - store_history: 是否在历史里记录代码,并且增加执行次数。
        - user_expressions:在代码被执行后对这些表达式求值
        - allow_stdin:前端是否提供输入请求

    你的方法应该返回一个字典,包含在Execution results规定的字典。为了展现输出,它可以使用send_response() 来发送消息。

为了启动你的内核,在模块后面加上:

if __name__ == '__main__':
    from ipykernel.kernelapp import IPKernelApp
    IPKernelApp.launch_instance(kernel_class=MyKernel)

现在创建一个JSON的内核说明文件,然后通过 jupyter kernelspec install </path/to/kernel>。将你的内核模块放在Python可以导入的地方,一般是当前目录(做测试)。最后,你可以使用 jupyter console --kernel <mykernelname> 来运行你的内核。

例子:

$ ls echo/
echokernel.py kernel.json

echokernel.py:

from ipykernel.kernelbase import Kernel

class EchoKernel(Kernel):
    implementation = 'Echo'
    implementation_version = '1.0'
    language = 'no-op'
    language_version = '0.1'
    language_info = {
        'name': 'Any text',
        'mimetype': 'text/plain',
        'file_extension': '.txt',
    }
    banner = "Echo kernel - as useful as a parrot"

    def do_execute(self, code, silent, store_history=True, user_expressions=None,
                   allow_stdin=False):
        if not silent:
            stream_content = {'name': 'stdout', 'text': code}
            self.send_response(self.iopub_socket, 'stream', stream_content)

        return {'status': 'ok',
                # The base class increments the execution count
                'execution_count': self.execution_count,
                'payload': [],
                'user_expressions': {},
               }

if __name__ == '__main__':
    from ipykernel.kernelapp import IPKernelApp
    IPKernelApp.launch_instance(kernel_class=EchoKernel)

kernel.json:

{
    "argv":["python","-m","echokernel", "-f", "{connection_file}"],
    "display_name":"Echo"
}

安装

$ jupyter kernelspec install echo --user

这里,只为当前用户添加这个kernel。

查看

$ jupyter notebook

选择新创建的内核创建 notebook,并运行代码。

jupyter with new kernel echo

image

一些坑

  1. 运行 notebook 时无法找到 echokernel 模块:
[I 15:48:27.754 NotebookApp] Kernel started: 77759cfa-db55-4b70-be23-c14d69f8d87d
/usr/local/miniconda3/envs/py365/bin/python: No module named echokernel
[I 15:48:30.750 NotebookApp] KernelRestarter: restarting kernel (1/5), new random ports
/usr/local/miniconda3/envs/py365/bin/python: No module named echokernel
[I 15:48:33.766 NotebookApp] KernelRestarter: restarting kernel (2/5), new random ports
/usr/local/miniconda3/envs/py365/bin/python: No module named echokernel
[I 15:48:36.789 NotebookApp] KernelRestarter: restarting kernel (3/5), new random ports
/usr/local/miniconda3/envs/py365/bin/python: No module named echokernel
[I 15:48:39.812 NotebookApp] KernelRestarter: restarting kernel (4/5), new random ports
/usr/local/miniconda3/envs/py365/bin/python: No module named echokernel

需要将 echokernel.py 放置在 python PATH 中 ,这样在执行命令时才能访问到。

更多命令

$ jupyter kernelspec help
Manage Jupyter kernel specifications.

Subcommands
-----------

Subcommands are launched as `jupyter kernelspec cmd [args]`. For information on
using subcommand 'cmd', do: `jupyter kernelspec cmd -h`.

list
    List installed kernel specifications.
install
    Install a kernel specification directory.
uninstall
    Alias for remove
remove
    Remove one or more Jupyter kernelspecs by name.
install-self
    [DEPRECATED] Install the IPython kernel spec directory for this Python.

To see all available configurables, use `--help-all`

删除内核

$ jupyter kernelspec uninstall echo

参考

使用Nginx,supervisor在DigitalOcean中部署tornado项目

一直在想把微信的公众号的文章导出为RSS阅读,方便阅读和减少对微信的依赖,后来看到
zhu327/rss 这个项目,这是一个用来生成微博,微信公众号,知乎日报 RSS 的Web APP。
但是这个项目的demo部署在Red Hat的openshift上,
囿于对这个cloud的操作不是很熟,所以想着把这个项目重新部署到自己在DigitalOcean的机器上,就fork了这个项目开始啦!

以下涉及到的内容有:

  • Linux创建用户和修改用户组
  • git hooks实现自动部署
  • tornado项目的基本框架结构
  • supervisor管理进程
  • Nginx配置HTTP服务代理
  • DNS的记录添加

基本服务器设置

因为之前并没有在我的服务器上创建过其他用户,如果直接用root用户的话不好,所以需要专门的一个账户来负责部署。

  1. 登陆服务器:ssh root@<server-ip>

  2. 创建一个用户deploy: sudo adduser deploy

  3. 将用户加入sudoers中: sudo usermod -a -G sudo deploy

  4. 添加远程连接的权限,这样就省去了输入密码了:

    sudo su - deploy
    mkdir .ssh
    chmod 700 .ssh
    touch .ssh/authorized_keys
    chmod 600 .ssh/authorized_keys
    

    其中,700表示只有文件拥有者才能读,写以及打开文件,600表示只能读和写。

  5. 接着将自己的公钥加入authorized_keys文件中,这个公钥在自己本机~.ssh/id_rsa.pub中。没有的话可以用
    ssh-keygen -t rsa -C "[email protected]"来生成。

创建使用git hooks的自动部署

自动部署的好处就是省去了每次都要上服务器。可以参见之前的一篇博客
使用 Git Hooks 实现项目自动部署 来创建这个远程的git server。

这里,我们要先fork zhu327/rss 这个项目,然后用git clone --bare rss rss.git生成原来
项目的裸仓库,然后将其复制到服务器上。我使用的是~/remoteRepo/rss.git做为git server,~/deployment/rss做为真正
生产的代码文件目录。

其中,git hooks中的post-receive文件的内容为

#!/bin/sh
# Check the remote git repository whether it is bare
IS_BARE=$(git rev-parse --is-bare-repository)
if [ -z "$IS_BARE" ]; then
echo >&2 "fatal: post-receive: IS_NOT_BARE"
exit 1
fi

unset GIT_DIR
# current user is git
DeployPath=/home/deploy/deployment/rss
if [ ! -d $DeployPath ] ; then
echo >&2 "fatal: post-receive: DEPLOY_DIR_NOT_EXIST: \"$DeployPath\""
exit 1
fi

cd $DeployPath
git add . -A && git stash
git pull origin master

修改源代码

zhu327/rss 项目的部署在openshift,为了将其部署在自己服务器上,修改
是必须的。

  • 删除了项目中的openshift hooks部分

  • 将其中用到openshift环境变量OPENSHIFT_DIY_IPOPENSHIFT_DIY_PORT修改为对应的localhost8000端口

  • diy/templates/中的https://diy-devz.rhcloud.com修改为之后要用到的地址 http://rss.daozhang.info

  • 然后将修改好的代码在本地的virtualenv环境中测试,并生成需要的python的模块文件requirement.txt。如下:

    Jinja2==2.7.3
    MarkupSafe==0.23
    backports.ssl-match-hostname==3.4.0.2
    certifi==2015.04.28
    lxml==3.4.4
    python-dateutil==2.4.2
    python-memcached==1.54
    six==1.9.0
    tornado==4.2
    wsgiref==0.1.2
    

这些都好了之后就可以将本地的文件第一次push到服务器上了。因为之前已经设置好了git hook,所以可以在服务器上的
deployment/rss看到项目的代码更新了。

使用supervisor管理进程

supervisor是Linux中非常好用的进程管理工具,我们将使用它和Nginx一起来组成我们的服务的部署。

  1. 安装supervisor:pip install supervisor 或者 sudo apt-get install supervisor

  2. 创建一个目录来装supervisor的配置文件:mkdir -p ~/local/etc/supervisord

  3. 创建superviosr的出要的配置文件:touch ~/local/etc/supervisord.conf,并加入如下内容:

     [unix_http_server]
     file=/home/deploy/tmp/supervisor.sock
      
     [supervisord]
     user=deploy
     logfile=/home/deploy/logs/user/supervisord.log
     logfile_maxbytes=50MB
     logfile_backups=10 
     loglevel=info
     pidfile=/home/deploy/local/run/supervisord.pid supervisord.pid)
      
     [rpcinterface:supervisor]
     supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
      
     [supervisorctl]
     serverurl=unix:///home/deploy/tmp/supervisor.sock
      
     [include]
     files = /home/deploy/local/etc/supervisord/*.ini
    

    其中我们都适用用户目录下创建的locallogstmp文件夹来装这些文件。

  4. 创建一个rss.ini的文件用来作为rss服务:touch ~/local/etc/supervisord/rss.ini,放入如下内容:

    [program:rss]
    command=python2.7 /home/deploy/deployment/rss/diy/start.py
    

    其中,start.py是这个tornado项目的入口。

  5. 启动服务:supervisord -c /home/deploy/local/etc/supervisord.conf,因为用的是非默认的配置文件,这里
    指定相应的配置文件位置。

  6. 一旦我们在之后修改了项目push了之后,我们需要重新启动rss:supervisorctl restart rss,因此,为了方便,
    可以将这条命令加入项目git hooks中的post-receive文件末尾。

配置Nginx

Nginx很好很强大,我们用它来做为我们的HTTP服务器。

  1. 安装Nginx,这里,我们适用从源代码安装Nginx,并配置一些log,pid等的目录到deploy的用户目录下,这里,写
    一个安装的脚本install.sh

    mkdir -p ~/src
    mkdir -p ~/tmp/nginx/fcgi ~/tmp/nginx/proxy ~/tmp/nginx/client
    
    cd ~/src
    curl -O  http://nginx.org/download/nginx-1.2.1.tar.gz
    tar -xzvf nginx-1.2.1.tar.gz
    cd nginx-1.2.1
    
    ./configure   --prefix=$HOME/local/nginx  \
    --sbin-path=$HOME/local/sbin/nginx \
    --conf-path=$HOME/local/etc/nginx.conf  \
    --error-log-path=$HOME/logs/user/nginx/error.log \
    --http-log-path=$HOME/logs/user/nginx/access.log \
    --pid-path=$HOME/local/run/nginx/nginx.pid \
    --lock-path=$HOME/local/lock/nginx.lock \
    --http-client-body-temp-path=$HOME/tmp/nginx/client/ \
    --http-proxy-temp-path=$HOME/tmp/nginx/proxy/  \
    --http-fastcgi-temp-path=$HOME/tmp/nginx/fcgi/ \
    --with-http_flv_module \
    --with-http_ssl_module \
    --with-http_gzip_static_module
    
    make && make install
    

    在Nginx的安装过程中会列出这些配置信息:

    Configuration summary
      + using system PCRE library
      + using system OpenSSL library
      + md5: using OpenSSL library
      + sha1: using OpenSSL library
      + using system zlib library
    
      nginx path prefix: "/home/deploy/local/nginx"
      nginx binary file: "/home/deploy/local/sbin/nginx"
      nginx configuration prefix: "/home/deploy/local/etc"
      nginx configuration file: "/home/deploy/local/etc/nginx.conf"
      nginx pid file: "/home/deploy/local/run/nginx/nginx.pid"
      nginx error log file: "/home/deploy/logs/user/nginx/error.log"
      nginx http access log file: "/home/deploy/logs/user/nginx/access.log"
      nginx http client request body temporary files: "/home/deploy/tmp/nginx/client/"
      nginx http proxy temporary files: "/home/deploy/tmp/nginx/proxy/"
      nginx http fastcgi temporary files: "/home/deploy/tmp/nginx/fcgi/"
      nginx http uwsgi temporary files: "uwsgi_temp"
      nginx http scgi temporary files: "scgi_temp"
    
  2. 添加路径到PATH中:

    export PATH=/home/you/local/sbin:$PATH
    source ~/.bashrc
    
  3. 创建配置文件:~/local/etc/nginx.conf,在其中添加我们服务的配置:

    #user  deploy;
    worker_processes  1;
    
    error_log /home/deploy/logs/user/nginx/error.log;
    pid /home/deploy/local/run/nginx/nginx.pid;
    
    events {
        worker_connections  1024;
    }
    
    http {
        upstream rsstornado {
            server 127.0.0.1:8000;
        }
        include       mime.types;
        default_type  application/octet-stream;
    
        access_log /home/deploy/logs/user/nginx/access.log;
    
        keepalive_timeout 65;
        proxy_read_timeout 200;
        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        gzip on;
        gzip_min_length 1000;
        gzip_proxied any;
        # Relevant docs: http://wiki.nginx.org/HttpGzipModule#gzip_types
        # Enables compression for additional MIME-types besides "text/html".
        # "text/html" is always compressed.
        gzip_types text/plain text/css text/xml
                   application/x-javascript application/xml
                   application/atom+xml text/javascript;
    
        # Only retry if there was a communication error, not a timeout
        # on the Tornado server (to avoid propagating "queries of death"
        # to all frontends)
        proxy_next_upstream error;
    
        server {
            listen       80;
            # server_name  localhost;
            # Allow file uploads
            client_max_body_size 50M;
    
            location / {
                proxy_pass_header Server;
                proxy_set_header Host $http_host;
                proxy_redirect off;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Scheme $scheme;
                proxy_pass http://rsstornado;
            }
    
        }
    }
    

    其中,upstream rsstornado指向了我们的rss的端口。

  4. 之后使用/home/deploy/local/sbin/nginx -t来检查这些配置,期望的输出为:

    nginx: the configuration file /home/deploy/local/etc/nginx.conf syntax is ok
    nginx: configuration file /home/deploy/local/etc/nginx.conf test is successful
    
  5. 运行服务:/home/deploy/local/sbin/nginx

如果一切顺利,这时,我们在浏览器中输入服务器对应的ip时就可以看到这个web app了。

添加A纪录

最后的话需要在自己的dns服务商中添加一条指向服务器ip的A距离,例如在 he.net 中添加
一条A记录即可。很快,就可以直接使用 http://rss.daozhang.info 访问这个app了。

最后

这样,我们就完成了这个server的配置。在我部署这个server的过程中,微信对应的RSS生成的解析实效了,
我觉得是因为sogou在其url中添加了一个序列,这个序列是有AES算法得出来的,并且一段时间会换一个key来
生成这个序列,所以我暂时也不知道怎么处理这个,有待进一步研究。

Seek, think then speak

“Seek, think then speak”,这是我在我的Twitter
上的签名,也是我在日常生活和工作中一个做事的基本准则。当自己得到一个消息,或者开始一个
新的任务的时候,不是下意识地就相信这个消息,开始这个任务,而是要经过这三步过程之后,
得出自己的结论,才开始行动。

Seek

探索,或者说是寻求,就是当你得到这个消息的时候,不是盲目地相信,而是先开始收集和甄别资料。
在这个信息化一直信息爆炸的时代,越来越多的误导和虚假消息充斥在我们的身边,新闻电视,媒体资讯,
社交网络,信息方便的同时也可能是误导和虚假消息滋生的温床。信息本无对错,只是当它们被少数人
利用,曲解的时候,信息的对错才开始变得有利可图。

Think

思考,结合自己的知识背景和收集到的信息,思考信息的对错,以及其中个所包含的其他有价值的信息,
这是一步非常重要的过程。善恶只在一念之间,这一念就是你的思考,你的想法。思考,可以是道理更深刻,
事物更透彻,思考的好处不言而喻,古今之集大成者,莫不是善于思考的人。

Speak

发言,又或者可以是行动(Move),就是在思考之后表达自己的见解,采取一定的行动支持这个见解,
以达到说服自己,说服别人,是别人达成对你的共识,就如同演讲家和行动派表现出来的一样。

我更喜欢诸如Twitter一类的自由开放社交工具,一个重要的原因是消息的对冲。QQ,微信之类的熟人社交,
消息相对封闭,而且熟人会弱化我们对消息的思考,又如微博,却因别有用心的控制儿失去了本来的对冲
能力。但这种情况发生的时候, “Seek,think then speak” 就变更加重要了。比如在MH370消失的三四后,
微博上开始有人谣传MH370安全返航,微博上很快澄清,儿微信圈和QQ圈就无法及时跟上,seek在这个过程
中让我没有轻易相信这这不实的信息。

Speak, 在这个容易因为言获罪的时期,发言和行动变得愈加重要。沉默是金,那是因为真理,对于非正义,
speak才是真理。

年度总结 - 2014年的点点滴滴

在2014年还有15天就结束的时候,总结一下自己在2014年的工作生活和学习。2014年的故事比2013年少,但是琐碎的学习项目和整理多了很多。

** 学习环境 **

  • feedly: rss聚合阅读
  • Evernote: 做笔记
  • Pocket: 稍后阅读
  • Trello: 项目管理
  • blog: 知识整理和创造
  • 基本实现Windows, Ubuntu, Android, Mac, iOS平台之间数据同步

** Linux **

  • bash脚本初步
  • Arch Linux安装和基本配置
  • Raspberry Pi: LAMP
  • gunicorn使用
  • nginx入门

** Vim + Git **

  • 使用YCM
  • dotfile(e.g. .vimrc, ...)备份以及工作环境快速配置
  • git以及github常用基础功能

** C **

  • 2048 in C (其实没完成)
  • PDT(a tiny card game demo) in C

** Python **

  • PDT in python
  • 初学Django
  • Virtualenv虚拟python环境
  • pip

** Web **

  • 使用Jekyll
  • github pages
  • 博客采用Markdown
  • Html5 + CSS3基础
  • OAuth 2.0
  • 全平台Cross the Great Firewall
  • Tor
  • 网络基础

** Mac/iOS **

  • Swift语言入门
  • AFNetworking框架
  • ShanbayWords.app Demo
  • SwiftWeather.app Demo
  • iOS Frameworks概览
  • cocoapods管理类库
  • Html5 web离线app

** 读书 **

  • 《开源世界旅行手册》
  • Dive into Python3
  • Getting started with OAuth 2.0
  • Getting Real
  • 《黑客与画家》
  • 《算法概论》(* Algorithms *,PDV)
  • 《一个陌生女人的来信》
  • 《饥饿游戏》(I, II,III)
  • ......

** 设备 **

  • 2014年新入4台设备:(略)。

** 生活 **

  • 六月初去了北戴河,然后是泰山,七月嵩山,恒山和九月西安、华山。
  • Color Run
  • 庆幸只发烧了两天,继续保持。

以上就像列家常一样把2014年能记得的东西都写了个遍,那么问题来了,挖掘机...不:

  1. 很多事情不能及时记录:所有的事情更像是五月份之后做的,五月之前的基本没记住。
  2. 时间花销记录不清楚:总感觉事情多,但是却不知道时间用到哪边去了。
  3. 看的书太少:没有2013年多,成系统的书少,非技术书类少,2014年主要倾向于开发文档和碎片化的文章,对知识和认识的系统化贡献少。

因此2015年在这写方面确实要改善和加强。故制定2015年的主要目标如下(比较宽的目标,无先后,要细分):

  1. 规划:

    • 每周有小结,三月一次书面记录和总结
    • 日常时间花销记录(e.g. RescueTime)
    • 学会记账
    • 静坐和冥想(谁用谁知道!)
    • 读书以及读书笔记,锻炼 -> 100日行动
  2. 学习:

    • Python: 常见标准库的了解和重点学习
    • Swift: GUI基本设计,Cocoa Touch layer基本
    • 网络基础
    • 算法和数据结构:基本算法和数据结构的理解和掌握
    • 数学知识的回顾和加强
    • 英语加强,托福
  3. 杂项

    • 继续未完成的旅行:衡山,以及青海。

2015年想来事情也是比较多的,加油!

Wait, wait! 虽然2015年还有15天到来,但是可以做的事情还是很多,好好想想,and期待惊喜的发生!

P.S. 明年总结的时候这个也会是比较二的一篇,除非我没有进步!

2013年的总结:年度总结 - 过去的2013年。突然觉得这个好矫情啊!!

如何阅读苹果开发文档

coding-woman-5

原文:How to read Apple’s developer documentation

对于很多人来说,这篇文章听起来很奇怪,因为我们已经习惯了 Apple 的 API 文档的工作方式,因此我们精神上已经经过调整以快速找到我们想要的东西。

但这是一个有趣的事实:去年我最热门的文章请求之一是帮助人们真正阅读 Apple 的代码文档。您如何找到您需要的 iOS API,如何浏览所有材料以找到您真正想要的内容,以及您如何深入了解为什么事情按照他们的方式工作?

所以,如果你曾经寻求帮助来理解 Apple 的开发者文档,首先我要让你知道你并不孤单 - 许多人都在努力解决这个问题。但其次,我希望这篇文章会有所帮助:我会尽力解释它的结构,它有什么好处(以及不好的地方),以及如何使用它。

更重要的是,我将向您展示经验丰富的开发人员寻找额外信息的位置,这些信息通常比Apple的在线文档更有价值。

“这是什么?” vs “你怎么用它?”

任何书面的 API 文档通常采用以下五种形式之一:

  1. 接口代码,显示了什么是方法名称和参数,属性名称和类型,以及类似的,带有一些描述它应该做什么的文本。
  2. API 的文本描述了它应该做什么以及一般指导用例。
  3. 广泛使用的有用的 API 示例代码。
  4. 如何使用 API 代码段。
  5. 解决常见问题的简单教程:如何做 X,如何做 Y,以及如何做 Z 等等。

粗略地说,苹果公司第一点做了很多,其次是第二点和第三点,第四点很少,第五点几乎没有。

所以,如果你正在寻找“如何用 Y 做 X ”的具体例子,你最好从我的 Swift 知识库开始 - 这正是它的用途。

了解 Apple 的文档解决的问题,可以帮助您从中获得最大收益。它并不是一个结构化的教程,它不会向您介绍一系列概念来帮助您实现目标,而是作为 Apple 支持的数千个 API 的参考指南。

寻找一个类

Apple的在线文档位于 https://developer.apple.com/documentation/ ,虽然您能在 Xcode 中使用本地副本,但我会说大多数人使用在线版本只是因为他们可以更容易地找到内容。

绝大多数 Apple 的文档都描述了接口,而这正是大多数时候你会看到的。我想使用一个实际的例子,所以请先在您的网络浏览器中打开https://developer.apple.com/documentation/ ,这是所有Apple开发者文档的主页。

apple-developer-documentation

您会看到所有 Apple 的 API 分为 App FrameworksGraphics and Games 等类别,您已经看到了一件重要的事情:所有深蓝色文本都是可点击的,并会带您进入特定框架的API文档。是的,它使用相同的字体和大小,没有下划线,说实话,深蓝色链接和黑色文本之间没有太大区别,但你仍然需要留意这些链接 - 有很多他们,你会用它们来深入挖掘主题。

现在请从 App Frameworks 类别中选择 UIKit,您将看到它的功能(为iOS创建用户界面)的简要概述,标有“重要”(Important)的大黄色框,然后是类别列表。那些黄色的盒子确实值得关注:虽然它们经常被使用,它们几乎总能阻止你犯下根本错误,这些错误导致出现奇怪的问题。

uikit-overview

此页面上重要的是共同描述 UIKit 的类别列表。这是人们经常迷路的地方:他们想要了解像 UIImage 这样的东西,所以他们必须仔细查看该列表以找到它可能出现的合适位置。

uikit-topics

在这种情况下,您可能会查看“资源管理”(Resource Management),因为它的副标题“管理存储在主可执行文件之外的图像,字符串,故事板和 nib 文件”听起来很有希望。但是,您会感到失望 - 您需要向下滚动到 “图像和 PDF”(Images and PDF)部分才能找到 UIImage

这就是为什么我谈过的大多数人只是使用自己喜欢的搜索引擎。他们输入他们关心的类,并且 - 只要它有“UI”,“SK”或类似的前缀 - 它可能是第一个结果。

不要误会我的意思:我知道这种做法并不理想。但是面对搜索一个类或者去 https://developer.apple.com/documentation/ ,选择一个框架,选择一个类别,然后选择一个类,第一个就是更快。

重要提示:无论您选择哪种方法,最终都会在同一个地方,所以只做最适合您的方法。现在,请找到并选择 UIImage

阅读类的接口

一旦选择了您关心的类,该页面就有四个主要组件:概述,版本摘要,接口和关系。

uiimage-overview

概述是“API的文本描述,描述了它应该做什么以及一般指导用例”,我之前提到过 - 我要求你选择 UIImage,因为它是文本描述何时运行良好的一个很好的例子。

当它是我第一次使用的类时,特别是如果它刚刚推出时,我通常会阅读概述文本。但是对于其他一切 - 我之前至少使用过一次的任何类 - 我跳过它并尝试找到我所得到的具体细节。请记住,Apple 文档确实不是一种学习工具:当您考虑到特定目的时,它最有效。

如果您不总是为所选 Apple 平台的最新版本开发,则版本摘要 - 页面右侧的侧栏 - 非常重要。在这种情况下,您将看到 iOS 2.0 +tvOS 9.0+watchOS 2.0+,它告诉我们何时 UIImage 类首次在这三个操作系统上可用,并且它仍然可用 - 如果它已被弃用(不再可用)你会看到像 iOS 2.0-9.0 这样的东西。

此页面上的实际内容 - 以及作为 Apple 框架中特定类的主页的所有页面 - 都列在“主题”标题下。这将列出该类支持的所有属性和方法,再次分解为使用类别:“获取图像数据”,“获取图像大小和比例”等。

uiimage-topics

如果您选择的类具有任何自定义初始化方法,则始终会首先显示它们。 UIImage 有很多自定义初始化方法,你会看到它们都被列为签名 - 只是描述它所期望的参数的部分。所以,你会看到这样的代码:

init?(named: String)
init(imageLiteralResourceName: String)

**提示:**如果您看到 Objective-C 代码,请确保将语言更改为 Swift。您可以在页面的右上角执行此操作,也可以在重要的 iOS 测试版引入更改时启用 API 更改选项。

switch-swift

记住,初始化方法写成 init? 而不是 init 的是容易出错的 - 它们返回一个可选项,以便在初始化失败时它们可以返回 nil

在初始化器的正下方,您有时会看到一些用于创建类的高度专业化实例的方法。这些不是 Swift 意义上的初始化器,但它们确实创建了类的实例。对于 UIImage,你会看到这样的事情:

class func animatedImageNamed(String, duration: TimeInterval) -> UIImage?

class func 部分意味着你将使用 UIImage.animatedImageNamed() 方式调用。

在初始化程序之后,事情变得有点不那么有条理:你会发现属性方法和枚举自由混合在一起。虽然您可以滚动查找您要查找的内容,但我可以认为大多数人只需要 Cmd + F 在页面上查找文字就可以了!

有三点需要注意:

  • 嵌套类型 - 类,结构和枚举 - 与属性和方法一起列出,这需要一点时间习惯。例如,UIImage 包含嵌套的枚举 ResizingMode
  • 任何带有直线穿过的东西都是不推荐使用的。这意味着 Apple 打算在某些时候将其删除,因此您不应将其用于将来的代码,并建议开始重写任何现有代码。(在实践中,大多数API长期以来都被“弃用” - 许多许多年。)
  • 一些非常复杂的类 - 例如,UIViewController - 会将额外的文档页面与其方法和属性混合在一起。查找它们旁边的页面图标,以及一个简单的英文标题,如“相对于安全区域定位内容”(Positioning Content Relative to the Safe Area)。

在页面的底部你会找到 Relationships,它告诉你它继承了哪个类(在这种情况下它直接来自 NSObject),以及它符合的所有协议。当您查看 Swift 类型时,本节更有用,其中协议关系更复杂。

阅读属性或方法页面

您已经选择了一个框架和类,现在是时候查看特定的属性或方法了。查找并选择此方法:

class func animatedResizableImageNamed(String, capInsets: UIEdgeInsets, resizingMode: UIImage.ResizingMode, duration: TimeInterval) -> UIImage?

您应该在 Creating Specialized Image Objects 类别中找到它。

animatedresizableimagenamed-1
animatedresizableimagenamed-2

这不是一个复杂的方法,但它确实展示了这些页面的重要部分:

  • Apple 有几种不同的方法来编写方法名称。之前的那个 - 长 class func animatedResizableImageNamed - 然后是方法页面标题中显示的形式(animatedResizableImageNamed(_:capInsets:resizingMode:duration:)),以及方法页面的声明部分中的形式。
  • 正如您在版本摘要中所看到的(在右侧),此方法在 iOS 6.0 中引入。因此,虽然主要的 UIImage 类从第1天开始就已存在,但这种方法是在几年后推出的。
  • 方法声明的各个部分都是可点击的,都是紫色的。但是要小心:如果你单击 UIImage.ResizingMode,你将去哪里取决于你是否点击了“UIImage”或“ResizingMode”。 (提示:您通常需要单击右侧的那个。)
  • 您将看到每个参数含义和返回值的简要说明。
  • “讨论”(Discussion)部分详细介绍了此方法的具体使用说明。这几乎总是 - 每个页面中最有用的部分,因为在这里您可以看到“不要调用此方法”或“小心......”
  • 你可能会找到一个 See Also 部分,但这有点受欢迎 - 这里只是我们在上一页的方法列表。

现在,UIImage 是一个老类,并没有太大变化,因此它的文档处于良好状态。但是一些较新的 API - 以及许多没有像 UIKit 那样被喜欢的旧 API - 仍然记录不足。例如,来自 SceneKitSCNAnimation 或来自 UIKitUITextDragPreviewRenderer:两者都是在 iOS 11 中引入的,并且在它们发布18个月后仍然包含“无可用概述(No overview available)”作为其文档。

当你看到“没有可用的概述(No overview available)”时,你会失望,但不要放弃:让我告诉你接下来要做什么......

查看代码

尽管 Apple 的在线文档相当不错,但您经常会遇到“无可用概述(No overview available)”,或者您发现没有足够的信息来回答您的问题。

康威定律(Conway's law)指出,设计系统的组织受制于设计,这些设计是这些组织的通信结构的副本。“也就是说,如果你以某种方式工作,你将以类似的方式设计事物。

Apple 在我们行业中的独特地位使他们以一种相当不寻常的方式工作 - 几乎可以肯定它与您自己公司的工作方式完全不同。是的,他们有 API 审核讨论,他们试图讨论API应该如何用两种语言看待,是的,他们有专门的团队来制作文档和示例代码。

但是他们获取示例代码的门槛非常高:通常需要一些非常好的东西才能拿出来,并且通过多层检查来处理法律问题。因此,虽然我可以在一小时内输入一个项目并立即将其发布为文章,但 Apple 需要花费更长的时间才能完成同样的工作 - 他们非常认真地对待他们的形象,而且非常正确。如果你曾经想过为什么文章很少出现在官方 Swift 博客上,现在你知道了!

现在,我说这一切的原因是 Apple 有一个快速使用的捷径:他们的工程师在他们的代码中留下评论的门槛似乎显着降低,这意味着你经常会在 Xcode 中找到有价值的信息。这些评论就像细小的金子(gold dust)一样:它们直接来自 Apple 的开发者,而不是他们的开发者出版(developer publications)团队,而且我对 devpubs 非常热爱,很高兴直接听到来自源头的声音。

还记得我提到过 SceneKit 的 SCNAnimation 在 Apple 的开发者网站上没有记录吗?好吧,让我们来看看Xcode可以向我们展示的内容:按 Cmd + O 打开“快速打开(Open Quickly)”菜单,确保右侧的 Swift 图标为彩色而不是空心,然后键入“SCNAnimation”。

您将看到列出的一些选项,但您正在寻找 SCNAnimation.h 中定义的选项。如果您不确定,选择 YourClassName.h 文件是您最好的选择。

无论如何,如果你打开 SCNAnimation.h Xcode 将显示生成的 SCNAnimation 头文件版本。它的生成是因为原始版本是Objective-C,因此 Xcode 为 Swift 进行了实时翻译 - 这就是 Swift Quickly 框中的彩色 Swift 徽标的含义。

现在,如果你按 Cmd + F 并搜索“class SCNAnimation”,你会发现:

/**
 SCNAnimation represents an animation that targets a specific key path.
 */
@available(iOS 11.0, *)
open class SCNAnimation : NSObject, SCNAnimationProtocol, NSCopying, NSSecureCoding {  
    /*!
     Initializers
     */

    /**
     Loads and returns an animation loaded from the specified URL.

     @param animationUrl The url to load.
     */
    public /*not inherited*/ init(contentsOf animationUrl: URL)

这只是一个开始。 是的,该类及其所有内部都有文档,包括用法说明,默认值等。 所有这一切都应该在在线文档中,但无论出于什么原因它仍然没有,所以要准备好查找代码作为一个有用的补充。

最后的提示

此时,您应该能够查找在线文档以获取您喜欢的任何代码,并查找头文件注释以获取额外的使用说明。

但是,在准备好面对全部 Apple 文档之前,还有两件事需要了解。

首先,您经常会遇到标记为“已归档(archived”)”,“遗留(“legacy”)”或“已退休(“retired)”的文档 - 即使对于相对较新的事物也是如此。当它真的老了,你会看到诸如“这篇文章可能不代表当前发展的最佳实践”之类的消息。下载和其他资源的链接可能不再有效。“

尽管 Apple 是世界上最大的公司之一,但 Apple 的工程和 devpubs 团队几乎没有人员 - 他们不可能在保留所有内容的同时覆盖新的 API。因此,当你看到“存档”文档或类似文件时,请运用你的判断:如果它在某个版本的 Swift 中至少你知道它最近是模糊的,但即使不是,你仍然可能会发现那里有很多有价值的信息。

其次,Apple 拥有一些特别有价值的出色文档。这些都列在 https://developer.apple.com 的页脚中,但主要是人机界面指南。这将引导您完整地为 Apple 平台设计应用程序的所有部分,包括说明关键点的图片,并提供大量具体建议。即使这个文档是构建 iOS 应用程序时最重要的一个,但很少有开发人员似乎已经阅读过它!

接下来做什么?

我之前曾写过关于 Apple 文档的问题 - 我担心那里没有鼓励,但至少如果你在努力,它可能会让你觉得不那么孤单。

幸运的是,我有很多可能更有用的材料:

您认为阅读Apple文档最有效的方法是什么? 在Twitter上发送你的提示:@twostraws

GitHub Pages 自定义域名实践整理

这篇博客将整理在配置博客以及项目 Pages 的自定义域名过程,遇到的问题以及解决方法。Github 的文档对于如何配置自定义域名有详细的介绍,这里不会全部翻译,只重点记录实践的过程,内容涉及为用户网站,公司网站,以及项目网站添加 Apex 域名(qiwihui.com),二级域名(www.qiwihui.com)以及开启 HTTPS。最后,所有指向 www.qiwihui.com 的请求将会被重定向至 https://qiwihui.com

一些注意

Github 支持的自定义域名类型

支持的自定义域名类型 域名例子
www subdomain www.example.com
one apex domain & one www subdomain example.com & www.example.com
apex domain example.com
custom subdomain blog.example.com

GitHub Pages 站支持的域名

GitHub Pages 站类型 在 Github 上 Pages 的默认域名和主机地址 页面被如何重定向 自定义域名举例
User Pages 站 username.github.io 自动重定向到设置的自定义域名 user.example.com
Organization Pages 站 orgname.github.io 自动重定向到设置的自定义域名 org.example.com
用户拥有的 Project Pages 站 username.github.io/projectname 自动重定向到 User Pages 站自定义域名的子目录(user.example.com/projectname project.example.com
公司拥有的 Project Pages 站 orgname.github.io/projectname 自动重定向到 Organization Pages 站自定义域名的子目录(org.example.com/projectname project.example.com

以个人 Pages 项目为例子

开启 Github Pages 功能

在项目 Settings 中,找到 GitHub Pages 这一区域,选择 Source 为对应的要部署的分支,这里我选择 gh-pages branch

gh-pages

其中,选择 master branch 会视 /README.md 为 web 的 index.html,选择 master branch /docs folder 会视 /docs/README.md 为 web 的 index.html

在项目配置中自定义域名

Custom domain 中添加自己的域名并保存:

custom-domain

或者,在项目分支中添加 CNAME 文件,CNAME 文件的内容为

qiwihui.com

这里推荐第二种,尤其对于有设置 CI 的项目,因为 CI 上将第一种设置覆盖。
这一步是比较重要却又容易忽视的一步:

  • 如果添加到 GitHub Pages 中的是 qiwihui.com,那么 www.qiwihui.com 会被重定向到 qiwihui.com
  • 如果添加到 GitHub Pages 中的是 www.qiwihui.com,那么 qiwihui.com 会被重定向到 www.qiwihui.com

这里我选择重定向到 www.qiwihui.com,所以设置为 qiwihui.com

添加 DNS 记录

为了能设置Apex 域名,需要在 DNS 中配置 A 记录指向 github 的 IP:

185.199.108.153
185.199.109.153
185.199.110.153
185.199.111.153

a-record

同时,设置 CNAME 记录将 www.qiwihui.com 指向 qiwihui.github.io,即 <你的 github 用户名>.github.io。对于公司来说,这个地址是 <公司名称>.github.io

www-record

确认 DNS 记录

以下是设置好之后的 DNS 记录情况:

$ dig +noall +answer qiwihui.com
qiwihui.com.            60      IN      A       185.199.111.153
qiwihui.com.            60      IN      A       185.199.110.153
qiwihui.com.            60      IN      A       185.199.108.153
qiwihui.com.            60      IN      A       185.199.109.153
$ dig www.qiwihui.com +nostats +nocomments +nocmd 

; <<>> DiG 9.10.6 <<>> www.qiwihui.com +nostats +nocomments +nocmd
;; global options: +cmd
;www.qiwihui.com.               IN      A
www.qiwihui.com.        28      IN      CNAME   qiwihui.github.io.
qiwihui.github.io.      28      IN      A       185.199.110.153
qiwihui.github.io.      28      IN      A       185.199.108.153
qiwihui.github.io.      28      IN      A       185.199.111.153
qiwihui.github.io.      28      IN      A       185.199.109.153

SSL(HTTPS)配置,强烈推荐开启

勾选 Enforce HTTPS

enfore_https

Github 会自动保持 HTTPS 证书的有效。

项目 Pages

当给项目设置 Pages 时,一般都已经有一个个人或者公司的 Pages 了,如果没有,就可以按以上的过程添加。如果已经设置了,则只需要很简单的两步即可:

以下以个人项目 [qiwihui/fullstackpython.com](https://github.com/qiwihui/fullstackpython.com),设置地址为 fullstackpython.qiwihui.com

  1. 在项目中开启 Github Pages,并添加 CNAME 文件指向 fullstackpython.qiwihui.com

fullstackpython

  1. 在 DNS 记录中添加 CNAME 记录将 fullstackpython.qiwihui.com 指向 qiwihui.github.io,即 <你的 github 用户名>.github.io。对于公司来说,这个地址是 <公司名称>.github.io

fullstackpython-record

一段时间后即可。

参考

没有智能手机的第一天

在停止博客的一个多月时间里,我除了上班的五天白天,再加上周末出去爬山的两天,
剩下的时间很多都花在了我那只智能手机上,微信,QQ,G+,以及一些有节操和没节操
的应用和游戏,这一个多月的时间就这样荒芜的度过了。

直到我暂停出行的计划,更多
地和朋友接触,我才觉得,智能手机在给我带来很大方便的同时,也使得我的生活变得
狭窄,交际变得狭隘。于是我决定:离开智能手机一到两个月,就像去年手机坏掉一样。

第一天

我把手机交给了朋友保管,在微信上和QQ上留下了电话和邮箱,开始了我一个月的非智能
生活。第一天是很艰难的,尤其是在突然离开手机之后,我获取信息的方式直接转移到了
电脑,这就意味着我要很多时候开着电脑,背单词,阅读文章,收邮件等等都从手机向电
脑迁移,无可选择。

第一天计划的实施还是有点水分的,总是不自觉地拿起那台很古老的手机,打开又关闭,
才能继续回来学习工作,然后在iPad上上了一会微信,然后删了微信,整理了所有的不
需要的会上瘾的软件,这才算是正式开始了。

理了头发表决心。

计划

在接下来的一两个月的时间里,计划是这样的:

  • 单词 >100个/天,英文文章>5个/天,有扇贝网站管理。
  • linux + python + mysql
  • 《一个陌生女人的来信》+《Lovely bone》(English) + 《小王子》等
  • 锻炼: 开始我的为其50周的马拉松训练计划。
  • 健康管理: 爬,坐,走,跑......
  • 华山之行(九月份)。

目标

尽最大的诚意,但不知道何时会消失,如果可以,就不要再试一次

玩微信跳一跳小游戏的姿势

微信跳一跳小程序火了,来看一看玩耍的正确姿势。

目标

距离判断 + 按压模拟

距离判断

距离

  1. 手动判断点击/像素点判断,自动点击

  2. OpenCV 图像分析

按压时间计算

  1. 手机屏幕系数
  2. 基于二次函数拟合计算:

综合

  1. 机器学习和神经网络

按压模拟

  1. adb/wda 指令
  2. 机械臂模拟手指点击

参考

头脑王者以及使用mitmproxy

头脑王者开房答题抓题库,答题自动改答案

大纲:

项目地址:qiwihui/tnwz

  1. 如何为找接口请求

    • 获取所有接口及对应接口参数,加密方式;
    • 确定游戏流程;
  2. 精准答题

    • 利用对战抓取题库;
    • 答题匹配
  3. 自动答题

    • 利用mitmproxy修改答题请求

9个值得知道的cURL的用法

对于 HTTP 工程师和 API 设计师来说,使用命令行操作 HTTP 是非常有用的技能。cURL
库和 curl 命令可以给你设计请求,放入管道并查看相应的能力。curl 能力的缺点在于它能覆盖多广的
命令选项。使用 curl --help 会展示出150条不同的选项。这篇文章演示了9个基本的,现实程序用到的 curl 命令。

在这篇教程中我们会使用httpkit的 echo 服务做为端点,回显服务的响应
是它收到 HTTP 请求的 JSON 表示。

创建请求

我们从最简单的 curl 命令开始。

请求

curl http://echo.httpkit.com

响应

{
  "method": "GET",
  "uri": "/",
  "path": {
    "name": "/",
    "query": "",
    "params": {}
  },
  "headers": {
    "host": "echo.httpkit.com",
    "user-agent": "curl/7.24.0 ...",
    "accept": "*/*"
  },
  "body": null,
  "ip": "28.169.144.35",
  "powered-by": "http://httpkit.com",
  "docs": "http://httpkit.com/echo"
}

就这样,我们用 curl 创建了一个请求,curl 使用的 HTTP 动词默认为 GET,请求的资源指向的是
httpkitecho 服务:http://echo.httpkit.com

你可以添加路径和查询变量:

请求

curl http://echo.httpkit.com//path?query=string

响应

{ ...
  "uri": "/path?query=string",
  "path": {
    "name": "/path",
    "query": "?query=string",
    "params": {
      "query": "string"
    }
  }, ...
}

设置请求方法

curl默认的请求方法为 GET ,可以用 -X 参数设置成任何你想要的方法,通常为 POSTPUTDELETE
方法,甚至是自定义的方法。

请求

curl -X POST echo.httpkit.com

响应

{
    "method": "POST",
    ...
}

正如你看到的,http:// 协议前缀可以不使用,因为这是默认假定的。接着实施 DELETE 方法:

请求

curl -X DELETE echo.httpkit.com

响应

{
    "method": "DELETE",
    ...
}

设置请求头部

请求头部允许客户端给服务器提供诸如授权,内容类型等信息。比如,OAuth2 使用 Authorization
来传递访问令牌(access tokens)。curl 使用 -H 选项设置自定义头部。

请求

curl -H "Authorization: OAuth 2c4419d1aabeec" \
     http://echo.httpkit.com

响应

{...
"headers": {
    "host": "echo.httpkit.com",
    "authorization": "OAuth 2c4419d1aabeec",
  ...},
...}

可以使用 -H 多次来设置多个头部。

请求

curl -H "Accept: application/json" \
     -H "Authorization: OAuth 2c3455d1aeffc" \
     http://echo.httpkit.com

响应

{ ...
  "headers": { ...
    "host": "echo.httpkit.com",
    "accept": "application/json",
    "authorization": "OAuth 2c3455d1aeffc" 
   }, ...
}

发送请求体

现今许多有名的 HTTP API 使用 application/jsonapplication/xmlPOSTPUT 资源,
而不是用HTML化的数据。我们试试 PUT 一些 JSON 数据到服务器上。

请求

curl -X PUT \
     -H 'Content-Type: application/json' \
     -d '{"firstName":"Kris", "lastName":"Jordan"}'
     echo.httpkit.com

响应

{
   "method": "PUT", ...
   "headers": { ...
     "content-type": "application/json",
     "content-length": "40"
   },
   "body": "{\"firstName\":\"Kris\",\"lastName\":\"Jordan\"}",
   ...
 }

使用文件作为请求体

将 JSON/XML 写到命令行中是令人头疼的,尤其有时这个文件很大时。幸运的是, curl@readfile
可以很容易地读取文件的文本。如果上面例子中的 JSON 保存为文件 example.json, 我们可以这么做:

请求

curl -X PUT \
     -H 'Content-Type: application/json' \
     -d @example.json
     echo.httpkit.com

发送 HTML 表单数据

如果不能发送带有数据的请求体,可以设置类似 POST 的方法真是没什么用。也许我们可以试试发送 HTML
表单数据。使用 -d 选项,我们可以制定 URL 编码的名称和值。

请求

curl -d "firstName=Kris" \
     -d "lastName=Jordan" \
     echo.httpkit.com

响应

{
  "method": "POST", ...
  "headers": {
    "content-length": "30",
    "content-type":"application/x-www-form-urlencoded"
  },
  "body": "firstName=Kris&lastName=Jordan", ...
}

注意到 POST 这个方法,即使我们没有指明方法,当 curl 看到表单数据时它会指定 POST 方法。
可以使用 -X 选项来覆盖这个方法。请求的 Content-Type 也被自动设置为 application/x-www-form-urlencoded
这样服务器就知道怎么解析数据了。最终,请求体由编码了每一个表单域的 URL 构成。

发送 HTML Multipart/file 表单(上传文件)

当涉及到文件上传的表单时,正如你从写上传文件表单时知道的那样,这些使用 multipart/form-data 文本类型,
带有 enctype 属性。cURL 使用 -F 配合上面介绍的 @readFile 宏来处理。

请求

curl -F "firstName=Kris" \
     -F "[email protected];type=text/plain" \
     echo.httpkit.com

响应

{
  "method": "POST",
  ...
  "headers": {
    "content-length": "697",
    "content-type": "multipart/form-data;
    boundary=----------------------------488327019409",
    ... },
  "body": "------------------------------488327019409\r\n
           Content-Disposition: form-data;
           name=\"firstName\"\r\n\r\n
           Kris\r\n
           ------------------------------488327019409\r\n
           Content-Disposition: form-data;
           name=\"publicKey\";
           filename=\"id_rsa.pub\"\r\n
           Content-Type: text/plain\r\n\r\n
           ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAkq1lZYUOJH2
           ... more [a-zA-Z0-9]* ...
           naZXJw== [email protected]\n\r\n
           ------------------------------488327019409
           --\r\n",
...}

-d 选项一样,当使用 -d 选项时 curl 会自动地默认使用 POST 方法,multipart/form-data 文件
类型头部,计算长度并组成请求体。请注意 @readFile  宏是怎样读取一个文件的文本为任何字符的,这个不是
一个单独的操作,;text/plain 指定了文件的 MIME 文本类型。在未指定的情况下,curl 会尝试嗅探文本类型。

测试虚拟主机,不使用 DNS

通常,在不修改 DNS 覆盖主机的情况下测试一个虚拟主机或者是缓存代理时很有用的。只需使用 cURL 将请求指向
主机的 IP 地址 并覆写 Host 头。

请求

curl -H "Host: google.com" 50.112.251.120

响应

{
  "method": "GET", ...
  "headers": {
    "host": "google.com", ...
  }, ...
}

查看响应头部

API 正越来越多的利用响应头部来提供授权,速率限制,缓存等方面的信息。cURL 使用 -i 选项来查看响应头部
和响应体。

请求

curl -i echo.httpkit.com 

响应

HTTP/1.1 200 OK
Server: nginx/1.1.19
Date: Wed, 29 Aug 2012 04:18:19 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 391
Connection: keep-alive
X-Powered-By: http://httpkit.com

{
  "method": "GET",
  "uri": "/", ...
}

原文:9 uses for cURL worth knowing

关于地球如何进行流浪的一点整理

Earth vs Jupiter

流浪过程梗概

天文学家预言,太阳将会在四百年内发生氦闪爆炸,它将化作一颗巨大而黯淡的红巨星,地球将在这次氦闪中被汽化,太阳系内所有适合生存的行行都将被吞没。为了生存,人类选择驾驶地球逃亡。

联合政府制定了分为5个阶段的逃亡计划:

  1. 刹车时代:利用地球发动机使地球停止自转;
  2. 逃逸时代:全功率开动发动机,使地球达到逃逸速度,飞出太阳系;
  3. 前流浪时代:在外太空继续加速,飞向半人马座比邻星;
  4. 后流浪时代:在中途使地球重新自转,开始减速;
  5. 新太阳时代:地球泊入比邻星轨道,成为这颗恒星的卫星。

这个计划将延续2500年,100代人类。

根据目前放出的《流浪地球》电影预告,电影应该主要讲述刹车时代和逃逸时代摧毁小行星,逃离木星引力这个过程中的故事,至于之后的人类大叛乱,如果有下一部的话,应该就会在下一部了。

流浪过程

以下的部分是对地球流浪过程中的一些问题的讨论和解释。

太阳怎么了?

image

氦闪

如何选择?飞船派和地球派

行星发动机理论可行吗?

这个问题具体请参考流浪地球:行星发动机理论可行吗?

结论:

先说结论:(依据书中提供的发动机数据)

  1. 地球刹住车需要大概50年的时间。
  2. 地球从太阳流浪到比邻星大概需要25600年,差不多正好是书中提到的2500年的十倍。
  3. 消耗的总能量(质能转换)占地球总质量的大概千万分之一,如果人类的质量利用率是100%的话。“挖石头”看来足以支持流浪地球的能量来源。

(未完。。。)

月球一起吗?

刹车

逃逸,引力加速

小行星带

木星

比邻星

参考

使用免费的let’s encrypt证书为网站开启https(已过时)

这篇博客将介绍使用免费的let's encrypt证书, 为网站开启https。

HTTPS简介

(https, http over ssl)

为啥要用Let's Encrypt

(free, easy)

Let's Encrypt介绍

(directory tree)

安装实践

我使用的是Debian 7,其他系统类似。

  1. 使用官方推荐的letsencrypt-auto安装:
$ git clone https://github.com/letsencrypt/letsencrypt

$ cd letsencrypt

$ ./letsencrypt-auto --help
  1. 获取证书

实验前,我已将www.qiwihui.com站点移到了要安装的服务器上,nginx已经在运行,因此可以使用 webroot 模式来获取证书,
先安装webroot插件,这是一个可以不用停止 Web 服务就能让 Let’s Encrypt 验证域名的插件:

location ~ /.well-known {
    allow all;
}

安装证书命令如下:

$ ./letsencrypt-auto certonly --webroot --webroot-path /var/www/blog/ -d qiwihui.com -d www.qiwihui.com --agree-tos --email [email protected]

其中/var/www/blog/为网站根目录。证书申请成功后会提示一下信息,包括证书存放目录和证书过期时间:

IMPORTANT NOTES:

- Congratulations! Your certificate and chain have been saved at
/etc/letsencrypt/live/qiwihui.com/fullchain.pem. Your cert will
expire on 2016-07-08. To obtain a new version of the certificate in
the future, simply run Let's Encrypt again.

- If you like Let's Encrypt, please consider supporting our work by:

Donating to ISRG / Let's Encrypt:  https://letsencrypt.org/donate
Donating to EFF:                    https://eff.org/donate-le

重要提示:需要将站点的DNS指向对用的服务器,否则会提示申请不过。

  1. 配置Nginx

首先生成2048位 DH parameters:

$ mkdir -p /var/www/ssl/
$ sudo openssl dhparam -out /var/www/ssl/dhparam.pem 2048

Nginx的配置如下:

server {
        listen 443 ssl;

        server_name qiwihui.com www.qiwihui.com;

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_certificate     /etc/letsencrypt/live/qiwihui.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/qiwihui.com/privkey.pem;

        ssl_dhparam /var/www/ssl/dhparam.pem;

        ssl_prefer_server_ciphers  on;
        ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';

	#网站其他配置
}

如果想要开启全站 https 的话,需要将 http 转向到 https,再添加一个 server 就好了:

server {
    listen 80;
    server_name qiwihui.com www.qiwihui.com;
    return 301 https://$server_name$request_uri;
}

修改完成后reload nginx 就可以了:nginx -s reload

验证检测

  1. 用浏览器打开目标网址https://qiwihui.com,可以查看到证书信息:

10-https-on-qiwihui-com

  1. 使用 Qualys ssllabs 在线测试服务器证书强度以及配置正确性:

10-ssllabs-results

后续更新

Let’s Encrypt 的有效期只有90天,官方客户端不支持持续更新,所以要设置自动更新,让证书一直有效。

在crontab 中设置定时任务:

30 2 * * 1 /root/letsencrypt/letsencrypt-auto renew >> /var/log/le-renew.log
35 2 * * 1 /etc/init.d/nginx reload

上述配置会再每周一凌晨2:30执行letsencrypt-auto renew,在2点35分重新加载nginx配置,同时更新日志会在写在/var/log/le-renewal.log中。

总结

Let's Encrypt TLS/SSL is free.

用Homebrew 安装 v2ray 以及 Homebrew-cask 安装 V2RayX

最近开始转向使用 v2ray 作为主要的翻墙工具,在 macOS 上安装和使用都需要下载编译好的软件包然后解包使用,不是很方便,联系到 macOS 下常用的包管理 Homebrew,何不自己提交一个?

v2ray及V2RayX是啥?

V2Ray 是一个模块化的代理软件包,它的目标是提供常用的代理软件模块,简化网络代理软件的开发。

简单说 v2ray 就是翻墙代理软件(但不止于软件,是一个平台)。V2RayX 就是 macOS 下一个简单的 v2ray 的GUI程序。

Homebrew呢?

macOS上强大的包管理工具,类似于Ubuntu的apt。

安装:

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

使用,比如下载 curl:

brew install curl

安装v2ray

不知道为啥,官方的Homebrew Formula不接受 v2ray 源,所以只能自己写了,见 qiwihui/homebrew-v2ray

安装:

brew tap qiwihui/v2ray
brew install v2ray-core

使用:

首先,需要配置 /usr/local/etc//v2ray.config.json
其次,配置v2ray登录时自动开启:

brew services start v2ray-core

或者,可以手动运行:

v2ray -config=/usr/local/etc//v2ray.config.json

安装V2RayX

我向官方 Homebrew-Cask 提交了一个Formula,可以直接使用如下命令安装

brew cask install v2rayx

不过GUI毕竟不能覆盖命令行的全部功能,所以能用命令行v2ray的话,就尽量不使用V2RayX吧。

Nginx服务器设置HTTP/2

我的博客已经支持了 HTTP/2, 在此将介绍如何在 Nginx 上设置 HTTP/2 及相关注意事项(坑)。

前提

HTTP/2 安装需要以下前提:

  • Nginx 版本在1.9.5以上
  • OpenSSL 版本在 1.0.2g 以上(支持 ALPN)

不同 Linux 系统对于 ALPNNPN 的支持可以参见下表

Operating System OpenSSL Version ALPN and NPN Support
CentOS/Oracle Linux/RHEL 5.10+ 0.9.8e Neither
CentOS/Oracle Linux/RHEL 6.5+, 7.0+ 1.0.1e NPN
Ubuntu 12.04 LTS 1.0.1 NPN
Ubuntu 14.04 LTS 1.0.1f NPN
Ubuntu 16.04 LTS 1.0.2g ALPN and NPN
Debian 7.0 1.0.1e NPN
Debian 8.0 1.0.1k NPN

所以要么升级使用带有 OpenSSL 1.0.2 的 Ubuntu 16.04 LTS,要么从头编译 Nginx.

我的服务器系统是 Debian 7, OpenSSL 版本是1.0.1t, 所以需要重新编译 Nginx 和 OpenSSL.

安装过程

安装 OpenSSL

下载并安装 OpenSSL:

# cd ~
# wget http://www.openssl.org/source/openssl-1.1.0e.tar.gz
# tar -zxf openssl-1.1.0e.tar.gz
# cd openssl-1.1.0e
# ./configure
# make
# sudo make install

使用 openssl version 来查看安装好的 OpenSSL 的版本。

其他 Nginx 编译需要的环境

需要编译 PCRE 库和 zlib 库[]:

# wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.40.tar.gz
# tar -zxf pcre-8.40.tar.gz
# cd pcre-8.40
# ./configure
# make
# sudo make install
# wget http://zlib.net/zlib-1.2.11.tar.gz
# tar -zxf zlib-1.2.11.tar.gz
# cd zlib-1.2.11
# ./configure
# make
# sudo make install

编译 Nginx

首先,下载最新的 nginx,我使用 1.10.3.

cd ~
wget -c http://nginx.org/download/nginx-1.10.3.tar.gz
tar xzvf nginx-1.10.3.tar.gzcd nginx-1.10.3

其实,获取 Nginx 配置参数,使新版 Nginx 和之前的配置一样

# nginx -V

nginx version: nginx/1.9.6
built by gcc 4.7.2 (Debian 4.7.2-5) 
built with OpenSSL 1.0.1t  3 May 2016
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-file-aio --with-threads --with-ipv6 --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_ssl_module --with-cc-opt='-g -O2 -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie'

上述配置用已经有 --with-http_v2_module 选项了,还需要在上述配置参数后面加上 --with-openssl=/path/to/your/openssl-1.1.0e 指向新版本的 OpenSSL 文件夹

./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-file-aio --with-threads --with-ipv6 --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_ssl_module --with-cc-opt='-g -O2 -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie' --with-openssl=/home/qiwihui/openssl-1.1.0e

可以看到大致输出为

Configuration summary
  + using threads
  + using system PCRE library
  + using OpenSSL library: /home/qiwihui/openssl-1.1.0e
  + md5: using OpenSSL library
  + sha1: using OpenSSL library
  + using system zlib library

  nginx path prefix: "/etc/nginx"
  nginx binary file: "/usr/sbin/nginx"
  nginx modules path: "/usr/lib/nginx/modules"
  nginx configuration prefix: "/etc/nginx"
  nginx configuration file: "/etc/nginx/nginx.conf"
  nginx pid file: "/var/run/nginx.pid"
  nginx error log file: "/var/log/nginx/error.log"
  nginx http access log file: "/var/log/nginx/access.log"
  nginx http client request body temporary files: "/var/cache/nginx/client_temp"
  nginx http proxy temporary files: "/var/cache/nginx/proxy_temp"
  nginx http fastcgi temporary files: "/var/cache/nginx/fastcgi_temp"
  nginx http uwsgi temporary files: "/var/cache/nginx/uwsgi_temp"
  nginx http scgi temporary files: "/var/cache/nginx/scgi_temp"

最后,编译并安装

# make
# sudo make install

之后就可以看到已经安装好了新版 Nginx了。

配置

配置 HTTPS

请参考之前博客 使用免费的let’s encrypt证书为网站开启https

开启 http/2

第一步完成后就设置好了一个 HTTPS 的网站了,在此基础之上开始 HTTP/2。首先,开启 HTTP/2:

listen 443 ssl http2 default_server;

其次,去除HTTP/2不支持的旧的不安全的密码套件[5]:

ssl_prefer_server_ciphers on;
ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;

最后,检查配置并重启 Nginx:

# nginx -t

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

# sudo /etc/init.d/nginx restart

检查

至此,不出问题的话你的服务器已经开始支持 HTTP/2 了,可以使用 HTTP/2 Test 来检测是否支持了 HTTP/2

其中,对 ALPN 的支持可以使用 OpenSSL 来检测:

echo | openssl s_client -alpn h2 -connect qiwihui.com:443 | grep ALPN

如果输出中包含 ALPN protocol: h2,说明服务端支持 ALPN,如果输出中包含 No ALPN negotiated,说明服务端不支持 ALPN

同时,在 Chrome 的开发者工具中也可以看到协议的版本

同时还可以对 HTTP/2 进行优化,请参见[6],不赘述了。

附录

附录一份 Nginx 的 http/2 简单配置

server {
        listen 443 ssl http2 default_server;
        listen [::]:443 ssl http2 default_server;

        server_name example.com www.example.com; 

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

        ssl_dhparam /path/to/your/dhparam.pem;
        ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;

        ssl_prefer_server_ciphers  on;
        add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";
        ssl_session_cache shared:SSL:5m;
        ssl_session_timeout 1h;
        
        root /path/to/your/folder/;
        index index.html;
}

server {

    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
}

参考

[1]. Supporting HTTP/2 for Google Chrome Users
[2]. 为什么我们应该尽快支持 ALPN?
[3]. Nginx官方教程 INSTALLING NGINX OPEN SOURCE
[4]. serverfault问题: Nginx configured with http2 doesn't deliver HTTP/2
[5]. TLS 1.2 Cipher Suite Black List
[6]. Optimizing Nginx for Best Performance

《平凡的世界》与平凡的世界

我今天要去两个书店淘书,万圣园和蓝羊书坊,便就想起了去年八月读过的小说《平凡的
世界》。小说是万学教育的老师推荐的,在刚开始工作的第二个月了就发奋四个星期读完
了,时至今日已经过去快一年了,可书中的许多情节依旧历历在目。

书中的故事

《平凡的世界》讲述了以孙少安和孙少平为代表的普通人在大时代历史进程中走过的艰难
曲折的道路。时间在上个实际70到80年代,从混乱到改革开放的时期,时代的变革深刻地
影响这每一代人的命运。

哥哥少安一直在家劳动,与村支书田福堂的女儿田润叶青梅竹马,两人互有爱慕之心,却
遭到田福堂的反对,经过痛苦的煎熬,少安与山西勤劳的姑娘秀莲结婚,润叶也只能含泪
与倾慕她的李向前结婚,改革开放后,机灵的少安看到机会,先是带领生产对实施责任制,
后又进城拉砖,用赚的钱办砖窑,成为冒尖户。

少平原来在县城高中读书,毕业后回乡做了一名老师,但他没有消沉,与县革委副主任田
福军女儿田晓霞建立了友情,青春的梦想和追求也激励着他到外面去“闯荡世界”,他从漂
泊的揽工汉成为正式的建筑工人,最后又获得了当煤矿工人的好机遇,而田晓霞毕业后也
到省城成为了一名记者。在两人产生了强烈的感情时候,田晓霞却因在抗洪采访中为抢救
灾民光荣牺牲,少平悲痛不已。后来少平在一次事故中毁容,他没有被不幸压跨,重新回
到矿山迎接新的挑战。

平凡的世界里

《平凡的世界》是一部很长的小说,但是文字十分流畅,很快就可以带入我们进入这两个
在黄土高原上闪亮的两个人的故事。在那个时代变革明显的时代,个人的命运也和时代的
命运紧紧地联系在一起,与此同时,个人的追求和梦想也在一步一步地影响这他们的轨迹。
少安看准了时代的先机,少平追求不一样的外面世界,这也深深地影响他们的命运和感情。

他们的感情都有着悲剧性的一面。少安与田润叶,少平与田晓霞,最终没能在一起,甚是
惋惜。书中在许多细节上的描写令人感动,比如少平与少安相约两年之后再相见的那段,
以及少平在得知田晓霞牺牲之后的感情变化,让人心中为之而动。

我喜欢书中提到的叶赛宁的一首诗:不惋惜,不呼唤,我也不啼哭……金黄的落叶堆满我心间,
我已经不再是青春少年……

年度总结 - 过去的2013年

写在2013年到2014年还有不到一个月的时候, 对我第一年工作的状态有一个简单的描述, 每年都要给自己写一个年终总结。

  • 选择了和有共同目标的团队一起调研,奋斗的感觉真好。
  • 选择了请假写论文,这样我可以学霸一次。
  • 选择了毕业向喜欢的女生表白,不想彼此错过,虽然很短暂。
  • 选择了毕业工作,放弃保研,不去考公务员,我不后悔自己的选择,这会是一次milestone。
  • 选择了在公司附近租房,虽然有些贵,但是不用挤三小时地铁。
  • 选择了扇贝,收获了一种学习习惯。
  • 选择了写博客,建个人博客Daozhang.info,写BUCTML网站(inBuilding),虽然还在构建中。
  • 选择了贡献开源项目@github,因为我开始讨厌封闭的W系统。
  • 现在每天都要看几页书,虽然少,但积累的力量很恐怖,不看不爽说。
  • 我喜欢吃巧克力,而且是黑巧克力。
  • 选择了学轮滑,耍蝴蝶刀,因为我喜欢。
  • 选择了给自己一个KeepStudying梦,我会为之而努力!多谢老姐的泼热水支持。
  • 感谢一路相伴的大学童鞋,四年的感觉真好,重逢必有时!
  • 工作团队很喜欢,哈哈!

  • 因为生病,错过了班级的毕业旅行,再也没有了。
  • 今年没有去宁波,只能等到明年了,我不是拖延症。
  • Facebook, Twitter, G+, QQ, Wechat...分散了我的精力,有社交依赖了。
  • 没能把Google Glass的购买码送出去,人品。
  • 我不想扯到关于买房买车的问题。

使用 Travis CI 自动更新博客

Travis CI 自动检测代码变化,拉取,编译博客并部署到 GitHub Pages

写好博客之后,部署总会占去一段时间:编译、部署、推送和检查。手动部署多了也就烦了,一则容易出错,
比如把 master 分支用 gh-pages 分支覆盖了,二则劳动是重复的,重复的劳动就应该自动化去解决。

最早的想法

使用 GitHub Webhooks 实现自动部署,这就需要有一台服务器,在服务器上启动服务接受 Github 的
回调,然后拉取代码,编译,将编译后的代码要么部署在同一台服务器上,要么推送到代码 gh-pages 分
支上。前者额外需要编写服务,配置博客 Nginx,可能还需要配置 HTTPS,以及对服务器进行加固,总归
就是需要额外的更多东西来支持。所以还是觉得用已经存在的线上自动化服务方便一些(其实就是懒)。

Travis CI

持续集成(Continuous Integration,CI)的 SaaS 服务,好处不言而喻。

配置 Travis

gem install travis
travis login
language: node_js
node_js:
- 6.9.0
install:
- git submodule update --init
- npm install hexo-cli -g
- npm install
script:
- hexo clean
- hexo generate --deploy --quiet
branches:
  only:
  - master
cache:
  directories:
  - node_modules
notifications:
  email:
    recipients:
    - [email protected]
    on_success: change
    on_failure: always

使用 Travis 自动部署

ERROR Deployer not found: git

hexo-deployer-git

npm install hexo-deployer-git --save

配置认证

往 Github 仓库中提交代码是需要认证的,不管是用用户密码,Access Token还是SSH key。一种方法是
直接将认证写在 .config.yml 中,不是说不行,是太年轻。好在 Travis CI 不仅支持加密文件
也支持加密 Keys,这就为认证这一块
扫清了道路,我决定使用 OAuth 认证 Git 来提交代码到仓库中。

操作步骤:

  1. 生成 Github Personal Access Token;

  2. 使用 Travis CI 命令行加密 Personal Access Token;

    travis encrypt GH_TOKEN=<token> --add
  3. .travis.yml 中添加配置

    before_install:
    - git config --global push.default matching
    - git config --global user.name "qiwihui via Travis CI"
    - git config --global user.email "[email protected]"
    - sed -i'' "/^ *repo/s~github\.com~${GH_TOKEN}@github.com~" _config.yml
    env:
      global:
      - secure: IYXTVHItgbEn...

在 Travsi CI 中配置项目

  1. Publicizing or hiding organization membership

自定义域名

  1. qiwihui.github.io/qiwihui/ => blog.qiwihui.com
  2. Enforce https

胜利完成!

参考

使用Github提供的功能做一些有趣的事情

Github 除了作为代码仓库并与全世界的开发者共同协作外,利用 Github 还能做一些其他有趣的事情,这篇文章将简要介绍这些功能。

个人博客

Github 并非博客托管站点,但确实很多程序员都喜欢用来写博客。使用 Github 写博客的方法有以下几种。

  1. Github pages
  2. 直接提交静态文件到 github pages(Hexo 等)
  3. 利用 github issue 写博客,配合 Github API 做博客展示
  4. 利用 github wiki 写博客
  5. 直接在 repo 里提交 markdown 文件

Github issues 做博客评论

个人简历

  1. 使用 repo 保存简历
  2. 根据 Github 的贡献生成个人简历

书籍以及 Gitbook

GitHub做图床

Github做监控

GitHub敏感信息泄露:获取以及监测

Github作为项目管理以及功能增强

Github Zenhub

Github issue as GTD tool

比如 qiwihui/pocket_readings

参考

扇贝300天小记:坚持的力量

到今天,我已经在扇贝网上完成了300+天的背单词和阅读文章
, 单词量虽然不算很多,但是在这过程中的感想还是值得分享的。

我喜欢道家之数“三”,道家曰:一生二,二生三,三生万物。天下之事情有三而生,即是
刚刚开始,故而在300天左右的时候是很适合分享的。

起因和坚持的动力

背单词的最初起因里带有种愤,气愤,大体就是成为前女友眼中的“极品前任”。虽然这
在最出的几天很激励,但是这种感觉很快就消失了,之间有一段迷茫期,不知到自己为什么
背单词。直到有一天我找到了另一种坚持,一个自己一直想
去的地方:Multnomah Falls,这是在美国Oregon这州的一个瀑布,我被她绚丽的落差所折
服,有生之年不然是要前往的,虽然觉得去旅游和背单词不是很搭边,没什么联系。

就是这个,犹如在远方等待着我的少女,让我坚持到了现在:

06-multnomahfalls

方法

扇贝网是我知道的为数不多的几个背单词的网站,除了单词,还有新闻文章,书籍,以及一
起背单词的小组和论坛,这在一定程度上激励着你一直坚持背下去,小组的作用更加的明显
,不打卡就踢人的制度很合适。对单词的单词的理解程度也完全靠自己的自觉。

感受

背单词最大的感受是你不能只背单词,只背单词如同嚼蜡,刚开始就会觉得很舒服,没有营
养又损害身体,带来的效果也很小。单词背的同时结合着文章的阅读效果是很明显的,以前
老师常说的在语境中理解单词的含义单体就是这个意思。其次是要让自己处在一种英语的氛
围中,可以用英语阅读写文章,使用英文和朋友交流,使用英文的办公环境......如此种种
,都是很有效果的。再者,别人和自己的经验告诉我:背单词应该是意见很快乐的事情,如
果在这个过程过程中觉得很痛苦,那么是应该考虑一下自己的方法了。

李笑来在《把时间当作朋友中》中提到了背单词的方法:

在背单词的时候,事实上,在做所有类似的必须记住大量信息的工作的时候,一定要想办法
由衷地把这件事当成一件快乐的事情来做。

我的一个朋友曾跟我分享他的做法:当年他终于搞明白要拿到奖学金就得获得GRE高分的时
候,背单词量要求吓了一跳。他说,他用两天才说服自己这应该是件快乐的事情。

一共要搞定20000个单词,而因此可能获得的奖学金是40000美元左右 且连续4年没有失业的
可能,那么每个单词就值20元人民币,这还只不过是算了一年的收入而已。

所以,他终于明白背单词是很快乐的,他每天都强迫自己背下200个单词,每在确定记住了
一个单词前面画上一个勾时,他就想象一下刚刚数过一张20元人民币的钞票。每天睡觉的时
候总感觉心满意足,因为今天又赚了4000块!

在这样的坚持了300+天以后,我觉得在更多的地方都体会到了一个坚持力量(很鸡汤的一
句话),但是确实是,坚持锻炼,或者开始每天/每两天更新一千字博客。

年度总结 - 我的2015, 做了很多事, 欠了很多债

一句话总结2015年: 做了很多事, 欠了很多债。

2015年

年初三月离开了毕业后的第一份正式工作, 七月底加入青松, 中间的四个月从迷茫焦虑, 到完成第一次知识整合和补充, 算是一次小的飞跃。 在新团队的这
半年是自己能力和知识增长最快的半年。

感谢一路陪伴的岩, 许多事情不再纠结, 更有勇气去做一件事情。

整理和输出的东西太少, Evernote 和 Pocket 上记录的文章基本未有效整理, 博客自上次更新已是半年, 这点需要改进。

个人项目上, COMICS项目, 微信RSS项目和自己的公众号(我都忘了叫啥了)相继停止了维护/更新。 开源项目基本维持在阅读和 fork 别人项目的水平上, 对开源项目的贡献不够。
自己开发和信息收集的方法工具没有整理。

2015年阅书寥寥, 《三体》和《量子物理史话》是为数不多的能记住的, 倒是知乎上迄今645万字的阅读量确实令我咋舌, 读书的质和量都有待提高。

语言能力上, 英语继续保持之前的学习量, 只是意语刚开始没多久就放弃了。

每年都会学一项不一样的技能或者挑战一件不一样的事情, 2015年一个人背包旅行了一个月, 见识了江南的风景, 新增的技能就算滑雪了。相较于之前, 2015年的技能成长
比较缓慢。

2015年半壮半胖得长了十斤, 体重达到了历史最高点, 这是一段时间失衡与调整的结果。 2016年需要停止增长, 增加体能和力量训练。

2016年目标

个人能力成长上:

  • 编程能力上, Python/Django 编程能力加强, 深入理解代码运行的底层机制;
  • 计算机基础知识的补足, 主要是网络知识和数据结构, 算法知识等;
  • 前端能力以及产品化能力;
  • 系统化知识的学习方法和框架, 增加整理和输出;
  • 利用自己掌握的资源, 建立信息收集和整合项目, 打造自己的工具。

生活上

  • 一两项新的技能, 一项自我挑战, 一张共同的愿望清单;
  • 乐观一些, 简单一些。

2015年是变革与变化, 机遇和挑战。新的一年, 新的成长, 新的奋斗, 不变的梦想!

附录一下逗比的过去:

年度总结 - 2017年

工作:

  • 安全方向;

  • 大数据;

  • 机器学习初学:线性回归,神经网路,SVM;

  • 深度学习入门

  • 语言:Python, iOS, Go

  • 阅读/读书:非技术的书阅读较少,五本左右

  • 知识整理系统:RSS, PinBoard -> Pocket -> IFTTT -> Evernote记录,github分析

  • 开源项目,维护乏力,hiwifi-ss

  • 我的专长:

    • 数学基础:机器学习和深度学习有天然的优势
    • 我的弱点:对于做事情的热度不够持久=> 利用这点,每种学一段,交替进行
    • 情绪管理:无法控制自己的情绪对于自己工作的影响,一度不知道怎么控制自己

目标:

  • 网路自由化和安全化:RSS,翻墙,去中心化运动,网络中立

产品和实现

  1. 机器学习和深度学习
  2. Python
  3. iOS, macOS开发
  4. 架构
  5. 生活:自我认知,恋爱,惰性
  6. 情绪影响工作

一个关于数学概率的问题

题目--百万英雄

你参加一个游戏,在你面前有4张1000万支票,其中一张是真的。游戏开始,你选了一张,之后主持人在剩下
的3张里,选择一个展示出来,验证后发现是假的。

问题:请分情况理性分析,此时,你的参赛权的价格

  • 情况一:不允许修改之前的选择
  • 情况二:有重新选择的权利

回答:请用下面两种方法分别作答

  • 方式1(理论推导):请给出理论推导和计算过程,情况二需说明如何行使权力;
  • 方式2(编程模拟):使用程序准确客观地模拟上述两种情况下,选手平均获得的奖金,得到参赛权的价格。

解答

方式1(理论推导)

情况1: 不能重新选择时获奖的概率是1/4
情况2: 可以重新选择时是3/8
理由:

  1. 不能重新选择时,你的选择不受主持人选择的影响,故为 1/4;
  2. 可以重新选择时,会受主持人的影响,是后验概率;第一步选择时,有四种可选,有 1/4 选择真实的,3/4 选择错误的,主持人的选择在剩下的三个中排除了一个错误的,剩两个。选择真实后重选,再次选中的概率为0,故为 1/4 * 0 = 0;选择假的后重选,选中概率为1/2,故为 3/4 * 1/2 = 3/8;总的选中真的概率为 0 + 3/8 = 3/8

方式2(编程模拟):

import random


class Hero:
    """英雄
    """

    def __init__(self):
        self.num = None

    def pick(self, nums):
        self.num = random.choice(nums)


class Host:
    """主持人
    """
    def __init__(self):
        self.num = None

    def pick(self, nums, bnum):
        """主持人
        """
        self.num = random.choice(nums)
        while bnum == self.num:
            self.num = random.choice(nums)

class MH:
    """游戏过程
    """
    def __init__(self):
        self.nums = [0, 1, 2, 3]
        self.host = Host()
        self.hero = Hero()
        self.bnum = random.randint(0, 3)

    def reward(self):
        """奖励
        """
        if self.hero.num == self.bnum:
            return 100
        else:
            return 0

    def play_without_regret(self):
        """不允许修改之前的选择
        """
        self.hero.pick(self.nums)
        self.nums.remove(self.hero.num)
        self.host.pick(self.nums, self.bnum)
        return self.reward()

    def play_with_regret(self):
        """有重新选择的权利
        """
        self.hero.pick(self.nums)
        self.nums.remove(self.hero.num)
        self.host.pick(self.nums, self.bnum)
        self.nums.remove(self.host.num)
        self.hero.pick(self.nums)
        return self.reward()

sum1 = 0
sum2 = 0
# 模拟10000次
times = 10000
for i in range(times):
    sum1 += MH().play_without_regret()
    sum2 += MH().play_with_regret()
avg1 = sum1/float(times)
avg2 = sum2/float(times)
print(avg1)
print(avg2)

结果:

>> python bh.py
24.81
37.12

与理论计算一致

引申

三门问题(Monty Hall Problem)
电影《决胜21点》

Hexo git deployer 删除了提交历史记录该怎么整?

原文:Hexo git deployer removes commits history? Let's do something about that!

我发现 Hexo 是构建博客和应用许多知名的软件开发原则的好工具,其中之一是自动化。这就是我决定将此博客与 Travis CI 集成以执行 GitHub pages 部署的原因。但几天之后我注意到一个重要问题 - 从 CI 服务器部署新版本的博客导致从 master 分支中删除所有提交并从一次又一次地初始化提交开始。我花了一段时间才找到解决这个问题的工作方案。这篇博文解释了这个问题的简单解决方案。

为什么 hexo deploy 会首先删除历史记录?

让我们从了解实际发生的事情开始。当你为 git 部署选项运行 hexo deploy [1]命令时,Hexo 会创建一个名为 .deploy_git 的隐藏文件夹,并将生成的文件从 public 文件夹复制到该文件夹。接下来,它初始化目标为 Hexo 远程部署分支的git存储库(如果它尚不存在),并从该文件夹执行 git push --force 到仓库和你在 _config.yml [2]文件中定义的分支。

清单1. 博客的部署配置

deploy:
  type: git
  repo: [email protected]:wololock/wololock.github.io.git
  branch: master

如果你从本地计算机构建和部署博客,并且永远不会删除(或意外丢失)你的博客源代码,你可能永远不会遇到此问题。当你从未被擦除的工作空间执行此操作时,则存在具有完整历史记录的文件夹 .deploy_git,并且 hexo deploy 仅推送实际修改的那些文件。当你迁移到像 Travis CI 这样的 CI 服务器时,这就变了,因为它使用干净的工作区和仓库的新克隆执行构建。在这种情况下,.deploy_git 文件夹根本不存在,将从头开始重新创建。

那么如何部署和保存历史呢?

我发现解决方案非常简单。以前我负责部署的 .travis.yml 文件部分看起来像这样:

清单2. 以前的 Travis CI 部署配置

deploy:
  skip_cleanup: true
  provider: script
  script: hexo deploy
  on:
    branch: develop

只要我将更改推送到 develop 分支,它就会触发 hexo deploy。在这种情况下,它最终创建了一个新的 .deploy_git 文件夹并强制将初始提交推送到 GitHub 仓库。然后,我做了一个小改进 - 我创建了一个简短的 bash 脚本。

清单3. 部署博客使用的脚本

#!/bin/bash

# 使用已部署文件初始化目标
git clone --depth 1 --branch=master https://github.com/wololock/wololock.github.io.git .deploy_git

cd .deploy_git

#从 ../public/ 复制之前删除所有文件
# 这样 git 可以跟踪上次提交中删除的文件
find . -path ./.git -prune -o -exec rm -rf {} \; 2> /dev/null

cd ../

# 部署
hexo clean
hexo deploy

这个脚本完全按照它在注释中所说的那样做:

  • 它将 master 分支从远程存储库克隆到 .deploy_git 以获取现有提交历史记录。
  • 然后它从 .deploy_git 中删除所有非 git 对象存储库文件,因此从 public 文件夹复制文件将跟踪已删除的文件。
  • 最后 - 它执行常规部署的 hexo deploy 命令。

最后,这是在引入部署bash脚本后的部署配置部分:

清单4. 当前的 Travis CI 部署配置

deploy:
  skip_cleanup: true
  provider: script
  script: sh deploy.sh
  on:
    branch: develop

由于这个解决方案,我能够保留站点更新的历史记录,并跟踪使用给定站点更新实际修改的文件的更改。

github hexo history

最后的话

我希望你发现这篇文章很有用。它描述了 Hexo + Travis CI + GitHub 用例的解决方案,但它可以解决从 CI 服务器环境运行时其他类似静态站点生成器可能遇到的问题。

参考

  1. Documentation: https://hexo.io/docs/deployment
  2. https://github.com/wololock/wololock.github.io/blob/develop/_config.yml#L88-L93

Elasticsearch cheat sheet

1

curl -X<REST Verb> <Node>:<Port>/<Index>/<Type>/<ID>

curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
  "query": { "match_all": {} },
  "_source": ["account_number", "balance"],
  "sort": { "balance": { "order": "desc" } }
}'

bool must: 所有的查询都必须为真
bool should: 只要有一个查询匹配
bool must_not: 查询列表中的的所有查询都必须都不为真

2. 执行过滤器

_score: 指定的搜索查询匹配程度的一个相对度量。得分越高,文档越相关,得分越低文档的相关度越低。
Elasticsearch中的所有的查询都会触发相关度得分的计算。对于那些我们不需要相关度得分的场景下,Elasticsearch以过滤器的形式提供了另一种查询功能。

过滤器在概念上类似于查询,但是它们有非常快的执行速度,这种快的执行速度主要有以下两个原因:

  • 过滤器不会计算相关度的得分,所以它们在计算上更快一些
  • 过滤器可以被缓存到内存中,这使得在重复的搜索查询上,其要比相应的查询快出许多。

通常情况下,要决定是使用过滤器还是使用查询,你就需要问自己是否需要相关度得分。如果相关度是不重要的,使用过滤器,否则使用查询。

curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
  "query": {
    "filtered": {
      "query": { "match_all": {} },
      "filter": {
        "range": {
          "balance": {
            "gte": 20000,
            "lte": 30000
          }
        }
      }
    }
  }
}'

3

doc['my_field'].value和_source.my_field之间的不同:

  • 首先,使用doc关键字,会使相应的字段加载到内存,执行速度更快但是更耗费内存;
  • 第二,doc[...]符号 仅允许简单的值字段,只在基于字段的非分析或者单个项上有意义;
  • _source加载、分析source,然后仅仅返回相关部分的json。

参考

在iOS-Swift项目中集成CppJieba分词

在垃圾短信过滤应用 SMSFilters 中,需要使用 Jieba 分词库来対短信进行分词,然后使用 TF-IDF 来进行处理` 分词库是 C++ 写的,这就意味着需要在Swift中集成 C++ 库。
在官方文档 "Using Swift with Cocoa and Objective-C" 中,Apple只是介绍了怎么将 Swift 代码跟 Objective-C 代码做整合,但是没有提C++,后来在官方文档中看到了这样一段话:

You cannot import C++ code directly into Swift. Instead, create an Objective-C or C wrapper for C++ code.

也就是不能直接导入 C++ 代码,但是可以使用 Objective-C 或者 C 对 C++ 进行封装。所以项目中使用 Objective-C 做封装,然后在 Swift 中调用,下面就是这个过程的实践,Demo 代码见 SwiftJiebaDemo

整合过程

分成三步:

  1. 引入C++文件;
  2. 用 Objective-C 封装;
  3. 在 Swift 中 调用 Objective-C;

引入C++文件

Demo中使用的是"结巴"中文分词的 C++ 版本 yanyiwu/cppjieba。将其中的 include/cppjieba 和依赖 limonp 合并,并加入 dict 中的 hmm_modeljiaba.dict 作为基础数据,并暴露 JiebaInitJiebaCut 接口:

//
//  Segmentor.cpp
//  iosjieba
//
//  Created by yanyiwu on 14/12/24.
//  Copyright (c) 2014年 yanyiwu. All rights reserved.
//

#include "Segmentor.h"
#include <iostream>

using namespace cppjieba;

cppjieba::MixSegment * globalSegmentor;

void JiebaInit(const string& dictPath, const string& hmmPath, const string& userDictPath)
{
    if(globalSegmentor == NULL) {
        globalSegmentor = new MixSegment(dictPath, hmmPath, userDictPath);
    }
    cout << __FILE__ << __LINE__ << endl;
}

void JiebaCut(const string& sentence, vector<string>& words)
{
    assert(globalSegmentor);
    globalSegmentor->Cut(sentence, words);
    cout << __FILE__ << __LINE__ << endl;
    cout << words << endl;
}

以及

//
//  Segmentor.h
//  iosjieba
//
//  Created by yanyiwu on 14/12/24.
//  Copyright (c) 2014年 yanyiwu. All rights reserved.
//

#ifndef __iosjieba__Segmentor__
#define __iosjieba__Segmentor__

#include <stdio.h>

#include "cppjieba/MixSegment.hpp"
#include <string>
#include <vector>

extern cppjieba::MixSegment * globalSegmentor;

void JiebaInit(const std::string& dictPath, const std::string& hmmPath, const std::string& userDictPath);

void JiebaCut(const std::string& sentence, std::vector<std::string>& words);

#endif /* defined(__iosjieba__Segmentor__) */

目录如下:

$ tree iosjieba
iosjieba
├── Segmentor.cpp
├── Segmentor.h
├── cppjieba
│   ├── DictTrie.hpp
│   ├── FullSegment.hpp
│   ├── HMMModel.hpp
│   ├── HMMSegment.hpp
│   ├── Jieba.hpp
│   ├── KeywordExtractor.hpp
│   ├── MPSegment.hpp
│   ├── MixSegment.hpp
│   ├── PosTagger.hpp
│   ├── PreFilter.hpp
│   ├── QuerySegment.hpp
│   ├── SegmentBase.hpp
│   ├── SegmentTagged.hpp
│   ├── TextRankExtractor.hpp
│   ├── Trie.hpp
│   ├── Unicode.hpp
│   └── limonp
│       ├── ArgvContext.hpp
│       ├── BlockingQueue.hpp
│       ├── BoundedBlockingQueue.hpp
│       ├── BoundedQueue.hpp
│       ├── Closure.hpp
│       ├── Colors.hpp
│       ├── Condition.hpp
│       ├── Config.hpp
│       ├── FileLock.hpp
│       ├── ForcePublic.hpp
│       ├── LocalVector.hpp
│       ├── Logging.hpp
│       ├── Md5.hpp
│       ├── MutexLock.hpp
│       ├── NonCopyable.hpp
│       ├── StdExtension.hpp
│       ├── StringUtil.hpp
│       ├── Thread.hpp
│       └── ThreadPool.hpp
└── iosjieba.bundle
    └── dict
        ├── hmm_model.utf8
        ├── jieba.dict.small.utf8
        └── user.dict.utf8

接下来开始在项目中集成。首先创建一个空项目 iOSJiebaDemo,将 iosjieba 加入项目中。

单页应用 SwiftJiebaDemo 添加 SwiftJiebaDemo
create-single-view-app swift-jieba-demo-1 swift-jieba-demo-2

添加 iosjieba:

iosjieba-1

见代码: qiwihui/SwiftJiebaDemo@caeb6c2

C++ 到 Objective-C 封装

这个过程是将 C++ 的接口进行 Objective-C 封装,向 Swift 暴露。这个封装只暴露了 objcJiebaInitobjcJiebaCut 两个接口。

//
//  iosjiebaWrapper.h
//  SMSFilters
//
//  Created by Qiwihui on 1/14/19.
//  Copyright © 2019 qiwihui. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface JiebaWrapper : NSObject

- (void) objcJiebaInit: (NSString *) dictPath forPath: (NSString *) hmmPath forDictPath: (NSString *) userDictPath;
- (void) objcJiebaCut: (NSString *) sentence toWords: (NSMutableArray *) words;

@end
//
//  iosjiebaWrapper.mm
//  iOSJiebaTest
//
//  Created by Qiwihui on 1/14/19.
//  Copyright © 2019 Qiwihui. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "iosjiebaWrapper.h"
#include "Segmentor.h"

@implementation JiebaWrapper

- (void) objcJiebaInit: (NSString *) dictPath forPath: (NSString *) hmmPath forDictPath: (NSString *) userDictPath {

    const char *cDictPath = [dictPath UTF8String];
    const char *cHmmPath = [hmmPath UTF8String];
    const char *cUserDictPath = [userDictPath UTF8String];
    
    JiebaInit(cDictPath, cHmmPath, cUserDictPath);
    
}

- (void) objcJiebaCut: (NSString *) sentence toWords: (NSMutableArray *) words {
    
    const char* cSentence = [sentence UTF8String];
    
    std::vector<std::string> wordsList;
    for (int i = 0; i < [words count];i++)
    {
        wordsList.push_back(wordsList[i]);
    }
    JiebaCut(cSentence, wordsList);
    
    [words removeAllObjects];
    std::for_each(wordsList.begin(), wordsList.end(), [&words](std::string str) {
        id nsstr = [NSString stringWithUTF8String:str.c_str()];
        [words addObject:nsstr];
    });
}

@end

见代码: qiwihui/SwiftJiebaDemo@7d196bb

Objective-C 到 Swift

在 Swift 中调用 Objecttive-C 的接口,这个在官方文档和许多博客中都有详细介绍。

  1. 加入 {project_name}-Bridging-Header.h 头文件,即 SwiftJiebaDemo_Bridging_Header_h,引入之前封装的头文件,并在 Targets -> Build Settings -> Objective-C Bridging Header 中设置头文件路径 SwiftJiebaDemo/SwiftJiebaDemo_Bridging_Header_h
//
//  SwiftJiebaDemo-Bridging-Header.h
//  SwiftJiebaDemo
//
//  Created by Qiwihui on 1/15/19.
//  Copyright © 2019 Qiwihui. All rights reserved.
//

#ifndef SwiftJiebaDemo_Bridging_Header_h
#define SwiftJiebaDemo_Bridging_Header_h

#import "iosjiebaWrapper.h"

#endif /* SwiftJiebaDemo_Bridging_Header_h */

bridging-header-2

  1. 将使用到 C++ 的 Objective-C 文件修改为 Objective-C++ 文件,即 将 .m 改为 .mm: iosjiebaWrapper.m 改为 iosjiebaWrapper.mm

见代码:qiwihui/SwiftJiebaDemo@94852b1

使用

使用时需要先初始化 Jiaba分词,然后再进行分词。

class Classifier {

    init() {
        let dictPath = Bundle.main.resourcePath!+"/iosjieba.bundle/dict/jieba.dict.small.utf8"
        let hmmPath = Bundle.main.resourcePath!+"/iosjieba.bundle/dict/hmm_model.utf8"
        let userDictPath = Bundle.main.resourcePath!+"/iosjieba.bundle/dict/user.dict.utf8"

        JiebaWrapper().objcJiebaInit(dictPath, forPath: hmmPath, forDictPath: userDictPath);
    }

    func tokenize(_ message:String) -> [String] {
        print("tokenize...")
        let words = NSMutableArray()
        JiebaWrapper().objcJiebaCut(message, toWords: words)
        return words as! [String]
    }
}

控制台输出结果:

result

可以看到,测试用例 小明硕士毕业于**科学院计算所,后在日本京都大学深造 经过分词后为
〔拼音〕["小明", "硕士", "毕业", "于", "**科学院", "计算所", ",", "后", "在", "日本", "京都大学", "深造"],完成集成。

见代码: qiwihui/SwiftJiebaDemo@bc42e13

遇到的问题

由于自己对于编译链接原理不了解,以及是 iOS 开发初学,因此上面的这个过程中遇到了很多问题,耗时两周才解决,故将遇到的一些问题记录于此,以便日后。

  1. "cassert" file not found

.m 改为 .mm 即可。

  1. compiler not finding <tr1/unordered_map>

设置 C++ Standard LibraryLLVM libc++

llvm

参考: mac c++ compiler not finding <tr1/unordered_map>

  1. warning: include path for stdlibc++ headers not found; pass '-std=libc++' on the command line to use the libc++ standard library instead [-Wstdlibcxx-not-found]

Build Setting -> C++ Standard Library -> libstdc++ 修改为 Build Setting -> C++ Standard Library -> libc++

  1. use of unresolved identifier

这个问题在于向项目中加入文件时,Target Membership 设置不正确导致。需要将对于使用到的 Target 都勾上。

相关参考: Understanding The "Use of Unresolved Identifier" Error In Xcode

参考

One small story by Fan Zhang at GMIC 2014

I attended GMIC 2014 in Beijing at May 5th and May 6th.
GMIC is short for Global Mobile Internet Conference, and it is a really huge conference.
I just want to share a small story I heard in the conference.

0x00

In WeTalk Stage, one of the 8 stages, Fan Zhang, who is the brain behind
Midi Modern Music Festival, gave us a speech: Listen to the Original Where Music is Eternal.
In the speech, he shared us a song named "The Brightest Star in the Sky" of 2013 Shanghai Midi Festival
(watch it here), and told us a story about the boy
who was singing with tears at 2:31 in the video.

<iframe width="560" height="315" src="//www.youtube.com/embed/Z7qgMJCmAHs" frameborder="0" allowfullscreen></iframe>

The boy met his girlfriend when they are student in college,
but they got separated after graduation and went back to their own hometown. It was the first time they saw each other
after 2 years. When catching this song, the boy couldn't stop crying with mixed feeling.

This is what music means that something in your deep heart and express your real feeling, I think.

I would like to go to Midi festival the next time in Beijing, and listen to this song.

0x01

Here is the song
"The Brightest Star in the Sky" in English. Hope you like it.

年度总结 - 2016年,是忙碌而无知的一年

2016年是毕业后觉得过得最快的一年,也是至今觉得过得最快的一年。因为"忙碌"和没有思考,我在这一年过得没有目标,没有计划。

Ingress 和 Minecraft

2016年主要玩了两款游戏:Ingress 和 Minecraft(我的世界)。在Ingress上花费了很多的夜晚时间,在 Minecraft 上花费了一些周末时间。

Ingress

入坑 Ingress 已经四年,今年重新捡起,从7月到11月,我用了五个月的时间从原来的8级升到了16级(游戏等级上线),时间是2016年11月30日23点58分,
总计4,0000,000AP。16级,我的第一个念头是:Never Again,我再也不能16级了,可能再也不会半夜两三点仍然在路上活动,可能再也不会冻手冻脚地
在寒风中画图,可能再也奔波几十公里去连一条 link,可能再也不会月走路330km... 但依旧会和队友一起做刷任务,做多重。每一次升级,都是一次
never again 事情,但是这一次真的就never again 了。
Ingress 的游戏经历回想过来也是像电影一样:入坑,摸索,渐熟,AFK,归来,重拾,融入,疯狂,最后归于平静。也许之后回想起来这一段时间,
也会感叹一句:也曾经疯狂过。

P.S. 同一个号可以转阵营重置而再次16级,但是,你愿意背弃自己的信仰么?

Minecraft

我的世界很自由,所有的东西都可以自己构建,因此世界只限制于想象力。自己维护了一个 Minecraft 的服务器,和朋友共同建设了一个世界。

开源项目贡献

hiwifi-ss

我在8月1号开始维护开源项目:hiwifi-ss, 这是一个基于前人工作做的极路由翻墙插件,
主要完成了在新版本极路由上的界面更新和功能修复。因为工作和 Ingress 的原因,这个项目在10月底就暂时没有继续维护了。

还想做一些其他的项目和实现一些其他的想法,比如一个RSS在线阅读器。

学习

技能

2016年缺乏系统地学习。之前获取和记录知识的过程(书,RSS, 博客等 -> Pocket暂存 -> Evernote记录和归纳 -> 博文输出)并没有很好的实行和保持,
使得代码虽然会写,但是没有系统地去理解为什么,没有去理解怎样更好。拿搬砖来讲,只是回垒墙,还没有上升到造房子或者造更好的房子的程度。

读书

今年没有读太多的书,准确说很少,以至于在最近的一段时间明显感觉到粗口增加,思考迷茫。看的文章很碎,而且没有及时思考,使得这一年没有太多的
**收入。

英语、日语

最近一段时间的英语学习质量也明显下降,多次任务没有认真及时完成,总体感觉能力没有提升。日语学习中断,停滞不前。

生活

生活依旧是生活。

今年喜欢上了星星,很多时间在晚上活动,很多时候熬夜到很晚不肯睡。其实这也没有什么不对。只是如无必要,不要晚睡。

2016年想去很多地方,然而总是错过,广州,深圳,杭州,上海,以及一直以来的衡山,都成为了今年的遗憾。唯一的努力是完成了十月计划而耽搁的日本之行,
第一次出国成就达成。但是计划不足和日语能力也使得这次出行有些仓促,没有达到自己的预期。

感情

逃避问题和冷漠处理问题都是感情的敌人。(来自一个人的反思)

逃避可耻且没有用。然而一个人却可以做很多事情而不需要进行顾虑太多。

社交

有很多的时间花在了刷 Twitter 上,没事了刷,吃完饭刷,中午刷,晚上刷,甚至有时走路都在刷。认识了一些新朋友,但是除了游戏和社交软件
上的对话,没有更多深入的交流了。

总结和计划

总结

计划有余而行动不足,是这一年的总结。很多事情有了开始,但是没有很好地坚持下去

2017年的计划

日拱一卒,功不唐捐。

这是以后的每一年计划的一个宗旨:至少要坚持干完一件事情!每年都想做很多事情,但可能没有时间,也可能没有精力,不期待速成,但求每天都有进步。

详:

  1. 早睡 如无必要,不许晚睡。如果没有非要第二天完成的事情,不要晚睡。睡前完成阅读任务即可。
  2. 早起 早起是一种习惯,坚持。早起之后可以做如下事情:静坐,总结前一天和活动和列举当天的任务,或者早起简单的运动。
  3. 戒咖啡 原因很简单,咖啡影响节律控制和胃。
  4. 每天阅读半小时 阅读指阅读除了技术书籍以外的书籍,每看完一本书都需要思考和总结。
  5. 每周写总结 包括工作总结和非工作总结。
  6. 每周一篇博客 技术博客或者其他内容博客,但是每月技术博客数量应该要多余其他博客数量。总结也好,记录也好,要有输出,才能进步。
  7. 每天拍一张照片 简单,但是坚持,发在 Instagram 上。

简:

  1. 技术成长。
  • 网络基础知识和操作系统知识;
  • 全栈(开发,运维,产品等)知识构建;
  • 语言:动态语言,函数语言和强类型语言;
  • 维护开源项目;
  1. 知识记录,归纳和总结:(书,RSS, 博客等 -> Pocket暂存 -> Evernote记录和归纳 -> 博文输出)
  2. 坚持锻炼身体,合理饮食。
  3. 学好日语。这是今年要坚持完成的一件事
  4. 英语不落下。
  5. 完成一次旅行,登山。
  6. 学会独处。

Git 小结

整理自手把手教你用git.

  1. git reflog 查看历史记录的版本号id
  2. Discard:
    • git reset --hard HEAD^
    • git reset --hard HEAD~100
    • git reset --hard <one commit>
  3. git checkout -- <file>
    • 修改后,还没有放到暂存区,使用 撤销修改就回到和版本库一模一样的状态。
    • 另外一种是第一次修改已经放入暂存区了,接着又作了修改,撤销修改就回到添加暂存区后的状态。
  4. 暂存区 -> 工作区
    • git reset HEAD <file>
    • HEAD严格来说不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是当前分支。
  5. push
    • git remote add origin https://github.com/username/project_name.git 关联一个远程库
    • git push –u origin master (第一次要用-u, 以后不需要)
  6. 分支管理策略:
    • 通常合并分支时,git一般使用”Fast forward”模式,在这种模式下,删除分支后,会丢掉分支信息。可以使用带参数 –no-ff来禁用”Fast forward”模式。git merge --no-ff -m "comments" <branch_name>
    • 分支策略:首先master主分支应该是非常稳定的,也就是用来发布新版本,一般情况下不允许在上面干活,干活一般情况下在新建的dev分支上干活,干完后,比如上要发布,或者说dev分支代码稳定后可以合并到主分支master上来。
  7. git stash: 可以把当前工作现场 ”隐藏起来”,等以后恢复现场后继续工作。
    • git stash list: 查看
    • 恢复:
      1. git stash apply 恢复,恢复后,stash内容并不删除,你需要使用命令git stash drop来删除。
      2. 另一种方式是使用git stash pop,恢复的同时把stash内容也删除了。
  8. 多人协作:
    • 推送分支:
      1. master分支是主分支,因此要时刻与远程同步。
      2. 一些修复bug分支不需要推送到远程去,可以先合并到主分支上,然后把主分支master推送到远程去。
    • 抓取分支:
      1. push非master分支(e.g. dev):git checkout –b dev origin/dev, edit something, git push origin dev
      2. 另一个同伴更新:git branch --set-upstream dev origin/dev, git pull, edit something, git push origin dev
    • 协作模式:
      1. 首先,可以试图用git push origin <branch-name>推送自己的修改.
      2. 如果推送失败,则因为远程分支比你的本地更新早,需要先用git pull试图合并。
      3. 如果合并有冲突,则需要解决冲突,并在本地提交。再用git push origin <branch-name>推送。
  9. delete remote branch
    • git push origin —delete <branch_name>
  10. get remote branch locally
    • git branch --set-upstream dev origin/dev

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.