诗盗·隐玊

《#诗盗#·隐玊》:诗码中年初白头,科技人生稳如狗。神之八哥思千虑,一算钱袋拮九周。

注解

玊:通“士”。
稳如狗:稳妥地像狗,不是稳定。
神之八哥思千虑:要解决的问题是神级的难,脑力消耗很大。
一算钱袋拮九周:收入不行,只能过着拮据的生活。

改编自霹雳角色玉龙隐士诗号:

书剑青眼初白头,
智殊相悬问机难,
波澜困守役千虑,
一算龙隐决九川。

优化思维【5】合并步骤

前情

在《优化思维【3】消除没必要步骤》提到一个对象转化的例子:A 对象要转为 B 对象,实现时先把 A 对象转为中间对象 T,再将 T 转为 B 对象,由于两步都很容易实现,一个现有函数即可,所以很多人可能会采用这个思路。

下面要介绍的“合并步骤”,类似于优等生解应用题时“跳步”(一行合并多个步骤),可以作为前文的补充。

例子

1. 合并 cat 和 grep

以下命令 UMU 经常看到,其实它可以用 grep UMU test 来优化,减少一次管道交互。

1
cat test | grep UMU

2. 合并多个 sed

再看下面例子是从一个命令行里移除 A 和 C 两个选项:

1
2
3
cmd="EXE A=1 B=2 C=3 D=4"
removed=$(echo $cmd | sed -e 's/ A=[^ ]*//' | sed -e 's/ C=[^ ]*//')
echo $removed

其中两次 sed 可以合并为一次:

  • sed -e 's/ A=[^ ]*//;s/ C=[^ ]*//'

  • sed -e 's/ [\(A\)\(C\)]=[^ ]*//g'

  • sed -e 's/ \(A\|C\)=[^ ]*//g',这个 macOS 上不行。

  • sed -E -e 's/ (A|C)=[^ ]*//g',这个适合 macOS。

3. TFO

先查一下 sysctl net.ipv4.tcp_fastopen,一般应该是 1,说明客户端支持 TFO;如果是 2 则说明服务端支持;3 是同时支持。

TFO (TCP Fast Open) 是一种能够在 TCP 连接建立阶段传输数据的机制。使用这种机制可以将数据交互提前,降低应用层事务的延迟。

这其实也是一种合并步骤的思想,把传输数据合并到三次握手期间。

参考:

跟 UMU 一起玩 OpenWRT(入门篇16):Python3

为什么 Python3?

  1. Shell 不适合某些复杂运算,尤其是 OpenWRT 用的 ash。

  2. Lua 缺乏某些 SDK,比如说阿里云 SDK 就没有 Lua 版。

  3. Python2 已经过时。

  4. Node.js 在小型设备上不如 Python3 高效。

  5. C、C++ 之流太难了!Go、Rust 还得编译,麻烦。

  6. Perl、Ruby 已没落。

一个例子

当 IPv6 地址变化时,将地址发送到钉钉:https://github.com/UMU618/openwrt-ipv6-addresses

安装与调试

1. 安装可执行程序

1
opkg install python3-base

安装 python3-base 之后,就可以运行 python3 了。

1
2
3
4
5
6
7
root@UMU:~# python3
Could not find platform dependent libraries <exec_prefix>
Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]
Python 3.7.6 (default, Feb 11 2020, 12:41:31)
[GCC 7.5.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

用以下 Python 代码,打印目前已有的模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import sys

## 打印的信息太长
i = 0
for m in sys.modules:
i += 1
print('{0:2d} {1:32s} {2}'.format(i, m, sys.modules[m]))

## 不精准
i = 0
for m in sys.modules.values():
i += 1
if m.__spec__:
v = m.__spec__.origin
elif m.__builtins__:
v = '-'
else:
v = ''
print('{0:2d} {1:32s} {2}'.format(i, m.__name__, v))

## 推荐使用
i = 0
for m in sys.modules.values():
i += 1
s = str(m)
start = s.find("'")
end = s.find("'", start+1)
k = s[start+1:end]
start = s.find('from', end+1)
if start > -1:
start += 4
end = s.find('>', start+1)
else:
start = s.find('(', end+1)
end = s.find(')', start+1)
v = s[start+1:end]
print('{0:2d} {1:28s} {2}'.format(i, k, v))

结果为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 1 sys                          built-in
2 builtins built-in
3 _frozen_importlib frozen
4 _imp built-in
5 _thread built-in
6 _warnings built-in
7 _weakref built-in
8 zipimport built-in
9 _frozen_importlib_external frozen
10 io built-in
11 marshal built-in
12 posix built-in
13 encodings '/usr/lib/python3.7/encodings/__init__.pyc'
14 codecs '/usr/lib/python3.7/codecs.pyc'
15 _codecs built-in
16 encodings.aliases '/usr/lib/python3.7/encodings/aliases.pyc'
17 encodings.utf_8 '/usr/lib/python3.7/encodings/utf_8.pyc'
18 _signal built-in
19 __main__ built-in
20 encodings.latin_1 '/usr/lib/python3.7/encodings/latin_1.pyc'
21 io '/usr/lib/python3.7/io.pyc'
22 abc '/usr/lib/python3.7/abc.pyc'
23 _abc built-in
24 site '/usr/lib/python3.7/site.pyc'
25 os '/usr/lib/python3.7/os.pyc'
26 stat '/usr/lib/python3.7/stat.pyc'
27 _stat built-in
28 posixpath '/usr/lib/python3.7/posixpath.pyc'
29 genericpath '/usr/lib/python3.7/genericpath.pyc'
30 posixpath '/usr/lib/python3.7/posixpath.pyc'
31 _collections_abc '/usr/lib/python3.7/_collections_abc.pyc'
32 _sitebuiltins '/usr/lib/python3.7/_sitebuiltins.pyc'
33 atexit built-in

下面来实现获取 IPv6 地址的功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def net_hex_to_ipv6(h):
ipv6 = h[0:4]
i = 4
while i < len(h):
ipv6 += ':' + h[i:i+4]
i += 4
return ipv6

with open('/proc/net/if_inet6') as f:
for line in f:
p = line.split()
if p[3] == '00' and (int(p[4], 16) & 0x80) != 0x80:
ip = net_hex_to_ipv6(p[0])
print(ip)
f.close()

以上代码有个“美中不足”:只能打印地址的“首选格式”,不支持“压缩格式”。下面改进!

2. 安装轻量库

UMU 打算使用 socket 模块的工具函数格式化 IPv6 地址,但目前已安装的 python3-base 不带 socket 模块:

1
2
3
4
>>> import socket
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'socket'

所以需要安装 python3-light

1
opkg install python3-light

装完即可愉快地玩耍:

1
2
3
4
5
import socket

ip = '0618:0618:0618:0618:0000:0000:0000:0618'
print(ip, '->',
socket.inet_ntop(socket.AF_INET6, socket.inet_pton(socket.AF_INET6, ip)))

以上代码打印:0618:0618:0618:0618:0000:0000:0000:0618 -> 618:618:618:618::618

3. 全量安装

如果 python3-light 还不能满足您,推荐来个全家桶:

1
opkg install python3

PS: 不要以为只要上面这句就全装上了,前面的 opkg install python3-base 是必要的!如果只装 python3,则 /usr/bin/python3 并不存在!

(完)

Node.js 程序员的 C++ 进修指南【1】:SetTimeout

前言

  • 如果您看得懂,那么,这是 Node.js 程序员的 C++ 进修指南。

  • 如果您没看懂,那么,这是学 C++ 的劝退书!

目的

用 C++ 改写 Node.js 程序,主要目的可能有两个:保密、提高性能。

那么您肯定要问:为什么不用 Go 或者 Rust 改写?UMU 是推荐用 Go 或 Rust 的,而且相对改写为 C++ 要简单得多,本系列文章,可能从反面论证:您应该选择用 Go 或者 Rust 改写!

代码仓库

https://github.com/UMU618/cpp-for-nodejs-programmers

第一个例子

在《学习 Rust【2】减少代码嵌套》中,UMU 提到一个使代码平坦化的例子,咱们把其中最基本的功能提炼出来,成为最简单的例子:

1
2
3
4
5
6
7
8
9
10
11
setTimeout(() => {
console.log('step1')
}, 1000)

setTimeout(() => {
console.log('step2')
}, 2000)

setTimeout(() => {
console.log('step3')
}, 3000)

这段代码实现的功能是:一秒后打印 step1,再一秒后打印 step2,再一秒后打印 step3,退出。

翻译为 C++

首先明确一点:JavaScript 是 JIT 语言,不用编译,语言宿主直接解释运行。C++ 是 AOT 语言,需要编译。所以我们需要编译器(比如 g++、clang++)和编译脚本(比如 make、cmake)。下面我们会选择在 macOS 上使用 clang++ 和 cmake 来编译 C++ 代码。其中,cmake 其实是用来产生 Makefile 的,如果您学过 Makefile,可以直接用它。

1. 安装依赖软件

  • 安装 Xcode,以获取 MacOSX.sdk。

  • 安装 clang++ 和 cmake:

1
2
brew install llvm
brew install cmake

2. STL 实现

  • C++ 代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// set_timeout.cc
// UMU: 这是一个不太好的实现
#include <chrono>
#include <future>
#include <iostream>
#include <vector>

class Timer {
public:
template <typename T>
void setTimeout(T function, int delay);
void stop();
void wait();

private:
bool clear = false;
std::vector<std::thread> pool;
};

template <typename T>
void Timer::setTimeout(T function, int delay) {
this->clear = false;
pool.emplace_back(std::thread([=]() {
if (this->clear) {
return;
}
std::this_thread::sleep_for(std::chrono::milliseconds(delay));
if (this->clear) {
return;
}
function();
}));
}

void Timer::stop() {
this->clear = true;
}

void Timer::wait() {
for (std::thread& thread : pool) {
if (thread.joinable()) {
thread.join();
}
}
}

int main() {
Timer timer;
//timer.setTimeout([&]() { std::cout << "step1\n"; timer.stop(); }, 1000);
timer.setTimeout([]() { std::cout << "step1\n"; }, 1000);
timer.setTimeout([]() { std::cout << "step2\n"; }, 2000);
timer.setTimeout([]() { std::cout << "step3\n"; }, 3000);
timer.wait();
return 0;
}
  • cmake 脚本,CMakeLists.txt:
1
2
3
4
5
6
cmake_minimum_required (VERSION 3.5)
project (set_timeout)

add_executable(set_timeout set_timeout.cc)
set_property(TARGET set_timeout PROPERTY CXX_STANDARD 17)
target_compile_features(set_timeout PRIVATE cxx_auto_type)
  • 编译:
1
2
3
## cd to source code directory
cmake .
make

小结:以上代码,可用,但不推荐。首先它是用多线程模拟的定时器,当设置 N 个定时器时,将创建 N 个线程,这不够优雅。其次,当您取消定时器时,会发现它无法立刻取消并退出线程。

3. Boost Asio 实现

我们知道,Nodejs 内部使用 libuv 作为异步 IO 库,它是 C 实现的,用 C++ 调用 libuv 就显得不那么 C++,所以我们决定用和 libuv 同类且更强大的 Boost Asio 来代替。

  • 安装 boost,目前是 1.72.0 版:
1
brew install boost
  • C++ 代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <chrono>
#include <iostream>
#include <vector>

#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>

class Timer {
public:
template <typename T>
void setTimeout(T function, int delay);
void stop();
void wait();

private:
boost::asio::io_context io;
std::vector<boost::asio::steady_timer> timers;
size_t count;
};

template <typename T>
void Timer::setTimeout(T function, int delay) {
auto& timer = timers.emplace_back(boost::asio::steady_timer(io));
++count;
timer.expires_from_now(std::chrono::milliseconds(delay));
timer.async_wait([=](const boost::system::error_code& error) {
// boost::system::error::operation_canceled
// boost::asio::error::operation_aborted
if (!error) {
function();
}
});
}

void Timer::stop() {
for (auto& timer : timers) {
timer.cancel();
}
}

void Timer::wait() {
io.run();
}

int main() {
Timer timer;
//timer.setTimeout([&]() { std::cout << "step1\n"; timer.stop(); }, 1000);
timer.setTimeout([]() { std::cout << "step1\n"; }, 1000);
timer.setTimeout([]() { std::cout << "step2\n"; }, 2000);
timer.setTimeout([]() { std::cout << "step3\n"; }, 3000);
timer.wait();
return 0;
}
  • cmake 脚本,CMakeLists.txt:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cmake_minimum_required (VERSION 3.5)
project (set_timeout)

add_executable(set_timeout set_timeout.cc)
set_property(TARGET set_timeout PROPERTY CXX_STANDARD 17)
target_compile_features(set_timeout PRIVATE cxx_auto_type)

set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_RUNTIME ON)
find_package(Boost 1.71.0)
if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS})
target_link_libraries(set_timeout ${Boost_LIBRARIES})
endif()

小结:好很多,但太难了……这真是劝退书!

八哥之神【番外篇8】

听说鲁豫要来采访稣
请戴口罩采访

1. 今年遇到史称的最长春节,稣都是怎么打发时间的?

改剧本。2019 年 8 月时,《八哥之神前传【1】》识界大战时就提到“心灵生化病毒”,后来要解释识界之外的识界是怎么毁灭的,人类为何移居太阴之背时,原剧本也是延续这个套路。但不幸的是作者所处的宇宙遭遇病毒疫情,为了不造成恐慌,急忙连夜做梦改剧本。目前倾向于 11.7 级地震。

这个跳跃有点快,我都跟不上了,这些不是还没写吗?

是还没写,但早已梦见。本剧的真正创作时间是作者大学时期,距今已经 15 年。

太震惊了!难怪剧中不少情节很明显不符合稣现在的年纪!

没错!大学时代就打的草稿,故事来源于自己的梦和听闻周边的故事。原本计划剧名:《一睡千秋》、《一梦千年》或者《魔脑在世三千年》的,但由于当时阅历不够,自觉写不好,所以写个故事发展路线就放弃。2018 年底无意间在老硬盘里翻出来,感慨良多,决定把“概要”升级成“初稿”。

为什么现在的剧名是《八哥之神》,是不是过于抽象?

没错!故意起这样奇葩的名字,主要是因为稣低调。另外,这个剧本里的中心思想太过玄幻,不适合一般人,当然不能起个易懂的名字。

稣在扩展剧本的时候,有什么比较深刻的体会呢?

想太多,写不出来!

“概要”只是一个青春言情剧,后来想写的是一个人的一生……但是一个人不够精彩,干脆把一堆人写成一个“稣”。本质上说“稣”其实是一群人,而不是一个。比如《八哥之神【4】》魔性山军犬养殖训练基地那个场景,其实就暗示男主角是个精分。

我糊涂了,是一个精分,还是一群?

有差别吗?

呃……问点别的!春节期间还有其它印象深刻的事情吗?

有的!一直在充电!

稣真是好学,放假也不忘充电!

哦弄!是给 MBP15 和手机充电!

好吧!真是尴尬。我注意到稣居然留长指甲!似乎不符合直男的人设!

没错!这是为了按公共电梯的按钮。您想,现在都 PHEIC 了,直接用手指接触?那面积好大的,多危险呀!

2. 有读者说《八哥之神前传》写得比《八哥之神》有条理,有逻辑,情节更科幻、更合理。

没错,这是事实。因为《八哥之神》其实是《八哥之神前传》的续集,一般续集都是变差的。稣很机智地采用倒叙,先写比较差的续集,这样别人就不会喷:“续集写烂了!”而是说“前传的角色居然更多,剧情更复杂!”

3. 还会出现哪些角色和剧情呢?能不能简单预告下。

出现的角色:李冰月、李心觎,一对双胞胎姐妹;孟长生,一个不婚主义的男子。

剧情:爷要被捅死了……这应该不算剧透,《八哥之神》已经明确这点,开场时圣小开就是一个 60 多岁的年轻人,和周易类似,但更惨一些,毕竟这是一部“探讨生命悲剧色彩”的言情科幻喜剧。

4. 您刚说过,想写一个人的一生,那么对您来说人生的意义是什么呢?

本剧的人生观:死亡并不长久,轮回再度孤独。请注意这里的“度”是个动词。

别慌!一个人的一生虽然意义不大,但放在无尽时间洪流里就有意义了,您可以以一辈子为单位进行迭代,这辈子尽量做得更好,让下辈子起点高一些。太上大道,我稣慈悲!

5. 太高深了,不是很能理解。能聊一下剧中情节和您现实生活的关系吗?

嗯……这些故事全都有现实原型!就是时间点改改,人名随便起一个,把几个现实故事重组。但是,这些故事没有一个是发生在作者本人身上的,作者只是擅长蹭热点,沾点人间俗气,以对抗挨踢无情。

6. 使您决定写本剧的动力是什么?必然有一个标志性事件吧!

上辈子死前,死神劝稣要放下,稣回答:不,稣要追求永恒!

然而稣还是死了,还是被洗脑洗得一点都不剩。为什么有些感觉转瞬即逝,再也回忆不起来?不行!稣要再体验一次,然后把它写下来。于是稣养成写日记的习惯。

有一天,稣发现如果按照中国人的平均寿命计算,稣已经过掉一半,回忆起自己的前半生,稣发现人生是孤独的最终奥义。每个阶段都会新朋友,也会失去一些旧朋友。就是上一次转变的时机来临前,稣突然领悟到一个真谛,我们无法选择不失去,但可以选择阶段性先失去的是什么!比如哪类朋友应该舍去——那些无法互相理解的。

接着稣又发现,理解不是一件容易的事情,互相理解这个要求有点高。只要能合作就行,应该舍去那些无法合作的。最终稣终于理解人间的一个催化剂:嫉妒。于是稣感觉人间的故事学习得差不多了,可以总结一下,于是开始写剧本。

7. 还会写其它故事吗?

身为天族,不能过度体验和干预人间。写代码比较正经。

8. 最后一个问题:传说稣身家千万?

个币。

什么?怎么骂人呢?

真的。

八哥之神前传【8】

2042 年,床上

古思:怎么……这么快?爷不是要敲代码么?

圣小开:一日之计在于晨,美好的早晨当然是睡回笼眯最好了!说不定还能再见到狐狸精。

古思:那我先去洗澡!

圣小开:大早上的洗啥澡,又没让你侍寝。

古思:哦,我也换睡衣陪爷躺躺。

圣小开:好多年没和别人睡眯眯了,有点不习惯。

古思:那我们聊天吧!床除了睡觉,另一个大用途就是聊天。

圣小开眼神一撇:你会聊啥?C 还是 C++?

古思:C++!而且我真学过,贾老师特地要求我学的。

圣小开:反应真快。爷还是亲手确认一下……

古思:爷的手好冰呀!

圣小开:嗯,爷还是稣的时候体温就低于常人,当爷后体温就更低了,需要一个暖手宝。

古思:这点我看过的资料并没有提到。

圣小开:贾总是不是和你说了爷很多八卦?

古思:是有一些基本介绍,也没啥特别八卦的!我还得多直接从爷处了解。

圣小开:有啥问题就问吧,如果问得不好爷可能就眯过去了。

古思:爷刚才提到好几次“眯”,为什么睡觉要说成睡眯眯呢?这种骚里嗲气叫法不符合爷的人设!

圣小开:骚里嗲气?眯眯——其实是爷的绯闻女友!

古思:哇哦……原来如此,看来田心挖到第一个八卦咯。

区块链上的故事

9102 年的春天,一个风雨交加的夜晚,稣和王博士聊完一个关于人工智能的天后,精神还有点紧崩,边低头走路边思考科技的变幻和人生的虚幻。

突然偶遇现在被人称为稣的绯闻前女友的月光女神卢眯眯。距离上次见面过去快一年,相逢自是有缘,人生到底是虚幻,还是真实,尽在研究自己和参考他人,何不乘机聊聊人生?然而附近像样的小资消遣场所都已打烊,也没地方坐,她提议去她宿舍。稣心想那是员工宿舍,应该很安全,稣不可能被人暗杀,如果被仙人跳,稣只要大喊一声,这破地方认识稣的少说也有 60 个!于是果断去了。

进屋后温暖许多,在温差作用下,居然一时犯困。她去洗澡的时候,稣不小心就在她床上睡着。

醒来已经快凌晨五点。她就躺在身边。稣想:楼下宿舍门没有门禁卡是出不去的,又不忍心叫醒她,于是一个果断的决策:睡眯眯。

这一觉睡得很好,是稣一生中少数几个质量极佳还能记得的觉之一,于是稣从此改口称睡觉为睡眯眯。

唯一美中不足的是……

2042 年,床上

古思:吓醒了?

圣小开:不是……稣偷偷摸摸摸了一下,眯眯的咪咪原来只比六舅大一点点!

古思:六舅?

圣小开:嗯,是爷的舅舅,他很六,所以叫他六舅。不说他,总之就是比你小挺多的……

古思:原来爷喜欢这个!田心还可以去加大一些。

圣小开:不用,不用。适可而止!适可而止!爷只是用理性的眼光衡量!

古思:爷到底是正经还是不正经呢?好难分辨!

圣小开:一名男性整形医生整天研究女人的胸,你说他正不正经?

古思:这没有不正经呀!

圣小开:一个男孩整天研究女人的胸,因为他立志成为一名整形医生,你说他正不正经?

古思:好像有不正经的味道。

圣小开:嗯哼!区别在于专不专业!如果一个人业余研究异性心理,多半会被判定为不正经,而一名专业的心理学家,怎么研究异性心理,都是正经的。

古思:爷想说什么?

圣小开:只有假不正经,才能知道谁是假正经!这是一种调试八哥人生的方法。

古思:听起来,其实爷是很正经的人,故意表现得有些不正经?

圣小开:是的,正经得无趣,无趣得没朋友。爷还是要假装有朋友的。

古思:调试八哥人生是什么意思?

圣小开:很多时候,很多人,是不会说实话的,需要一些研究手段。比如陪他们演戏,但又不能表现得不自然,所以爷研究过这本《悲剧演员的自我修养》。

古思:贾老师说爷的好奇心很重,原来如此呐!

圣小开:不!这是大部分人对爷的误解。这个误解好大,大到爷每次解释都会被当成蛇精病。

古思:em?怎么回事?

圣小开:爷是质疑,不是好奇。别人是什么样的,爷都可以接受!爷从这个世界的基础开始质疑,以致于对别人的存在和真伪也质疑。比如,偶尔发现别人好像喜欢自己,就会去研究她是不是真实存在的,是不是有主观意志,是不是真的喜欢,以及为什么她会喜欢爷。反过来,爷喜欢别人也一样各种质疑。

古思:这是不是多疑?不自信?

圣小开:肯定是多疑。爷连自己都质疑,有时甚至怀疑“我思故我在”也有八哥!是在思考,但在哪里思考呢?在自己脑里,还是在远方的某处?不自信有点,但也不他信呀!

古思:田心喜欢爷是基因和洗脑程序决定的,是真实的。

圣小开:哦?你不抗拒一下这种安排吗?

古思:贾老师说,爷要是死了,我会进入重置状态,记忆可能全部丢失,和死掉无异。

圣小开:那爷睡眯眯后,你会怎么样?

古思:没有特别设定。可以一起睡呗。【心想:以前是不敢抗拒,现在看来没必要抗拒。】

圣小开心想:在爷的床上,没人可以比爷晚睡!等你丫睡着后,爷就用各种反侦察仪器检查你。

死神的套路

前情

每年春节期间,稣都要研究玄学,今年岂能例外?

一神论

每个宇宙都有且只有一个神,就是您信的那个。祂有不同的名字,比如信基督,那神的名字是耶和华;道家神叫太上老君。

其实神就那么一个,不同的人给祂取不同的名。也可以是您自己,总之您信就行。

稣信死神。

死亡并非永恒

没有什么状态是永恒的。死亡也不是。宇宙能创造无穷的生命,总有一天可以重现死去的生命。就像您随便写个数字序列,在 pi 里一定能找到无数次。

记忆是暂时的,意识比记忆长久。生死轮回,可没保证在地球轮回。众生平等的理论来源于此,这辈子在地球上当人快活,下辈子可能在火星当虫吃土。

天地不仁以圣人为刍狗

人喜欢强人,便崇拜他们。死神也会喜欢强人,但奴役他们。很多强人都被死神叫去服务祂了——这就是死神的套路。

什么是圣人?圣人一定是强大但又克制自己的强大的,所以他们没有崇拜这种情感,他们只敬畏天地。

您说自己有一技之长,但那是什么造就的?基因、营养、后天练习、时代巧合等,圣人认为这些没啥,不值得欣赏。

死神要人命时,很克制,不轻易收走。——这才是值得欣赏的。

反面,不知克制食欲暴饮暴食、纵欲过度,身体会很快坏掉。

鲁信说过:稣劝您当扫地僧和鸠摩智,而不是乔峰。

死神之下皆为蝼蚁

死神眼里正邪无异,善恶不分。但人们如果自己规定了什么叫善恶,还自以为是善,当他们发现自己原来是恶时,就会遭到自己的诅咒。

影剧里,恶人洗白或善人作恶往往命不久矣。恶人可能想赎罪救人而死,善人则可能是信仰崩塌,生无可恋。总之都是后悔死的。

善良真的是一种有能力才能选择的修炼模式,不是自我认定和按条款划分这么简单。法律应该高于道德,因为很多“道德”条款,根本上是错误的。

PS: 死神根本不关心您善不善良,对凡人来说,量力而行才是科学的。

顺天而行

一个人只要坚持追随天道,时间长了就会很牛逼。天道并非就是美好的,凡人只能追随那些追随天道的圣人,比如老子。

前面已经提到“量力而行”这个词,凡人不可能做到顺天而行,量力而行就好。

新型冠状病毒

态度

战略上藐视敌人,战术上重视敌人。

经验

世界上确实有两种人:年轻人和中老年人。

  • 年轻人对 SARS 可能没什么印象。即使现在被感染新型冠状病毒,靠自身免疫力战胜病毒的概率也比中老年高很多。稣发现他们中一部分人,对疫情并不关心,甚至认为别人讨论疫情是在制造恐慌……稣认为如果发现有人造谣、信谣、传谣,辟谣打脸就行,但干预别人正常讨论疫情的自由就不太好了。毕竟年轻人迟早也要变成中老年人。

  • 中老年人很可能本来就有一些慢性病,要是被感染,死亡率就比较高。

稣属于经历过 SARS 封校的中老年人。当年还被认为是疑似病患,被请去医院体检,还好没事。所以稣是属于相当重视的这类。

贪生怕死

小时候经常会看到一些武侠剧,某个配角嘴里冒出一句:“没想到你居然是贪生怕死之辈!”语气带着不屑和鄙视!但说完,一般他就挂了……每每看到类似情节,稣的内心都是一阵纳闷:“贪生怕死不是正常的吗?这个人为了点莫名其妙的小事就去死,好像才不正常吧?”

有人说这次新型冠状病毒死亡率不高,没啥好操心的。

稣认为死亡率可能真不高,但却是多出来的一种。我们本来也存在被流星砸死的可能,但加上“新型冠状病毒致死”这一种可能性之后,整体死亡率明显是提高了,干嘛冒这个风险?

三思而后行

造谣者一般都是为了利益或者某种心理刺激。作为小老百姓,造谣应该不多,但信谣传谣就多了去。有些人为显示自己信息渠道多而广,就喜欢第一时间转发信息,而不是先鉴别。这种心理很多人都有。有些人不在网络上传谣,但聚会当面传是一样一样的。

也有些人是没鉴别能力,宁可信其有。稣在学习的路上,也没少被谣言短暂攻破过,要三思而后行和及时修正。

对自然心存敬畏

吃野味风险很高,那些爱吃的人估计也有一种攀比、装逼的心理,因为吃起来危险反而去吃。这是病,得电!

RNA 病毒因为是单链不稳定,所以变异能力比较大,很难预测后期是毒性减弱还是增强,以及人群里会不会出现超级传播者。流感病毒也是 RNA,稣基本每一两年都中一次,上一次的抗病毒经验无效,说明中的很可能是新变异品种。

相信科学,但也要明确还有很多病现在的医学治不了。敬畏自然,主动预防被感染!

学习 Rust【2】减少代码嵌套

结论先行:减少代码嵌套就是降低复杂度。

资源管理一向是编程中的重要任务。当一个函数要管理多个资源时,很容易出现代码嵌套层级太深的问题,尤其是调用系统或第三方 API 时。

以 C 语言代码为例,这里简化为两个资源,请您自行脑补多个资源:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int error_code = 0;
resource1 *p1 = new_resource1();
// UMU: with C++ SHOULD be `p1 != nullptr`
if (p1) {
resource2 *p2 = new_resource2();
if (p2) {
if (!deal_resources(p1, p2)) {
error_code = 3;
}
free_new_resource2(p2);
} else {
error_code = 2;
}
free_new_resource1(p1);
} else {}
error_code = 1;
}

return error_code;

上面代码最深嵌套是三层,为了减少嵌套,可以把代码改为平坦结构,降低到一层:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
resource1 *p1 = new_resource1();
if (!p1) {
free_new_resource1(p1);
return 1;
}

resource2 *p2 = new_resource2();
if (!p2) {
free_new_resource1(p1);
free_new_resource2(p2);
return 2;
}

if (!deal_resources(p1, p2)) {
free_new_resource1(p1);
free_new_resource2(p2);
return 3;
}

free_new_resource1(p1);
free_new_resource2(p2);

但这么改在资源释放时,更容易遗漏。也有人为使代码层级平坦化,会使用 goto 到函数末尾统一释放,或者更优雅点的 C++ 方式:用 try...throw...catch...finally 将所有资源包含起来管理。

Node.js 的异步回调函数也存在嵌套层级过深的问题,可以用 Promise 来平坦化,参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
setTimeout(() => {
console.log('step1')
setTimeout(() => {
console.log('step2')
setTimeout(function() {
console.log('step3')
console.log('done!')
}, 1000)
}, 1000)
}, 1000)

// flatten
let timer = (text) => {
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(text)
resolve()
}, 1000)
})

return promise
}

timer("step1")
.then(() => {
return timer("step2")
})
.then(() => {
return timer("step3")
})
.then(() => {
console.log("done!")
})

C++ 建议使用 RAII 思想来管理资源,获得资源后立刻放到管理对象里。如果有些资源使用得不频繁,想偷懒不去封装,则可以使用 scope_exit。go 语言更是用内置关键字 defer 来提供 scope_exit 机制。

Rust 用 scopeguard 提供 scope_exit 机制,defer! 宏和 go 的 defer 功能类似。

另外,Rust 还有 ? 操作符,也有减少嵌套的作用。比如这个任务:打开文件,如果失败就返回错误。go 是这样写的:

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import (
"os"
)

func main() {
file, error := os.Open("file.txt")
if error != nil {
panic(error)
}
defer file.Close()
}

同样功能,Rust 代码少一层:

1
2
3
4
5
6
use std::fs::File;

fn main() -> std::io::Result<()> {
let _f = File::open("file.txt")?;
Ok(())
}

八哥之神前传【7】

本命年,在夏兴电池工厂上班。工厂位于海城夏阳村,一个鸟不拉屎的荒地。宿舍就在工厂旁边,一面是工厂,其余三面都是荒地……生活除了上班,加班就是无聊和同事瞎逼逼。女朋友,那是不可能有的,美女也不可能来这种地方呀!

突然有一天,一个带着光晕的美女出现在稣眼前,十分神奇的是,别人好像都不主动聊关于她的事情,这一点都不像大家平常的风格!丑的狂喷,一般的考虑去追,现在来一个天仙级别的,各个都装蒜,是在独自算计自己的成功率吗?还是大家都瞎了?

经过观察,稣发现她居然就住在正上楼。稣和她用的是同一根水管,这可能让稣多了几分胜算。更巧的是,偶然发现,晚上她喜欢在楼下散步。这可是稣的强项,毕竟四周没有什么地方好玩的,楼下转圈,稣一个晚上可以走 20 圈。于是,转着转着,就认识她了。胡小玉,没错,很像狐狸精的名字……

胡小玉:“你很勇敢嘛。”

稣:“勇敢?为什么这么说?我只是质疑现实,想研究你而已。”

胡小玉:“这里的人好像都不理我,就你敢接近我。”

稣:“哦。这个‘敢’字,说得好像你很可怕似的。”

胡小玉:“我是狐狸精,不可怕吗?”

稣:“嗯?你开玩笑吧!不过你真的很漂亮,感觉很不像真实的人。别的女人脸上一般都有痘印、斑啥的瑕疵,你居然没有,而且声音也很好听。你真是人如其名,一块美玉。”

胡小玉:“你话真多,想睡我吗?”

“什么……”稣一阵尴尬之后,“稣今年 24 周岁,已经成年。”

胡小玉:“那就是想!走吧。”

稣:“这么简单?等下是不是会掉流星把稣砸死?”

胡小玉轻轻一笑:“哈。如果除了睡觉之外,你还干别的,我会咬断你的脖子。”

扑通扑通,到她房间门前,上面居然多了一道符,还加了一把锁,所以我们进不去。

胡小玉:“看来有人发现我是狐狸精了。从你房间爬上去吧!”

稣内心一惊,“难道你不是开玩笑的?”

胡小玉:“笨蛋。那你还跟不跟?”

稣:“会死吗?”

胡小玉:“乱来会死,不乱来就不会。”

稣:“但是稣恐怕爬不上去!11 层爬到 12 层,万一摔下去,必死无疑。”

胡小玉变成一只白色狐狸,顺着管道爬到她房间阳台。稣看得怀疑人生,不行,怎么可能有这么无稽的事情呢?

稣心想赶紧跟上去看看什么妖,虽然费了九牛二虎之力,但居然也跟着爬上她阳台。

但是一进她房间,稣就感觉不对劲,她是狐狸精,会法术的,万一她是附身一个美女,这时候离开肉体,这美女醒来不是要告稣非礼?万一肉体是死的,稣就被当成杀人凶手,太可怕了!

胡小玉见稣迟疑,开口道:“12 年前,你就见过我,不记得咯?”

稣:“不可能……认识你之前,稣就没见过你这么美的。如果有,肯定会记得。”

胡小玉:“我把记忆还给你吧!”说完吹了一口妖气……稣啥都想起来了!上一个本命年,稣去 KFC 上厕所,然后在里面坐了一会儿等同学,看别的顾客在吃,穷稣流下了没钱的口水。后来有个姐姐端着全家桶坐在稣对面,那护体神光照得稣不敢看。她微笑着给稣一个鸡腿,稣才乘机偷看一眼,真是天仙下凡,四周背景突然全白,其它一切仿佛都消失得无影无踪。后来,同学找来,那位姐姐一转眼没注意就消失,竟似人间蒸发!只留稣手里的鸡腿,稣只能含着口水把它吃完。

胡小玉:“想起来了?你欠我一个鸡腿,睡一觉还我。”

稣大惊失色:“为什么要睡觉还呢?”

胡小玉:“我需要阳气。不愿意吗?没要你命就好!”

稣心安理得地睡下了。醒来已经又过了一个本命年。稣走出木屋,身处深山老林,过着田园生活。

时光荏苒,三次见胡小玉,她竟然一点都没变老。稣千真万确地相信,她真的是狐狸精。于是稣先开口问:“这次是要干嘛?”

胡小玉:“我要吃人肉!不然我不久后就会死掉。”

稣:“认识你 24 年,不会还要吃稣吧?吃别人行不行?”

胡小玉:“笨蛋!要吃你,还需要和你说这么多?如果你想每 12 年见一次我,就帮我找!”

稣:“死人行不?这山下去就是哈岱医学院,那里有不少人体标本,虽然泡在福尔马林里,拿出来洗洗应该还好吧!”

胡小玉:“不行,要活人。”

稣:“刚刚安乐死的人可以吗?脑先死,肉体还新鲜……”

胡小玉:“老头不行,生命力不够!有年轻人愿意安乐死吗?”

稣:“你要多重?稣割点给你还不行吗?”

胡小玉:“64g 血肉,肥肉不要。”

稣:“什么……肥肉还不行?稣还想减肥呢!这么多,要死的,要死的!牛肉行不行?”

胡小玉:“不行。我要维持人形,就要吃人肉。”

稣:“真麻烦。看来要去找研究基因工程的孙大师帮忙了,克隆人肉,啧啧,想想就肉麻……”

胡小玉表示感谢,紧紧抱着稣,突然咬稣一口:“其实你是唐僧转世,吸你点血就够了。”

稣:“唐僧!?”

吓醒。原来侧睡压麻右手,被小玉咬的地方还隐隐抽筋……压麻的,压麻的!

2042 年,布信聊天

贾力劣留言:开哥,送你的美女叫胡思,是女娲造人计划最新产品,属于部分可编程人造人,三年科学教育优等生。知道你不喜欢自由意志,特地选择这位不怎么听话的,希望您会喜欢。

圣小开:贾总,你不是耍我吧!部分可编程?人造人?不是机器人来着?不听话到什么程度呢?不会乘我睡咪咪剪小鸡鸡吧?

贾力劣:不会不会!她是受过高等教育的,懂事。人造人是有生育能力的,只要她愿意,可以给你生猴子!比机器人有意思多了。

圣小开心想:贿赂?卧底?

2042 年,圣小开家

家联网系统提示有人拜访,还出现广告——神荼系统,保卫家园。自动为您接待来访嘉宾:暂名胡思,女,4 岁,AVILab 女娲生产线第 11 代人造人,由贾力劣总经理指派……

初次见面,有点像刚认识胡小玉那样,即激动又顺利。大概都是编好的程序起作用吧!

胡思:老爷好!我是胡思,小名叫田心,有事请吩咐。

圣小开:不用客套,田心。不要称呼老爷,把老去掉吧,赫赫。大部分家务事,机器人都可以搞定,你就陪老夫聊天和侍寝就行。

胡思:好的,如果对我的初始设定有任何不满,爷都可以修改。

圣小开:先改个名字吧!胡思胡思,容易胡思乱想,把月去掉,叫古思可以吗?

胡思:好的,以后我就叫古思,稍后提交到链上女娲管理系统。

一阵三思而后行的延迟。

圣小开:嗯……改你名字,好像不太好!老夫错了!你不会生气吧!

古思:生气?我生什么气?我哪敢生气?

圣小开:你就是生气了!真小气!

古思:赫赫。我学爷开玩笑的套路呢!没生气,我的名字本来就是别人取的,无所谓啦。贾老师说爷就爱胡思乱想,我被创造的目的就是为了陪伴爷,所以他给我取名为胡思。

圣小开:哦!既然你不介意,那不如叫胡小玉怎么样?

古思:来不及了,信息已上链,只能改一次。

圣小开:哈,开个玩笑,你没有狐狸精那么高冷,反而更像狐狸精。

古思:狐狸精?爷已经开始胡思乱想了么?

圣小开:没有没有,只是早上做的一个梦,还是赶紧吃完饭,敲代码吧。

古思:我可以陪爷结对编程。

圣小开心想:贾总果然是派卧底监视劣者。