qiwihui / blog Goto Github PK
View Code? Open in Web Editor NEW技术和思考,基于issues
Home Page: https://qiwihui.com
技术和思考,基于issues
Home Page: https://qiwihui.com
平台:
比较方向:
其他:
Weka, knime.org, 阿里云机器学习PAI 等等
数据分析傻瓜话?
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.
Here is one picture of it.
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
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:
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.
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自动编译
大纲:
简单地写一下在VirtualBox上安装Arch Linux的过程,以此为在PC上安装做准备。在PC上安装的过程和下面描述的基本一致。
##0x00 准备
下载iso文件:在Arch官网上下载最新的镜像,这里
我选用了163.com节点的资源,下载archlinux-2015.01.01-dual.iso;
检查文件的完整性:在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 创建虚拟机
##0x01 开始安装
更改键盘布局和设置语言:
默认键盘布局为us
,非us布局可以用如下命令修改:
# loadkeys layout
layout可以是uk
, dvorak
等。设置语言:
磁盘分区
先看一下磁盘状态:
# 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'并回车即可保存修改。
格式化分区
再次回到命令行:
root@archiso ~ #
格式化分区:
# mkfs -t ext4 /dev/sda1
# mkfs -t ext4 /dev/sda3
# mkfs -t ext4 /dev/sda4
# mkswap /dev/sda2
挂载新分区
# swapon /dev/sda2
# mount /dev/sda3 /mnt
# cd /mnt
# mkdir boot home
# mount /dev/sda1 boot
# mount /dev/sda4 home
安装Arch
# cd /
# pacstrap /mnt base base-devel
生成fstab
文件
# genfstab -p /mnt >> /mnt/etc/fstab
可以看看fstab里面的内容:
# more /mnt/etc/fstab
初始化安装Boot Loader
# pacstrap /mnt syslinux
配置安装
运行以下命令:
# 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
完成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
完成安装
最后,更改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
重启Arch
# reboot
重启之后会再次进入CD启动,这时,去除安装CD,再次重启:
Devices > CD/DVD Devices > Remove disk from virtual drive
等待一小会:
Congradulations!
后续工作
链接网络:
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就不装了。
这篇文章的目的是为了配置树莓派,使其在启动时自动获取静态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]
输入"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
/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
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
, 其他可选为 LEAP
和 SHARED
重启树莓派,之后就会自动连上wifi了.
许多初次接触Python的人对于索引都会有同样的反应:这太奇怪了。在Python的列表,字符串和条件语句中都充斥着索引,但在我们习惯他们之前,
这些都会是我们程序的错误来源。因此,让我们硬着头皮上吧!
这片文章会写得很慢,为了解释一些基础和默认的东西。
我们会使用字符串切片举例,因为这事我们首先接触的,不过这对于列表切片和设定范围是一样的。我们有:
a = '0123456789'
其中第k个位置的字符为k。
我们使用如下方式对a进行切片:
b = a[start:stop:step]
或者直接地:
'0123456789'[start:stop:step]
在Python中,字符串和指向字符串的变量都是对象,所以都可以进行切片(事实上,Python中所有东西都是对象:数字,变量,字符串,函数,文件)。
有三件事情需要记住:
start
是我们想要的第一项(当然)stop
是我们第一个不想要的项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!
原文在这儿
大纲:
参考:
2019年一月已经过半,本该在年底十二月完成的总结又到现在才开始着笔。2018年发生了很多事,从年初比特币大涨至最高到现在互联网寒冬已至,不断变化的是环境,不变的是每年一次的年终总结。(误)
做为一个程序员,在保持自己技术水平同时,应当不断地学习,总结和思考新的技术,才能在这个行业不至于被淘汰。互联网寒冬来临之时,对于还不能掌握形式的我也只能先增强自身能力以期减少这个寒冬带来影响。
从2017年年底开始涉足机器学习深度学习,先是学习然后在工作项目中实践,至今一年有余。
机器学习和深度学习从吴恩达的《机器学习》和《深度学习》课程开始,吴恩达的课程简洁易懂,逻辑清楚,虽然都是英文,但也没有太大关系。不过这两个课程注重算法胡实现,缺少项目,因此可以需要 fast.ai 的机器学习和深度学习课程来巩固掌握。主要使用的框架是 Scikit-learn
,Keras
和 Tensorflow
,这三个掌握不足。
之后在公司安全项目中使用了一些分析和算法,对于机器学习和深度学习的理解和应用也只能说是皮毛。
年初(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 的体验轻便,方便梳理,是迄今用过的最舒服的项目管理工具。
Fork并修改了一个 Chrome 扩展:Octo Previewer,用来实时预览 Github 上的 PRs,Issues,Gists 的 Markdown 评论。
Trello 卡片 上记录这个过程。理论上是可行的,只是在有些工具上卡住了前进的路线。
这个基于极路由的翻墙项目没有持续维护,一则自己对于 lua 和 前端不熟悉,开发起来困难,二则我自己的极路由在搬家之后就没有了,再者极路由似乎大势已去,所以这个项目基本也就三四个月更新一次。这个项目给我带来的最大感触就是维护开源项目真的不容易。前些天看到 kalasoo 的文章《开源即责任》也是有感触。
2018年上半年忙与机器学习和项目,几乎没有读什么书,下半年(9月)开始入坑科幻小说。《三体》是一部好的科幻小说,但在国内《三体》造就的伪科幻迷群体以及随之而来的各种各种视为真理的概念着实令人难受,于是入坑科幻洗洗脑。科幻类基本都是名篇,看过之后,对于《三体》所带来的震撼减轻了不少,但是我更加佩服大刘了。
关于素数的两本书,主要是因为菲尔兹奖与阿贝尔奖双料得主迈克尔·阿蒂亚爵士讲述他对黎曼猜想的证明,这次事件间引起了我详细了解黎曼猜想的兴趣,为此还专门购买了经典教材《复变函数论方法》,期待进一步了解。这两本书适合一起看,互相补充。不幸的是,迈克尔·阿蒂亚爵士,于 2019 年 1 月 11 日上午逝世,享年八十九岁。他最后的尝试,令人佩服!
「我一直在尝试理解事物运行的原因。我对不能理解背后原理的公式不感兴趣。我总是试图挖掘事物背后的原理,所以如果我有一个公式,我就会去理解它为什么是这样。理解是一个非常困难的概念。人们认为数学的开始是你写下一个定理并附带证明。这不是开始,这是结束。对我来说,数学的创造性在你动手在纸上写字之前,在你尝试写公式之前。你描绘不同的事物,在脑海中反复思考。你尝试的创造,就像音乐家试图创作音乐,或诗人写诗一样。这个过程没有可以遵循的规律,你必须找到自己的方法。但到了最后,就像作曲家必须写下乐谱一样,你必须把它写下来。但最重要的一步是理解。证明公式本身可能不能让你理解。你可能有一个很长的证明,但到最后却不知道它为何是这样。但为了理解,你必须找到类似于直觉的能力,你必须感受它。」
——迈克尔·阿蒂亚爵士
今年看的非技术书基本都是在上下班坐着公交看的,积少成多,也就多了。今年的体会就是读书如抽丝。多读书涨见识。
平常看电影电视有时候喜欢写着代码,可能也不太记得太多,讲讲喜欢的吧。
年初买了 Nitendo Switch,不过没有太多时间花在玩游戏上,以至于《塞尔达传说:荒野之息》和《超级马里奥:奥德赛》都没有通关,只是偶尔用来玩玩 AoV,不过 NS 的体验确实非常不错,不论是个人还是联机,值得推荐。个人觉得游戏在于娱乐放松,若影响正常工作和情绪则视为不可,曾记得大学玩游戏还冲别人发过脾气也是太年轻。
我在王者荣耀S13赛季又重新玩了一段时间,每天三五局,升个一星两星就停止,掉个一星两星也停止,就这样达到了个人历史最好成绩,不过这个过程给我带来的影响也不少。一个是我发现在白天的队友比较坑,而到了夜晚会好很多,估计是小学生都去睡觉了,所以我每到十一二点就开始玩,有时会因为连胜而玩到很晚,以致停止一段时间之后仍不能好好早睡,严重影响精神状态;二是一局结束又开一局,犹如赌博,赢则更想赢,输则不服气,往往计较于一城得失,实在是影响心气。这个游戏不能投入太多时间。
很不错的游戏,只是都还没有玩通关,需要补上。
就和养儿子一样(虽然作者说的是丈夫),总是担心种种,吃没吃好,有没有被雨淋,有没有被欺负,路上还有钱吗。这就是为人父母的体验吧。
接触不多,不过和《王者荣耀》这种是一样的感受。看过一些游戏主播的视频,佩服一个LOL职业玩家转吃鸡的主播,在战场上沉稳,有判断。
已经弃坑了,一则是因为满级了,并没有刷成就的习惯,同时也因为没有太多时间出门,二则官方一直没有太多的积极活动,每次就是换一个地方刷牌子,再则在手机升级到 iOS 11 之后,官方推出了 Ingress Prime,游戏体验万分糟糕,之前的版本却不升级,遂弃。Ingress玩了四五年,也疯狂过,但是还是离开吧。
今年一大事就是和老婆领证了,还没有办酒席,两人都商量着简办,请亲戚吃个饭就感觉已经很隆重了。参加了几次同学的婚礼,无非接亲,闹新郎,宣个誓言,在众亲友面前挥泪感恩,对于这样重复的婚礼,也没有太大的兴趣。梦想人生的婚礼应当刺激,可以在远山,或者,招待一群好友,准备一堆食物,准备一段给大家的表演,大家也可以上台表演,发表自己的感想。
11月,迎来家庭另一个成员,一只英国短毛蓝猫,取名“狗狗”。虽然有时会觉得这和取狗蛋差不多,也不知道会不会让其他猫看不起。第一次养猫,总是担心这担心那,生怕他生病了,像极了父母。久了我也发现他还是很粘人的,不知道年后给他找了女朋友之后会不会还是这么粘人。
十月,堂弟找我借钱周转,并承诺四天还,鉴于有承诺而且是亲戚,也就爽快借了。第一次到期没任何动静,还钱还是再接着一句话也没说,我问了才换来一句“明天,明天一定换”,接着第二次就是“最迟不会超过后天下午”,然后是到期还了一半,我没收,要全款,就继续拖着,一星期没动静,问了几句就变成在外地学习没开手机,“明天下午三点之前”,然后“再给一个小时”,“我手机没电了”。最后钱是还了,但是我对他的信任一点都没有了,直接拉黑!
这件事让伙我认识到一点:在钱面前,亲戚的嘴脸也是令人恶心的。我借钱是因为我信任,却不代表你可以践踏。想起一个村民中奖却被村里人借得倾家荡产,村里人却觉得他有钱为什么就不能借点,可怕。
绝不是flag。
人生需要有目标,需要一个积极乐观的心态,和一群志同道合的同伴。
大纲
在 OS X 上使用 sed
会和 GNU 上不太一致,在此记录。
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做为我博客的版本控制,使用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
,这个会在更新之前被调用。
环境要求:
我们的实践过程会按照下边的过程实施:
+------------------------+ +------------------------+
| | | |
| +-----------------+ | push | +-------------------+ |
| |local repository |---+----------+->| remote repository | |
| +-----------------+ | | +-------------------+ |
| | | | |
+------------------------+ | |pull |
| V |
local machine | +-------------------+ |
| | deployment | |
| +-------------------+ |
| |
+------------------------+
server
$ cd ~
$ mkdir remoteRepo
$ cd remoteRepo
$ git init --bare webapp.git
$ cd ~
$ mkdir deployment
$ cd deployment
$ git clone ~/remoteRepo/webapp.git webapp
$ 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
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上时,这些代码就会被自动更新。
可以在最初在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
有一种情况是当本地更新了webapp,结果push到远程仓库后这个更新被reset了(虽然我觉得这个问题应该避免,
但是还是有可能发生),这是,简单地在hook中使用git push deploy master
是无法完成这个过程的,因为
远端的代码版本低于deploy端的代码版本,再使用pull的时候就不能实现同步,这时就应该使用另一种方式
更新代码:
git fetch --all
git reset --hard origin/master
即git reset
把HEAD 指向了新下载的未合并的节点,也就是在local machine上reset之后的。
免开发者权限,不弃保
大纲:
注册 -> 申请 -> 安装插件 -> 开通ssh权限
缺点:弃保修
翻墙折腾无止境.
使用路由器翻墙的一个好处在于,对于一些翻墙配置很繁琐的设备,只需要简单地连上路由wifi就可以实现翻墙,
来家里的朋友也可以不需要配置就可以一连翻墙.
在旧版本的极路由已经有很不错的翻墙设置方式,感谢前人的大树:三流火的shadwosocks插件.在旧版本的极路由上
设置shadowsocks翻墙可已参考极路由Shadowsocks家庭无痛翻墙实践.
最近极路由更新了新版本,管理界面风格大变导致之前的插件不能使用,在网上找了一段时间也没有看到有人对之前的
插件进行更新,所以决定自己写一个(其实后来才知道stary.love也有可用的插件,比我修改的插件功能强大很多).
所谓的自己写也只是在前人的基础上修改为适应新的极路由后台管理假面. 这过程要感谢stary.love的帮助,为我提供了
早期插件的一些源代码,以及许多帮着测试使用的人.
项目地址: qiwihui/hiwifi-ss, 现在插件的状态:
(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账号就可以了.
未来要做的一些工作:
怎么说呢, 我在最开始的时候, 寻找免费的vpn是获得翻墙的唯一方式, 这种方式的不好之处在于: vpn不稳定, 经常换,
而且花费在寻找上的精力和时间算下来不合算. 之后精力了地下铁路vpn
的消失之后, 自己搭建翻墙才成为我的主要翻墙
方式. 一个月花费的费用不到10美元, 带来的时稳定的流量和方式. VPS+shadowsocks/v2ray就可以提供稳定持久的方式.
不怎么使用vpn(免费或者收费)以及一些其他的收费翻墙服务,一则担心不安全, 流量劫持或者流量分析都有可能,甚者蜜罐,
二则是重点观察对象, 服务失效的可能性还是存在的. 因此, 加密翻墙流量和混淆翻墙行为时十分重要的过程.
因为GFW, 墙内封闭的环境使得获取技术知识的广度和及时性都受到了很严重的影响, 翻墙让搞技术的我们与世界更接近.
分享 @lepture的一个tweet:
「我的互联网,上谷歌维基搜知识,上Reddit看看头条,上YouTube学习和开眼界,上Twitter关注一些正在改变世界的人和事,
去Quora上看看好的问题和回答,去SlideShare上学习以及了解不同的想法和观点」
翻墙在于不断折腾.
在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开发者社区是怎样收到一本趋势读者也这样做的书。
原文在这儿。
CoaoaPods
是一套整体解决方案,我们在 Podfile
中指定好我们需要的第三方库。然后 CocoaPods
就会进行下载,集成,然后修改或者创建我们项目的 workspace
文件,这一系列整体操作。
相比之下,Carthage
就要轻量很多,它也会一个叫做 Cartfile
描述文件,但 Carthage
不会对我们的项目结构进行任何修改,更不多创建 workspace
。它只是根据我们描述文件中配置的第三方库,将他们下载到本地,然后使用 xcodebuild
构建成 framework
文件。然后由我们自己将这些库集成到项目中。Carthage
使用的是一种非侵入性的哲学。
另外 Carthage
除了非侵入性,它还是去中心化的,它的包管理不像 CocoaPods
那样,有一个中心服务器(cocoapods.org),来管理各个包的元信息,而是依赖于每个第三方库自己的源地址,比如 Github。
(可选)使用 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/
sudo gem install cocoapods
(可选)切换 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
如果Podfile文件中有
source 'https://github.com/CocoaPods/Specs.git'
也需要把它换成repo的源,否则依然是使用GitHub源
cd <project_folder>
pod init
编辑 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
pod install
Always 打开项目下 *.xcworkspace 文件作为项目入口
pod install [package_name]
: 安装特定版本的 podspod update [package_name]
: 升级 pods 到最新版本brew install carthage
编辑 Cartfile
,比如 SwiftyJSON
github "SwiftyJSON/SwiftyJSON"
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
Carthage
目录下:
$ 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
添加生成的文件: 项目 "General" -> "Linked Frameworks and Libraries" -> 将 Carthage/Build/iOS
中的 .framework
文件添加到项目中
"Build Phases" -> "+" -> "New Run Script Phase"
添加这个 Run Script 的作用是为了让运行时能够找到这个动态库。
还可以将 Carthage 所集成的第三方库生成的符号文件添加到项目中,这样我们在调试的时候,就可以步入第三方库内部的代码:Build Phrases
-> New Copy Files Phrase
,将 Carthage/Build/iOS 目录中的 SwiftyJSON.framework.dSYM
符号文件拖动进来
大纲:
spark app 结构
运行参数及优化
spark读取数据和输出
基于机器学习的垃圾短信过滤
Scikit-Learn 短信过滤模型训练,
iOS CoreML介绍
使用 coremltools 将 Scikit-Learn 的模型转为 CoreML 的 mlmodel
iOS App功能实现,界面编写
调试
App发布
优化模型大小,App大小及性能
一个内核是运行和解析用户代码的程序。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_scheme
和 key
用来加密信息,因此系统的其他用户不能发送代码来运行内核。
现在我需要自己定义一个内核,这个内核可以执行我们定义的逻辑。
这是简单的重用 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,并运行代码。
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
一直在想把微信的公众号的文章导出为RSS阅读,方便阅读和减少对微信的依赖,后来看到
zhu327/rss 这个项目,这是一个用来生成微博,微信公众号,知乎日报 RSS 的Web APP。
但是这个项目的demo部署在Red Hat的openshift上,
囿于对这个cloud的操作不是很熟,所以想着把这个项目重新部署到自己在DigitalOcean的机器上,就fork了这个项目开始啦!
以下涉及到的内容有:
因为之前并没有在我的服务器上创建过其他用户,如果直接用root用户的话不好,所以需要专门的一个账户来负责部署。
登陆服务器:ssh root@<server-ip>
创建一个用户deploy
: sudo adduser deploy
将用户加入sudoers中: sudo usermod -a -G sudo deploy
添加远程连接的权限,这样就省去了输入密码了:
sudo su - deploy
mkdir .ssh
chmod 700 .ssh
touch .ssh/authorized_keys
chmod 600 .ssh/authorized_keys
其中,700
表示只有文件拥有者才能读,写以及打开文件,600
表示只能读和写。
接着将自己的公钥加入authorized_keys
文件中,这个公钥在自己本机~.ssh/id_rsa.pub
中。没有的话可以用
ssh-keygen -t rsa -C "[email protected]"
来生成。
自动部署的好处就是省去了每次都要上服务器。可以参见之前的一篇博客
使用 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_IP
和OPENSHIFT_DIY_PORT
修改为对应的localhost
和8000
端口
将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
是Linux中非常好用的进程管理工具,我们将使用它和Nginx一起来组成我们的服务的部署。
安装supervisor:pip install supervisor
或者 sudo apt-get install supervisor
创建一个目录来装supervisor的配置文件:mkdir -p ~/local/etc/supervisord
创建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
其中我们都适用用户目录下创建的local
,logs
和tmp
文件夹来装这些文件。
创建一个rss.ini的文件用来作为rss服务:touch ~/local/etc/supervisord/rss.ini
,放入如下内容:
[program:rss]
command=python2.7 /home/deploy/deployment/rss/diy/start.py
其中,start.py
是这个tornado项目的入口。
启动服务:supervisord -c /home/deploy/local/etc/supervisord.conf
,因为用的是非默认的配置文件,这里
指定相应的配置文件位置。
一旦我们在之后修改了项目push了之后,我们需要重新启动rss:supervisorctl restart rss
,因此,为了方便,
可以将这条命令加入项目git hooks中的post-receive
文件末尾。
Nginx很好很强大,我们用它来做为我们的HTTP服务器。
安装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"
添加路径到PATH中:
export PATH=/home/you/local/sbin:$PATH
source ~/.bashrc
创建配置文件:~/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的端口。
之后使用/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
运行服务:/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”,这是我在我的Twitter
上的签名,也是我在日常生活和工作中一个做事的基本准则。当自己得到一个消息,或者开始一个
新的任务的时候,不是下意识地就相信这个消息,开始这个任务,而是要经过这三步过程之后,
得出自己的结论,才开始行动。
探索,或者说是寻求,就是当你得到这个消息的时候,不是盲目地相信,而是先开始收集和甄别资料。
在这个信息化一直信息爆炸的时代,越来越多的误导和虚假消息充斥在我们的身边,新闻电视,媒体资讯,
社交网络,信息方便的同时也可能是误导和虚假消息滋生的温床。信息本无对错,只是当它们被少数人
利用,曲解的时候,信息的对错才开始变得有利可图。
思考,结合自己的知识背景和收集到的信息,思考信息的对错,以及其中个所包含的其他有价值的信息,
这是一步非常重要的过程。善恶只在一念之间,这一念就是你的思考,你的想法。思考,可以是道理更深刻,
事物更透彻,思考的好处不言而喻,古今之集大成者,莫不是善于思考的人。
发言,又或者可以是行动(Move),就是在思考之后表达自己的见解,采取一定的行动支持这个见解,
以达到说服自己,说服别人,是别人达成对你的共识,就如同演讲家和行动派表现出来的一样。
我更喜欢诸如Twitter一类的自由开放社交工具,一个重要的原因是消息的对冲。QQ,微信之类的熟人社交,
消息相对封闭,而且熟人会弱化我们对消息的思考,又如微博,却因别有用心的控制儿失去了本来的对冲
能力。但这种情况发生的时候, “Seek,think then speak” 就变更加重要了。比如在MH370消失的三四后,
微博上开始有人谣传MH370安全返航,微博上很快澄清,儿微信圈和QQ圈就无法及时跟上,seek在这个过程
中让我没有轻易相信这这不实的信息。
Speak, 在这个容易因为言获罪的时期,发言和行动变得愈加重要。沉默是金,那是因为真理,对于非正义,
speak才是真理。
在2014年还有15天就结束的时候,总结一下自己在2014年的工作生活和学习。2014年的故事比2013年少,但是琐碎的学习项目和整理多了很多。
** 学习环境 **
** Linux **
** Vim + Git **
** C **
** Python **
** Web **
** Mac/iOS **
** 读书 **
** 设备 **
** 生活 **
以上就像列家常一样把2014年能记得的东西都写了个遍,那么问题来了,挖掘机...不:
因此2015年在这写方面确实要改善和加强。故制定2015年的主要目标如下(比较宽的目标,无先后,要细分):
规划:
学习:
杂项
2015年想来事情也是比较多的,加油!
Wait, wait! 虽然2015年还有15天到来,但是可以做的事情还是很多,好好想想,and期待惊喜的发生!
P.S. 明年总结的时候这个也会是比较二的一篇,除非我没有进步!
2013年的总结:年度总结 - 过去的2013年。突然觉得这个好矫情啊!!
原文:How to read Apple’s developer documentation
对于很多人来说,这篇文章听起来很奇怪,因为我们已经习惯了 Apple 的 API 文档的工作方式,因此我们精神上已经经过调整以快速找到我们想要的东西。
但这是一个有趣的事实:去年我最热门的文章请求之一是帮助人们真正阅读 Apple 的代码文档。您如何找到您需要的 iOS API,如何浏览所有材料以找到您真正想要的内容,以及您如何深入了解为什么事情按照他们的方式工作?
所以,如果你曾经寻求帮助来理解 Apple 的开发者文档,首先我要让你知道你并不孤单 - 许多人都在努力解决这个问题。但其次,我希望这篇文章会有所帮助:我会尽力解释它的结构,它有什么好处(以及不好的地方),以及如何使用它。
更重要的是,我将向您展示经验丰富的开发人员寻找额外信息的位置,这些信息通常比Apple的在线文档更有价值。
任何书面的 API 文档通常采用以下五种形式之一:
粗略地说,苹果公司第一点做了很多,其次是第二点和第三点,第四点很少,第五点几乎没有。
所以,如果你正在寻找“如何用 Y 做 X ”的具体例子,你最好从我的 Swift 知识库开始 - 这正是它的用途。
了解 Apple 的文档解决的问题,可以帮助您从中获得最大收益。它并不是一个结构化的教程,它不会向您介绍一系列概念来帮助您实现目标,而是作为 Apple 支持的数千个 API 的参考指南。
Apple的在线文档位于 https://developer.apple.com/documentation/ ,虽然您能在 Xcode 中使用本地副本,但我会说大多数人使用在线版本只是因为他们可以更容易地找到内容。
绝大多数 Apple 的文档都描述了接口,而这正是大多数时候你会看到的。我想使用一个实际的例子,所以请先在您的网络浏览器中打开https://developer.apple.com/documentation/ ,这是所有Apple开发者文档的主页。
您会看到所有 Apple 的 API 分为 App Frameworks
或 Graphics and Games
等类别,您已经看到了一件重要的事情:所有深蓝色文本都是可点击的,并会带您进入特定框架的API文档。是的,它使用相同的字体和大小,没有下划线,说实话,深蓝色链接和黑色文本之间没有太大区别,但你仍然需要留意这些链接 - 有很多他们,你会用它们来深入挖掘主题。
现在请从 App Frameworks
类别中选择 UIKit
,您将看到它的功能(为iOS创建用户界面)的简要概述,标有“重要
”(Important
)的大黄色框,然后是类别列表。那些黄色的盒子确实值得关注:虽然它们经常被使用,它们几乎总能阻止你犯下根本错误,这些错误导致出现奇怪的问题。
此页面上重要的是共同描述 UIKit
的类别列表。这是人们经常迷路的地方:他们想要了解像 UIImage
这样的东西,所以他们必须仔细查看该列表以找到它可能出现的合适位置。
在这种情况下,您可能会查看“资源管理”(Resource Management
),因为它的副标题“管理存储在主可执行文件之外的图像,字符串,故事板和 nib 文件”听起来很有希望。但是,您会感到失望 - 您需要向下滚动到 “图像和 PDF”(Images and PDF
)部分才能找到 UIImage
。
这就是为什么我谈过的大多数人只是使用自己喜欢的搜索引擎。他们输入他们关心的类,并且 - 只要它有“UI”,“SK”或类似的前缀 - 它可能是第一个结果。
不要误会我的意思:我知道这种做法并不理想。但是面对搜索一个类或者去 https://developer.apple.com/documentation/ ,选择一个框架,选择一个类别,然后选择一个类,第一个就是更快。
重要提示:无论您选择哪种方法,最终都会在同一个地方,所以只做最适合您的方法。现在,请找到并选择 UIImage
。
一旦选择了您关心的类,该页面就有四个主要组件:概述,版本摘要,接口和关系。
概述是“API的文本描述,描述了它应该做什么以及一般指导用例”,我之前提到过 - 我要求你选择 UIImage
,因为它是文本描述何时运行良好的一个很好的例子。
当它是我第一次使用的类时,特别是如果它刚刚推出时,我通常会阅读概述文本。但是对于其他一切 - 我之前至少使用过一次的任何类 - 我跳过它并尝试找到我所得到的具体细节。请记住,Apple 文档确实不是一种学习工具:当您考虑到特定目的时,它最有效。
如果您不总是为所选 Apple 平台的最新版本开发,则版本摘要 - 页面右侧的侧栏 - 非常重要。在这种情况下,您将看到 iOS 2.0 +
,tvOS 9.0+
和 watchOS 2.0+
,它告诉我们何时 UIImage
类首次在这三个操作系统上可用,并且它仍然可用 - 如果它已被弃用(不再可用)你会看到像 iOS 2.0-9.0
这样的东西。
此页面上的实际内容 - 以及作为 Apple 框架中特定类的主页的所有页面 - 都列在“主题”标题下。这将列出该类支持的所有属性和方法,再次分解为使用类别:“获取图像数据”,“获取图像大小和比例”等。
如果您选择的类具有任何自定义初始化方法,则始终会首先显示它们。 UIImage
有很多自定义初始化方法,你会看到它们都被列为签名 - 只是描述它所期望的参数的部分。所以,你会看到这样的代码:
init?(named: String)
init(imageLiteralResourceName: String)
**提示:**如果您看到 Objective-C
代码,请确保将语言更改为 Swift
。您可以在页面的右上角执行此操作,也可以在重要的 iOS 测试版引入更改时启用 API 更改选项。
记住,初始化方法写成 init?
而不是 init
的是容易出错的 - 它们返回一个可选项,以便在初始化失败时它们可以返回 nil
。
在初始化器的正下方,您有时会看到一些用于创建类的高度专业化实例的方法。这些不是 Swift 意义上的初始化器,但它们确实创建了类的实例。对于 UIImage
,你会看到这样的事情:
class func animatedImageNamed(String, duration: TimeInterval) -> UIImage?
class func
部分意味着你将使用 UIImage.animatedImageNamed()
方式调用。
在初始化程序之后,事情变得有点不那么有条理:你会发现属性方法和枚举自由混合在一起。虽然您可以滚动查找您要查找的内容,但我可以认为大多数人只需要 Cmd + F
在页面上查找文字就可以了!
有三点需要注意:
UIImage
包含嵌套的枚举 ResizingMode
。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
类别中找到它。
这不是一个复杂的方法,但它确实展示了这些页面的重要部分:
class func animatedResizableImageNamed
- 然后是方法页面标题中显示的形式(animatedResizableImageNamed(_:capInsets:resizingMode:duration:)
),以及方法页面的声明部分中的形式。iOS 6.0
中引入。因此,虽然主要的 UIImage
类从第1天开始就已存在,但这种方法是在几年后推出的。UIImage.ResizingMode
,你将去哪里取决于你是否点击了“UIImage”或“ResizingMode”。 (提示:您通常需要单击右侧的那个。)Discussion
)部分详细介绍了此方法的具体使用说明。这几乎总是 - 每个页面中最有用的部分,因为在这里您可以看到“不要调用此方法”或“小心......”See Also
部分,但这有点受欢迎 - 这里只是我们在上一页的方法列表。现在,UIImage
是一个老类,并没有太大变化,因此它的文档处于良好状态。但是一些较新的 API - 以及许多没有像 UIKit
那样被喜欢的旧 API - 仍然记录不足。例如,来自 SceneKit
的 SCNAnimation
或来自 UIKit
的 UITextDragPreviewRenderer
:两者都是在 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。
这篇博客将整理在配置博客以及项目 Pages 的自定义域名过程,遇到的问题以及解决方法。Github 的文档对于如何配置自定义域名有详细的介绍,这里不会全部翻译,只重点记录实践的过程,内容涉及为用户网站,公司网站,以及项目网站添加 Apex
域名(qiwihui.com),二级域名(www.qiwihui.com)以及开启 HTTPS。最后,所有指向 www.qiwihui.com
的请求将会被重定向至 https://qiwihui.com
。
支持的自定义域名类型 | 域名例子 |
---|---|
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 的默认域名和主机地址 | 页面被如何重定向 | 自定义域名举例 |
---|---|---|---|
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 |
在项目 Settings
中,找到 GitHub Pages
这一区域,选择 Source
为对应的要部署的分支,这里我选择 gh-pages branch
:
其中,选择 master branch
会视 /README.md
为 web 的 index.html
,选择 master branch /docs folder
会视 /docs/README.md
为 web 的 index.html
。
在 Custom domain
中添加自己的域名并保存:
或者,在项目分支中添加 CNAME
文件,CNAME
文件的内容为
qiwihui.com
这里推荐第二种,尤其对于有设置 CI 的项目,因为 CI 上将第一种设置覆盖。
这一步是比较重要却又容易忽视的一步:
qiwihui.com
,那么 www.qiwihui.com
会被重定向到 qiwihui.com
;www.qiwihui.com
,那么 qiwihui.com
会被重定向到 www.qiwihui.com
;这里我选择重定向到 www.qiwihui.com
,所以设置为 qiwihui.com
为了能设置Apex
域名,需要在 DNS 中配置 A 记录指向 github 的 IP:
185.199.108.153
185.199.109.153
185.199.110.153
185.199.111.153
同时,设置 CNAME
记录将 www.qiwihui.com
指向 qiwihui.github.io
,即 <你的 github 用户名>.github.io
。对于公司来说,这个地址是 <公司名称>.github.io
。
以下是设置好之后的 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
勾选 Enforce HTTPS
Github 会自动保持 HTTPS 证书的有效。
当给项目设置 Pages 时,一般都已经有一个个人或者公司的 Pages 了,如果没有,就可以按以上的过程添加。如果已经设置了,则只需要很简单的两步即可:
以下以个人项目 [qiwihui/fullstackpython.com](https://github.com/qiwihui/fullstackpython.com)
,设置地址为 fullstackpython.qiwihui.com
CNAME
文件指向 fullstackpython.qiwihui.com
:fullstackpython.qiwihui.com
指向 qiwihui.github.io
,即 <你的 github 用户名>.github.io
。对于公司来说,这个地址是 <公司名称>.github.io
。一段时间后即可。
在停止博客的一个多月时间里,我除了上班的五天白天,再加上周末出去爬山的两天,
剩下的时间很多都花在了我那只智能手机上,微信,QQ,G+,以及一些有节操和没节操
的应用和游戏,这一个多月的时间就这样荒芜的度过了。
直到我暂停出行的计划,更多
地和朋友接触,我才觉得,智能手机在给我带来很大方便的同时,也使得我的生活变得
狭窄,交际变得狭隘。于是我决定:离开智能手机一到两个月,就像去年手机坏掉一样。
我把手机交给了朋友保管,在微信上和QQ上留下了电话和邮箱,开始了我一个月的非智能
生活。第一天是很艰难的,尤其是在突然离开手机之后,我获取信息的方式直接转移到了
电脑,这就意味着我要很多时候开着电脑,背单词,阅读文章,收邮件等等都从手机向电
脑迁移,无可选择。
第一天计划的实施还是有点水分的,总是不自觉地拿起那台很古老的手机,打开又关闭,
才能继续回来学习工作,然后在iPad上上了一会微信,然后删了微信,整理了所有的不
需要的会上瘾的软件,这才算是正式开始了。
理了头发表决心。
在接下来的一两个月的时间里,计划是这样的:
尽最大的诚意,但不知道何时会消失,如果可以,就不要再试一次。
微信跳一跳小程序火了,来看一看玩耍的正确姿势。
距离判断 + 按压模拟
距离
手动判断点击/像素点判断,自动点击
OpenCV 图像分析
按压时间计算
综合
头脑王者开房答题抓题库,答题自动改答案
大纲:
项目地址:qiwihui/tnwz
如何为找接口请求
精准答题
自动答题
对于 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
,请求的资源指向的是
httpkit 的 echo 服务: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
参数设置成任何你想要的方法,通常为 POST
,PUT
,DELETE
方法,甚至是自定义的方法。
请求
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/json
和 application/xml
来 POST
和 PUT
资源,
而不是用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
如果不能发送带有数据的请求体,可以设置类似 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 构成。
当涉及到文件上传的表单时,正如你从写上传文件表单时知道的那样,这些使用 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 覆盖主机的情况下测试一个虚拟主机或者是缓存代理时很有用的。只需使用 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": "/", ...
}
天文学家预言,太阳将会在四百年内发生氦闪爆炸,它将化作一颗巨大而黯淡的红巨星,地球将在这次氦闪中被汽化,太阳系内所有适合生存的行行都将被吞没。为了生存,人类选择驾驶地球逃亡。
联合政府制定了分为5个阶段的逃亡计划:
这个计划将延续2500年,100代人类。
根据目前放出的《流浪地球》电影预告,电影应该主要讲述刹车时代和逃逸时代摧毁小行星,逃离木星引力这个过程中的故事,至于之后的人类大叛乱,如果有下一部的话,应该就会在下一部了。
以下的部分是对地球流浪过程中的一些问题的讨论和解释。
氦闪
这个问题具体请参考流浪地球:行星发动机理论可行吗?。
结论:
先说结论:(依据书中提供的发动机数据)
- 地球刹住车需要大概50年的时间。
- 地球从太阳流浪到比邻星大概需要25600年,差不多正好是书中提到的2500年的十倍。
- 消耗的总能量(质能转换)占地球总质量的大概千万分之一,如果人类的质量利用率是100%的话。“挖石头”看来足以支持流浪地球的能量来源。
(未完。。。)
这篇博客将介绍使用免费的let's encrypt证书, 为网站开启https。
(https, http over ssl)
(free, easy)
(directory tree)
我使用的是Debian 7,其他系统类似。
letsencrypt-auto
安装:$ git clone https://github.com/letsencrypt/letsencrypt
$ cd letsencrypt
$ ./letsencrypt-auto --help
实验前,我已将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指向对用的服务器,否则会提示申请不过。
首先生成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
https://qiwihui.com
,可以查看到证书信息: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.
最近开始转向使用 v2ray 作为主要的翻墙工具,在 macOS 上安装和使用都需要下载编译好的软件包然后解包使用,不是很方便,联系到 macOS 下常用的包管理 Homebrew,何不自己提交一个?
V2Ray 是一个模块化的代理软件包,它的目标是提供常用的代理软件模块,简化网络代理软件的开发。
简单说 v2ray 就是翻墙代理软件(但不止于软件,是一个平台)。V2RayX 就是 macOS 下一个简单的 v2ray 的GUI程序。
macOS上强大的包管理工具,类似于Ubuntu的apt。
安装:
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
使用,比如下载 curl
:
brew install curl
不知道为啥,官方的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
我向官方 Homebrew-Cask 提交了一个Formula,可以直接使用如下命令安装
brew cask install v2rayx
不过GUI毕竟不能覆盖命令行的全部功能,所以能用命令行v2ray的话,就尽量不使用V2RayX吧。
我的博客已经支持了 HTTP/2, 在此将介绍如何在 Nginx 上设置 HTTP/2 及相关注意事项(坑)。
HTTP/2 安装需要以下前提:
不同 Linux 系统对于 ALPN
和 NPN
的支持可以参见下表
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:
# 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 的版本。
需要编译 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,我使用 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了。
请参考之前博客 使用免费的let’s encrypt证书为网站开启https
第一步完成后就设置好了一个 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年到2014年还有不到一个月的时候, 对我第一年工作的状态有一个简单的描述, 每年都要给自己写一个年终总结。
Travis CI 自动检测代码变化,拉取,编译博客并部署到 GitHub Pages
写好博客之后,部署总会占去一段时间:编译、部署、推送和检查。手动部署多了也就烦了,一则容易出错,
比如把 master 分支用 gh-pages 分支覆盖了,二则劳动是重复的,重复的劳动就应该自动化去解决。
使用 GitHub Webhooks 实现自动部署,这就需要有一台服务器,在服务器上启动服务接受 Github 的
回调,然后拉取代码,编译,将编译后的代码要么部署在同一台服务器上,要么推送到代码 gh-pages 分
支上。前者额外需要编写服务,配置博客 Nginx,可能还需要配置 HTTPS,以及对服务器进行加固,总归
就是需要额外的更多东西来支持。所以还是觉得用已经存在的线上自动化服务方便一些(其实就是懒)。
持续集成(Continuous Integration,CI)的 SaaS 服务,好处不言而喻。
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
ERROR Deployer not found: git
npm install hexo-deployer-git --save
往 Github 仓库中提交代码是需要认证的,不管是用用户密码,Access Token还是SSH key。一种方法是
直接将认证写在 .config.yml
中,不是说不行,是太年轻。好在 Travis CI 不仅支持加密文件,
也支持加密 Keys,这就为认证这一块
扫清了道路,我决定使用 OAuth 认证 Git 来提交代码到仓库中。
操作步骤:
生成 Github Personal Access Token;
使用 Travis CI 命令行加密 Personal Access Token;
travis encrypt GH_TOKEN=<token> --add
在 .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...
胜利完成!
Github 除了作为代码仓库并与全世界的开发者共同协作外,利用 Github 还能做一些其他有趣的事情,这篇文章将简要介绍这些功能。
Github 并非博客托管站点,但确实很多程序员都喜欢用来写博客。使用 Github 写博客的方法有以下几种。
Github Zenhub
到今天,我已经在扇贝网上完成了300+天的背单词和阅读文章
, 单词量虽然不算很多,但是在这过程中的感想还是值得分享的。
我喜欢道家之数“三”,道家曰:一生二,二生三,三生万物。天下之事情有三而生,即是
刚刚开始,故而在300天左右的时候是很适合分享的。
背单词的最初起因里带有种愤,气愤,大体就是成为前女友眼中的“极品前任”。虽然这
在最出的几天很激励,但是这种感觉很快就消失了,之间有一段迷茫期,不知到自己为什么
背单词。直到有一天我找到了另一种坚持,一个自己一直想
去的地方:Multnomah Falls,这是在美国Oregon这州的一个瀑布,我被她绚丽的落差所折
服,有生之年不然是要前往的,虽然觉得去旅游和背单词不是很搭边,没什么联系。
就是这个,犹如在远方等待着我的少女,让我坚持到了现在:
扇贝网是我知道的为数不多的几个背单词的网站,除了单词,还有新闻文章,书籍,以及一
起背单词的小组和论坛,这在一定程度上激励着你一直坚持背下去,小组的作用更加的明显
,不打卡就踢人的制度很合适。对单词的单词的理解程度也完全靠自己的自觉。
背单词最大的感受是你不能只背单词,只背单词如同嚼蜡,刚开始就会觉得很舒服,没有营
养又损害身体,带来的效果也很小。单词背的同时结合着文章的阅读效果是很明显的,以前
老师常说的在语境中理解单词的含义单体就是这个意思。其次是要让自己处在一种英语的氛
围中,可以用英语阅读写文章,使用英文和朋友交流,使用英文的办公环境......如此种种
,都是很有效果的。再者,别人和自己的经验告诉我:背单词应该是意见很快乐的事情,如
果在这个过程过程中觉得很痛苦,那么是应该考虑一下自己的方法了。
李笑来在《把时间当作朋友中》中提到了背单词的方法:
在背单词的时候,事实上,在做所有类似的必须记住大量信息的工作的时候,一定要想办法
由衷地把这件事当成一件快乐的事情来做。我的一个朋友曾跟我分享他的做法:当年他终于搞明白要拿到奖学金就得获得GRE高分的时
候,背单词量要求吓了一跳。他说,他用两天才说服自己这应该是件快乐的事情。一共要搞定20000个单词,而因此可能获得的奖学金是40000美元左右 且连续4年没有失业的
可能,那么每个单词就值20元人民币,这还只不过是算了一年的收入而已。所以,他终于明白背单词是很快乐的,他每天都强迫自己背下200个单词,每在确定记住了
一个单词前面画上一个勾时,他就想象一下刚刚数过一张20元人民币的钞票。每天睡觉的时
候总感觉心满意足,因为今天又赚了4000块!
在这样的坚持了300+天以后,我觉得在更多的地方都体会到了一个坚持力量(很鸡汤的一
句话),但是确实是,坚持锻炼,或者开始每天/每两天更新一千字博客。
一句话总结2015年: 做了很多事, 欠了很多债。
年初三月离开了毕业后的第一份正式工作, 七月底加入青松, 中间的四个月从迷茫焦虑, 到完成第一次知识整合和补充, 算是一次小的飞跃。 在新团队的这
半年是自己能力和知识增长最快的半年。
感谢一路陪伴的岩, 许多事情不再纠结, 更有勇气去做一件事情。
整理和输出的东西太少, Evernote 和 Pocket 上记录的文章基本未有效整理, 博客自上次更新已是半年, 这点需要改进。
个人项目上, COMICS项目, 微信RSS项目和自己的公众号(我都忘了叫啥了)相继停止了维护/更新。 开源项目基本维持在阅读和 fork 别人项目的水平上, 对开源项目的贡献不够。
自己开发和信息收集的方法工具没有整理。
2015年阅书寥寥, 《三体》和《量子物理史话》是为数不多的能记住的, 倒是知乎上迄今645万字的阅读量确实令我咋舌, 读书的质和量都有待提高。
语言能力上, 英语继续保持之前的学习量, 只是意语刚开始没多久就放弃了。
每年都会学一项不一样的技能或者挑战一件不一样的事情, 2015年一个人背包旅行了一个月, 见识了江南的风景, 新增的技能就算滑雪了。相较于之前, 2015年的技能成长
比较缓慢。
2015年半壮半胖得长了十斤, 体重达到了历史最高点, 这是一段时间失衡与调整的结果。 2016年需要停止增长, 增加体能和力量训练。
2015年是变革与变化, 机遇和挑战。新的一年, 新的成长, 新的奋斗, 不变的梦想!
工作:
安全方向;
大数据;
机器学习初学:线性回归,神经网路,SVM;
深度学习入门
语言:Python, iOS, Go
阅读/读书:非技术的书阅读较少,五本左右
知识整理系统:RSS, PinBoard -> Pocket -> IFTTT -> Evernote记录,github分析
开源项目,维护乏力,hiwifi-ss
我的专长:
目标:
产品和实现
大纲:
spark
机器学习算法
异常监测和分类
你参加一个游戏,在你面前有4张1000万支票,其中一张是真的。游戏开始,你选了一张,之后主持人在剩下
的3张里,选择一个展示出来,验证后发现是假的。
问题:请分情况理性分析,此时,你的参赛权的价格
回答:请用下面两种方法分别作答
方式1(理论推导)
情况1: 不能重新选择时获奖的概率是1/4
情况2: 可以重新选择时是3/8
理由:
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 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
由于这个解决方案,我能够保留站点更新的历史记录,并跟踪使用给定站点更新实际修改的文件的更改。
我希望你发现这篇文章很有用。它描述了 Hexo + Travis CI + GitHub 用例的解决方案,但它可以解决从 CI 服务器环境运行时其他类似静态站点生成器可能遇到的问题。
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
: 查询列表中的的所有查询都必须都不为真
_score
: 指定的搜索查询匹配程度的一个相对度量。得分越高,文档越相关,得分越低文档的相关度越低。
Elasticsearch中的所有的查询都会触发相关度得分的计算。对于那些我们不需要相关度得分的场景下,Elasticsearch以过滤器的形式提供了另一种查询功能。
过滤器在概念上类似于查询,但是它们有非常快的执行速度,这种快的执行速度主要有以下两个原因:
通常情况下,要决定是使用过滤器还是使用查询,你就需要问自己是否需要相关度得分。如果相关度是不重要的,使用过滤器,否则使用查询。
curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
"query": {
"filtered": {
"query": { "match_all": {} },
"filter": {
"range": {
"balance": {
"gte": 20000,
"lte": 30000
}
}
}
}
}
}'
doc['my_field'].value和_source.my_field之间的不同:
在垃圾短信过滤应用 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。
分成三步:
Demo中使用的是"结巴"中文分词的 C++ 版本 yanyiwu/cppjieba。将其中的 include/cppjieba
和依赖 limonp
合并,并加入 dict
中的 hmm_model
和 jiaba.dict
作为基础数据,并暴露 JiebaInit
和 JiebaCut
接口:
//
// 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 |
---|---|---|
![]() |
![]() |
![]() |
添加 iosjieba:
见代码: qiwihui/SwiftJiebaDemo@caeb6c2
这个过程是将 C++ 的接口进行 Objective-C 封装,向 Swift 暴露。这个封装只暴露了 objcJiebaInit
和 objcJiebaCut
两个接口。
//
// 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
在 Swift 中调用 Objecttive-C 的接口,这个在官方文档和许多博客中都有详细介绍。
{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 */
.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]
}
}
控制台输出结果:
可以看到,测试用例 小明硕士毕业于**科学院计算所,后在日本京都大学深造
经过分词后为
〔拼音〕["小明", "硕士", "毕业", "于", "**科学院", "计算所", ",", "后", "在", "日本", "京都大学", "深造"]
,完成集成。
见代码: qiwihui/SwiftJiebaDemo@bc42e13
由于自己对于编译链接原理不了解,以及是 iOS 开发初学,因此上面的这个过程中遇到了很多问题,耗时两周才解决,故将遇到的一些问题记录于此,以便日后。
"cassert" file not found
将 .m
改为 .mm
即可。
compiler not finding <tr1/unordered_map>
设置 C++ Standard Library
为 LLVM libc++
参考: mac c++ compiler not finding <tr1/unordered_map>
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++
use of unresolved identifier
这个问题在于向项目中加入文件时,Target Membership
设置不正确导致。需要将对于使用到的 Target 都勾上。
相关参考: Understanding The "Use of Unresolved Identifier" Error In Xcode
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.
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.
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.
Here is the song
"The Brightest Star in the Sky" in English. Hope you like it.
2016年是毕业后觉得过得最快的一年,也是至今觉得过得最快的一年。因为"忙碌"和没有思考,我在这一年过得没有目标,没有计划。
2016年主要玩了两款游戏:Ingress 和 Minecraft(我的世界)。在Ingress上花费了很多的夜晚时间,在 Minecraft 上花费了一些周末时间。
入坑 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 的服务器,和朋友共同建设了一个世界。
我在8月1号开始维护开源项目:hiwifi-ss, 这是一个基于前人工作做的极路由翻墙插件,
主要完成了在新版本极路由上的界面更新和功能修复。因为工作和 Ingress 的原因,这个项目在10月底就暂时没有继续维护了。
还想做一些其他的项目和实现一些其他的想法,比如一个RSS在线阅读器。
2016年缺乏系统地学习。之前获取和记录知识的过程(书,RSS, 博客等 -> Pocket暂存 -> Evernote记录和归纳 -> 博文输出)并没有很好的实行和保持,
使得代码虽然会写,但是没有系统地去理解为什么,没有去理解怎样更好。拿搬砖来讲,只是回垒墙,还没有上升到造房子或者造更好的房子的程度。
今年没有读太多的书,准确说很少,以至于在最近的一段时间明显感觉到粗口增加,思考迷茫。看的文章很碎,而且没有及时思考,使得这一年没有太多的
**收入。
最近一段时间的英语学习质量也明显下降,多次任务没有认真及时完成,总体感觉能力没有提升。日语学习中断,停滞不前。
生活依旧是生活。
今年喜欢上了星星,很多时间在晚上活动,很多时候熬夜到很晚不肯睡。其实这也没有什么不对。只是如无必要,不要晚睡。
2016年想去很多地方,然而总是错过,广州,深圳,杭州,上海,以及一直以来的衡山,都成为了今年的遗憾。唯一的努力是完成了十月计划而耽搁的日本之行,
第一次出国成就达成。但是计划不足和日语能力也使得这次出行有些仓促,没有达到自己的预期。
逃避问题和冷漠处理问题都是感情的敌人。(来自一个人的反思)
逃避可耻且没有用。然而一个人却可以做很多事情而不需要进行顾虑太多。
有很多的时间花在了刷 Twitter 上,没事了刷,吃完饭刷,中午刷,晚上刷,甚至有时走路都在刷。认识了一些新朋友,但是除了游戏和社交软件
上的对话,没有更多深入的交流了。
计划有余而行动不足,是这一年的总结。很多事情有了开始,但是没有很好地坚持下去
日拱一卒,功不唐捐。
这是以后的每一年计划的一个宗旨:至少要坚持干完一件事情!每年都想做很多事情,但可能没有时间,也可能没有精力,不期待速成,但求每天都有进步。
详:
简:
Hello from qiwihui.
整理自手把手教你用git.
git reflog
查看历史记录的版本号idgit reset --hard HEAD^
git reset --hard HEAD~100
git reset --hard <one commit>
git checkout -- <file>
git reset HEAD <file>
git remote add origin https://github.com/username/project_name.git
关联一个远程库git push –u origin master
(第一次要用-u, 以后不需要)git merge --no-ff -m "comments" <branch_name>
git stash
: 可以把当前工作现场 ”隐藏起来”,等以后恢复现场后继续工作。
git stash list
: 查看git stash apply
恢复,恢复后,stash内容并不删除,你需要使用命令git stash drop
来删除。git stash pop
,恢复的同时把stash内容也删除了。git checkout –b dev origin/dev
, edit something, git push origin dev
git branch --set-upstream dev origin/dev
, git pull
, edit something, git push origin dev
git push origin <branch-name>
推送自己的修改.git push origin <branch-name>
推送。git push origin —delete <branch_name>
git branch --set-upstream dev origin/dev
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.