目录 ¶
-
- 硬盘速度测试
- 交互式绘图程序 gnuplot
- emacs 编辑器
- 构建 emacs
- ruff - 用 Rust 构建的 Python 的 linter 和 formatter
- pyright - Python 的类型检查器
- ffmpeg
- 如何对待坏代码
- 基于 Golang 的 Actor 引擎:Hollywood
- helix 编辑器
- Heap profiling
- libgc - C 和 C++ 的垃圾回收器
- Linux 内核的手册摘要
- 在 webfs 上挂载 luks 环形设备
- 霞鹜文楷
- micro 编辑器
- Github 发布的 monaspace 字体
- 标记清除算法
- 自豪版本规范
- Reader Monad
- sphinx 文档工具
- 深度学习库 torch
- Vanilla OS
- xdg-open 工具的配置
- zellij
- ZK Note
- zoxide
- 基础拓扑
- qutebrowser
阅读代码 ¶
std::vec::PeekMut::pop 的 self 参数改为 this: Self ¶
Rust 代码库:https://github.com/rust-lang/rust.git
提交:ce859d7713dbca1b2a3dff17ccc4d4a0598bffbf
函数 PeekMut 的成员函数 pop(self) -> T 被修改为 pop(this: Self) -> T。这导致不能通过 x.pop() 来调用 PeekMut::pop,而必须要写成 PeekMut::pop(&x)。
这个修改的原因是 PeekMut 实现了 Deref,可以解引用为 T 类型。写 x.pop() 的用户,可能是想调用 T::pop 而不是 PeekMut::pop。
记录 ¶
也许十份 ¶
「贫民窟里长大的孩子,他们眼中的希望,是什么模样?【南洋大宝荐vlog】」:https://www.bilibili.com/video/BV1LT4aekE7N
Ayeza ¶
稳重:你是问我来自哪个国家吗?
小女孩:(点头)。
稳重:我来自中国,你知道中国吗?
小女孩:知道!你叫什么名字。
稳重:我叫 Take it easy,你叫什么?
小女孩:我叫 Ayeza。
稳重:是个很美的名字。
Ayeza:谢谢。
在吃什么 ¶
稳重:你在吃什么?
Ayeza:这是培根肉。
Ayeza:(看着摄影机笑)。
稳重:你吃的这个东西多少钱呀?
Ayeza:20(比索)。
注:20 菲律宾比索大概兑换 2.5 元人民币(2025 年 1 月)。
稳重:所有这些吗?
Ayeza:(点头)。
稳重:你喜欢吃吗?
Ayeza:Yes。
相机 ¶
Ayeza:你这是拿着相机吗?
稳重:是,我在举着相机……
Ayeza:多少钱啊?(指相机)
稳重:……
稳重:也许够买十份这个(指培根肉)吧。
冯骥关于 GTA 的发言 ¶
背景 ¶
黑神话悟空没有获得 GTA 年度游戏奖,主创冯骥第二天发表了一篇微博。
为什么叫游戏科学 ¶
使用科学而不是神学或者玄学来解决问题,以此才能平视这个世界,看到强大的对手厉害在哪里。
如果有人不提具体问题,不讲论据和逻辑,却断言一件事情水很深,你也许应该考考他偏微分。科学不是真理,是用事实求是的态度追求真理。
游戏的价值 ¶
我一直相信,游戏的最大价值,是让这个世界幸福感的分布更公平了。
游戏的人与社会 ¶
刘梦斐
先有游戏还是现有文化? ¶
对游戏的偏见:
- 新媒介恐慌。当新出现了摄影和电影时,人们也因为「它会带走人的灵魂」而感到拒绝。
- 儒家文化的影响。儒家文化是集体文化,人的价值需要在集体中得到实现。而游戏使人可以在亚集体的圈层中得到满足,儒家文化一致的反对所有的类似渠道,认为它们是不道德的。如果游戏是道德的,人就到更大的集体中实现自己的动力会受到损害。
游戏(game)和游玩(play) ¶
游戏是有规则的游玩,是游玩的一部分。
游戏跟其他类似艺术媒介的区别在于,玩家并不是简单的观众,玩家是游戏的故事的一部分。游戏非常非常多样,难以一一个简单的本体论方法总结游戏。
可爱的雨女无瓜 ¶
雨女无瓜是「与你无关」的谐音。「与你无关」这个词的意思非常强硬,表示某件事你不应该插手,单独用这个词时,实际上是在表示对方多管闲事。
相比原词,这个词出现了很多事物,「雨」、「女」和「瓜」在给人的感受上都相对不具有侵略性,因此缓和了「与你无关」这个词的给人的强硬感觉。
第二,从发音来说「yu nv wu gua」相比「yu ni wu guan」发生了一个变音和一个漏音(或者说两个漏音),这实际上会让人联想起小孩子呀呀学语的过程中出错的场景,因而给人可爱的感觉。这是一种非常常见的心理现象,人类会对接近人类幼崽的行为表现喜爱,这可能是因为进化出这种感受能力能使得人类的幼崽的存活率更高,从而族群更加有进化优势。例如人类看到小猫小狗感到喜欢,觉得它们可爱也是一样的道理。
不过这种现象是双面的,也可能在一些情况下,人们对「看起来像小孩子的行为」感到厌恶,这也是人的常见心理感受,这多出现在需要人认真看待事物的场景,这些场景中小孩子自然都因为缺乏经验而不被信任,从而这些像小孩子的行为是不合适的。
做个没有感情的机器 ¶
假设你在路上看到一个身材非常好的男神或者女神。你从他或者她的好身材看出他一定经常去健身房。
我们可能会思考的是,他为什么变得身材这么好。我们想到很多可能:我们可能觉得,他天然的积极地热爱运动,就有这种人。我们还可能觉得,他是自律的人,做出了很大的牺牲才得到了如今的身材。
当我们认为他是牺牲而自律的时候,我们可能会羡慕他,也可能会感觉,这样牺牲得到的好身材并不值得。
当我们认为他是天生的时候,我们可能会嫉妒,觉得我也这样就好了,进而我们觉得,我自己可能不是天生的这种人,所以我可能做不到他这样了。
这些都是情绪判断。是我们面对一个目标时产生的情绪。在这个例子里,情绪本身并没有产生任何增益,他只能导致我们越来越搞不清我们对目标(通过健身得到好身材的)的态度。
在类似的这样的场景,我们可能需要摒弃我们的感情判断,通过理性做出机械化的思考。
Eliza ¶
Eliza 的核心真的很简单,它只是逆转了一切。它只是把人们说的话抛回去。它是一面镜子。
相同的道路,不同的原因。
其他 ¶
jpeach 博客 ¶
我发现了这个博客,它每一篇都很短,但是确实是一些值得记录的东西。博客的标题是「Remembering things by writing them down」。
象棋规则 ¶
棋盘 ¶
象棋棋盘是 9 x 10 的网格。象棋的棋子都放在棋盘的格点上。
1 2 3 4 5 6 7 8 9 1 +---+---+---+---+---+---+---+---+ | | | | \ | / | | | | 2 +---+---+---+---+---+---+---+---+ | | | | / | \ | | | | 3 +---+---+---+---+---+---+---+---+ | | | | | | | | | 4 +---+---+---+---+---+---+---+---+ | | | | | | | | | 5 +---+---+---+---+---+---+---+---+ | | 6 +---+---+---+---+---+---+---+---+ | | | | | | | | | 7 +---+---+---+---+---+---+---+---+ | | | | | | | | | 8 +---+---+---+---+---+---+---+---+ | | | | \ | / | | | | 9 +---+---+---+---+---+---+---+---+ | | | | / | \ | | | | 10 +---+---+---+---+---+---+---+---+
杂项 ¶
硬盘速度测试 ¶
测试硬盘的读写速度需要绕过 cache,直接写入硬盘,此外还要在测试时不要有其他进程干扰。
dd if=/dev/zero of=./testfile bs=1G count=1 oflag=direct dd if=./testfile of=/dev/null bs=1G count=1 iflag=direct
注意这里的 iflag 和 oflag 设置,dd 会报告平均的读写速度。
交互式绘图程序 gnuplot ¶
命令行参数 ¶
解释脚本文件绘制图像,-p 表示程序退出之后还展示窗口。
gnuplot -p script.gp
-e 选项直接从命令行指定命令。脚本文件中也是这样的若干命令。
gnuplot -p -e 'command1; command2'
plot 绘图命令 ¶
查看 http://gnuplot.info/demo ,包含许多绘图指令。
从 data.txt 读取数据,第一列是 X 轴座标,第二列是 Y 轴座标,绘制带点和带直线的折线。
plot 'data.txt' using 1:2 with linespoints title 'Hello'
从 data.txt 读取数据,数据的第一行不是数据,而是列的标题。以第 1 列为 X 轴,以第 2 到 5 列的每一列为 Y 轴绘制点图。
plot for [i=2:5] 'data.txt' using 1:i with points title columnheader(i)
在 -10 到 10 的范围内绘制 sin(x),arctan(x) 和 cos(arctan(x)) 三条曲线。
plot [-10:10] sin(x),atan(x),cos(atan(x))
其他设置 ¶
环境变量 GNUTERM=qt 设置绘制的「终端」,即用什么绘图库来展示图像。常见的终端还有 wxt 和 x11。不同终端的交互能力不同,例如我发现使用 wxt 终端可以有更好的交互操作。
set xlabel XX 和 set ylabel YY 设置图像的 X 轴和 Y 轴的标签。
set title TT 设置图像的标题。
set mouse 打开鼠标交互操作的支持,但这还需要终端支持对应的操作。
终端设置 ¶
set terminal wxt 或者 set terminal wxt 0 表示用命令设置「终端」,而最后的 0 表示第 0 个窗口,类似的可以设置多个窗口,只要 plot 在 set termainl 之后,就可以绘制不同的内容到不同的窗口。
还可以设置 set terminal svg 以及其他非窗口格式,它需要配合 set output PATH 设置输出文件的位置。
emacs 编辑器 ¶
配置 ¶
使用 M-x customize 打开配置 UI,可以方便地配置和修改配置变量。
- indent-tabs-mode 使用 TAB 缩进。
- confirm-kill-emacs 在退出 emacs 的时候确认。
- inhibit-startup-screen 启动屏幕。
- make-backup-files 自动备份。
- auto-save-default 自动保存。
- menu-bar-mode 菜单模式。
- tool-bar-mode 工具栏模式。
复制剪切粘贴 ¶
C-y 是粘贴,y 表示 yank(召回)。
C-w(wipe)是剪切选中的部分,M-w 是复制。复制和剪切的区别只有是不是删除原本的内容。所以被绑定到 C-w 和 M-w
放大缩小字体 ¶
- C-x C-= 放大字体。
- C-x C-- 减小字体。
- C-x C-0 恢复字体大小。
除了前缀 C-x,跟一般编辑器的习惯差不多。
也可以使用鼠标滚轮。
切换到制定行 ¶
M-g g 跳转到指定行。M-g 绑定了一系列移动指令,常用的有:
- n:next-error
- p:previous-error
- g:goto-line
M-g 后的 M-g、M-n、M-p 跟 g、n、p 的作用一样,我觉得这是为了防止按键按错。
ibuffer ¶
M-x ibuffer 启动 ibuffer。可以用 m(mark)键选择 buffer,用 u(undo)键取消,用 D 键删除。
用 RET 切换到 buffer。
可以用 C-x b NAME 可以切换到不同的 buffer,这样在你迷路的时候,相当于切换到了导航页面。
构建 emacs ¶
使用 --with-gnutls=no 和 --with-x=no 关闭对 gnutls 和 x 的支持。
emacs 需要 ncurses 支持(实际上是 tinfo 支持),而 ncurses 我构建的版本没有安装 pkg-config 脚本等信息,所以需要用 emacs 构建需要的 CFLAGS 和 LDFLAGS 来告诉 emacs terminfo 的位置。
export CFLAGS="-I$ncurese_DIR/include" export LDFLAGS="-L$ncurses_DIR/lib"
emacs 还需要 texinfo 的 makeinfo,需要先安装它。
ruff - 用 Rust 构建的 Python 的 linter 和 formatter ¶
An extremely fast Python linter and code formatter, written in Rust. https://docs.astral.sh/ruff
配置 ¶
使用 .ruff.toml 文件。
[lint] select = ["E", "PL", "F", "ANN", "TC", "SIM", "PTH"] ignore = ["ANN401"]
lint.select 选择一些打开的 linter。ruff 支持大量 linter。用 ruff linter 命令,可以查看所有的 linter。可以看到,每个 linter 前面都有个简称,这里配置的 SIM 和 PTH 等就是不同的 linter 的简称。
- E - pycodestyle
- PL - Pylint
- F - Pyflakes
- ANN - flake8-annotations
- TC - flake8-type-checking
- SIM - flake8-simplify
- PTH - flake8-use-pathlib
每个 linter 又都支持大量的检查项目。例如 ANN401 是 flake8-annotations 标号为 401 的检查。lint.ignore 数组的配置表示抑制这个检查。
pyright - Python 的类型检查器 ¶
pipx install pyright
使用 # pyright: strict 注释可以让 pyright 开启严格模式。在 pyrightconfig.json 配置 strict 为启用严格模式的文件夹列表效果也一样。
ffmpeg ¶
转换裸图像:test.yuv 包含 3 个 800x320 的 YUV444 的二进制数据。将它们编码成 out-%d.jpg。
ffmpeg -s 800x320 -pix_fmt yuv444p -i test.yuv 'out-%d.jpg'
解压视频,将 video.avi 解压成 out-%4d.jpg:
ffmpeg -i video.avi out-%04d.jpg
从标准输入输入一帧 H.265 帧,保存为 out.png:
some command | ffmpeg -i pipe:0 -v:frames 1 out.png
如何对待坏代码 ¶
- 尽量保持不动。
- 将自己的代码变成可以替换的,模块化的组件。可以随时在系统里插入和删除。避免重命名,这样可以将修改最小化。
- 经过实验发现代码确实要修改的时候,先隔离出一块小代码,然后重写这一小块。进行大量的测试和实验。
参考 ¶
https://www.yinwang.org/blog-cn/2017/05/17/practical-idealism
基于 Golang 的 Actor 引擎:Hollywood ¶
Hollywood 是一个基于 Golang 的超快安全轻量级的 Actor 引擎项目。它是为快速和低延迟的应用程序(如游戏服务器,广告代理商,交易引擎等)而构建的,能在一秒内处理超过 1000 万的消息。Hollywood 的主要特性包括消息在 Actor 失败时的可靠传输 (缓冲机制)、忘却式或请求响应式消息传送,采用高性能的 dRPC 作为运输层、优化的 proto 缓冲区(没有反射)、轻量级和高度可定制、集群支持等。
开源项目地址:https://github.com/anthdm/hollywood
helix 编辑器 ¶
helix 编辑器现在是我的主力编辑器。
插入模式光标改为下划线 ¶
配置:
[editor.cursor-shape] insert = "underline"
新增语言服务器 ¶
配置语言服务器:
[language-server.new-lsp] command = "lsp-command" args = ["--stdin"]
在语言中使用语言服务器::
[[language]] name = "new-language" language-servers = ["new-lsp"]
helix 可以支持使用多个 lsp 了。虽然我只在 python 里遇到过这种情况。
Heap profiling ¶
Heap profiling means profile the heap memory allocation of a system process.
This need libraries control the interface of allocate and free memory.And use some dump file to profiling when and where a heap profiling happends.
This is very useful if you want to check memory leak of you process.
- tcmalloc:https://github.com/google/tcmalloc
- jemalloc:https://github.com/jemalloc/jemalloc
libgc - C 和 C++ 的垃圾回收器 ¶
This library use the famous mark and sweep algorithm.
Linux 内核的手册摘要 ¶
维护者手册 ¶
https://www.kernel.org/doc/html/latest/translations/zh_CN/maintainer/index.html
维护者在合并合并请求的时候,需要知道:如果有不寻常的修改,需要解释原因。如果在合并窗口合并之后提交了合并,还要额外解释为什么错过了合并请求。
开发过程总览 ¶
https://www.kernel.org/doc/html/latest/translations/zh_CN/process/2.Process.html
内核每 2-3 个月(8-12 周)发布一次更新。
合并窗口:Linus 宣布打开合并窗口的时候,每天大约合并 1000 个补丁,持续大约 2 周,Linus 宣布合并窗口关闭,发布第一个 rc 版本,例如说 5.6-rc1。
测试修复周期:接下来的 6-8 周,除非特别的情况,只会接受修复补丁。如果开发者的特性错过了合并窗口,要等待下一个周期。这个周期可能会删除(撤销)之前合并的补丁,或者修复它们,发布更多的 rc 版本,例如 5.6-rc8,再进行测试和修复,逐步迭代达成稳定。
最后一个 rc 版本,会标记为稳定版本发布,例如 5.6。稳定版本会提交给稳定版本维护团队,Linus 又开始了新的合并窗口。
稳定维护团队会把收到的 5.6 版本理解为 5.6.0 版本,稳定维护团队会合并一些修复,发布 5.6.z 版本。稳定维护合并的补丁,首先要有合理的理由,一般要说明重要性,其次要已经合并到了下一个开发周期的主线中,这些 z 版本力求比新的版本更稳定,而不会引入新的特性支持。一个内核版本的维护最终会在一个 z 版本,例如 5.6.12 停止,最终版本要么是没有再发现 BUG 了,或者没有人关心有没有 BUG 了,或者单纯是稳定团队没有精力维护它了。
一般版本在稳定团队手中会维护几个月,而有些版本人为指定为长期支持版本。
信任链条 ¶
只有 Linus 才能合并代码到主线。但 Linus 个人没有能力(任何人都没法有这种能力)审阅和选择每一个补丁。Linux 系统划分为多个子模块,子模块有一个独立的维护者,他们每个人维护自己的分支,每个人接受不同开发者的补丁,最终最总成一列补丁发给 Linus。
由于 Linus 信任这些维护者,因此基本上相信这些补丁是好补丁,但还是会在关键问题上审阅和讨论。在这个结构里面,子系统的维护者就像 Linus 的下级节点,当然这些下级节点也会有下级节点,这就形成了信任链条。
整体上所有的信任链条加起来是一颗以 Linus 为顶点的树。
在 webfs 上挂载 luks 环形设备 ¶
安装 ¶
将某种 webfs 挂载到 /mnt/webfs,创建一个空洞文件:
mkdir /mnt/webfs/vdisk dd if=/dev/null of=/mnt/webfs/vdisk/vd0.img count=0 seek=128GB
将此文件用环形设备映射为设备:
sudo losetup -fP /mnt/webfs/vdisk/vd0.img sudo losetup -a
查看挂载为 vd0.img 的设备,例如是 /dev/loop0。初始化 luks 加密:
sudo cryptsetup luksFormat /dev/loop0
打开设备,给打开的设备起名叫 vdisk0:
sudo cryptsetup open /dev/loop0 vdisk0
设备会映射到 /dev/mapper/vdisk0。为映射后的设备初始化文件系统:
sudo mkfs.ext4 /dev/mapper/vdisk0
挂载映射后的设备:
sudo /dev/mapper/vdisk0 /mnt/vdisk/vd0
总结:
- 镜像文件在 /mnt/webfs/vdisk/vd0.img。
- 映射镜像文件的环形设备在 /dev/loop0。
- 环形设备的内容被 luks 加密,解密后的设备映射在 /dev/mapper/vdisk0。
- 明文设备挂载到 /mnt/vdisk/vd0。
卸载 ¶
卸载时先 umount 挂载点:
sudo umount /mnt/vdisk/vd0
再关闭 luks 设备:
sudo cryptsetup close /dev/mapper/vdisk0
释放环形设备:
sudo losetup -d /dev/loop0
这就完成了卸载。
重新挂载 ¶
重新挂载的过程跟安装相差不多,只是不用再 luksFormat 和 mkfs 了。
霞鹜文楷 ¶
颜值很高,而且制作了等宽字体。
micro 编辑器 ¶
micro 是个单模态编辑器。在写需要很多中文字的文档的时候,单模态更方便一些,micro 现在是我的编辑器的补充。
- C-s 保存。
- C-q 退出。
- C-e 运行命令。
- 命令 set softwarp true 设置长句换行。
- 设置颜色主题 set colorscheme dukelight-tc,dukelight 是一个亮色主题。
Github 发布的 monaspace 字体 ¶
Github 发布的编程字体:An innovative superfamily of fonts for code。
网站:https://monaspace.githubnext.com
Texture healing ¶
不同英文字母的自然宽度是不一样的,例如 mw 应该占据更多的宽度,同时 il 应该更窄,但是为了等宽这些字体都会变形。Texture Healing 就是在 m 和 i 站在一起的时候,m 会渲染更宽,i 会更窄,但是整体还是保持等宽。
变体 ¶
monaspace 字体一次发布了许多变体。它们用稀有气体的名字命名:
- Neon:Ne 氖
- Argon:Ar 氩
- Krypton:Kr 氪
- Xenon: Xe 氙
- Radon:Rn 氡
我并不很了解字体设计的术语,但是这些字体按照元素顺序表现出一些规律,Ne 是无衬线的,越靠近 Rn,「修饰」就越多,Rn 则是一种手写体。
标记清除算法 ¶
https://www.geeksforgeeks.org/mark-and-sweep-garbage-collection-algorithm/
数据结构 ¶
此 gc 算法管理的对象都需要额外一位标记是否可达,在对象创建时标记位都是 0。同时需要维护对象之间的引用关系。
即每个对象,如果用 obj 表示,可以访问和修改 obj.mark_bit,还可以访问 obj.refered_objects,表示所有可以被此 obj 访问的对象。
算法基于如下的逻辑:如果一个对象不可以直接或者间接引用到,则对象可以被回收清理。为了方便遍历,一般还会放置一个顶层对象,指向所有的可以直接访问的对象。
例如伪代码:
func get_map(k, v)
return {k, v}
end
let c = None
let b = 0
begin
a = 1
c = get_map(a, b)
end
# @1
在 @1 位置,b 和 c 可以直接访问。但 a 已经超出了作用域,从而不能直接引用。不过 c 是可以间接引用到 a 的。
root | \ b c # 直接访问 | \ b a # 间接访问
为了统一图结构,可以构建一个 root 节点,指向 b 和 c。在实际实现中,一般都存在类似 root 的结构,不需要专门来做,例如说当前的符号到值的绑定表。
当然,间接访问可以是多层的间接访问,c.b 又可能引用某个对象。所有这些加起来,就可以形成一张有向图。从上面的简单例子就可以看出,这个图可以是有环的。
标记阶段 ¶
标记阶段只需要一个带标记的深度优先搜索,将所有可以直接或者间接引用的对象的 mark-bit 都标记成 1。
func mark(node) if node.mark_bit == 1 return end node.mark_bit = 1 for node in node.refered_objects mask(node) end end
此时所有 mark-bit 是 1 的对象,就是不能回收的对象。
清除阶段 ¶
再遍历一次所有目标:
- 将 mark-bit 是 0 的对象清除;
- 将 mark-bit 是 1 的对象的 mark-bit 设为 0。
第二部分保证了 mark-and-sweep 算法执行完成之后,所有对象的 mark-bit 还是 0。这样下次的 mark-and-sweep 的标记阶段就可以再标记所有可达的对象了。
优劣 ¶
它有单独的 gc 阶段,跟程序运行的过程不会交叠在一起。
程序运行; gc; 程序运行;
这在期待低延迟的任务中,某个任务可能会因为触发了 gc 而产生很大的延迟。但如果不在意延迟,重点关注吞吐量,不需要在执行阶段进行 gc 动作的这个算法就是不错的。
另一个问题是内存碎片问题,它不对内存块做任何时间上的假设,因此很大可能会随着逐渐使用,堆内存变得细碎不连续,导致程序越来越难以申请内存。需要配合内存碎片整理算法来整理内存碎片。
Allocate ¶
从上一节看出,mark and sweep 过程之后 gc 子系统会持有一个空闲列表(freelist),包含回收掉的内存块。为了利用 freelist 中的内存,需要一个分配算法。
func allocate(freelist, size) let block = find_block(freelist) if block == :null return null end remove(freelist, block) first, second := split_block(block, size) if second != :null insert(freelist, second) end return first.body end
block := find_block(free_list, size)是算法的核心。
有三种常见的策略:
- first fit:找到第一块尺寸大于等于 size 的内存块。
- best fit: 找到最小的,尺寸大于等于 size 的内存块。
- worst fit:找到最大的一块,如果尺寸大于等于 size 则使用它。
不论怎样的查找策略,都会让剩下的内存块愈来愈小。因此我们需要把 freelist 中的(连续的)小内存块 合并成大内存块。合并内存块在 sweep 过程实现。
复习一下 sweep 的算法:
func sweep(heap_start, heap_end, freelist) p := heap_start while p < heap_end if p.mark == :t p.mark = :f else p.next = freelist freelist = p end p += p.size end end
合并发生在 p 插入 freelist 头部的过程中:
p.next = freelist freelist = p
如果 freelist 的头部内存跟 p 连续,将两块合并。
if p == freelist + freelist.size freelist.size += p.size else p.next = freelist freelist = p end
自豪版本规范 ¶
https://mastodon.online/@nikitonsky/113691789641950263
- 版本号码由 x.y.z 组成
- 如果你发布了自己感觉自豪的特性,就增加 x 版本(proud version)。
- 如果一般般,就增加 y 版本(default version)。
- 如果你对某个地方感到羞耻,就增加 z 版本(shame version)。
Reader Monad ¶
https://engineering.dollarshaveclub.com/the-reader-monad-example-motivation-542c54ccfaa8
考虑从顶层传入 email,构造一颗 DOM 树的过程,中间的某个组件可能不直接使用 email 这个参数,但是某个下层会使用,这个组件就需要负责传递 email 给下层。
page :: Email -> Html
page email =
div
[ topNav
, content email ]
如果使用 Reader Monad:
page :: Reader Email Html
page = do
content' <- content
return $ div
[ topNav
, content' ]
注意这里 email 的获取和传递都消失了。而在需要使用 email 的场景,使用 ask 创建 Monad,包裹 email 的内容。
widget :: Reader Email Html
widget = do
email <- ask
return $ div
[ p [ "Hey " ++ email ++ ", we've got a great offer for you!" ] ]
sphinx 文档工具 ¶
sphinx 基于 rst:
pipx install sphinx sphinx-quickstart doc_dir cd doc_dir make html && open _build/html/index.html make man make info
sphinx-quick 在 doc_dir 里生成了 Makefile 和 conf.py 等内容,还有文档的入口 index.rst。index.rst 里面有个 doctree 节,在里面列出文档树的文档页的名字(不用包含 .rst 后缀)。然后使用 make 编译文档树即可。
引用文档 ¶
使用:
:doc:`xxx`
来引用 xxx 文档。
esbonio ¶
esbonio 是社区开发的 rst 的 language-server:
pipx install esbonio
深度学习库 torch ¶
收缩和扩展维度:Tensor.squeeze 和 Tensor.unsqueeze ¶
一个张量的如果某个维度的长度是 1,那么删除这个维度并不会修改数据的实际内存布局。反过来,在维度中插入一个长度是 1 的维度也不改变内存布局。
squeeze 删除张量中长度是 1 的维度,如果指定了 dim= 参数,则删除指定维度。跟其他 torch 函数一样,张量的维度索引是从高维度到低维度视作一个元组时的元组下标。
unsqueeze(dim) 是在 dim 指定的维度插入长度是 1 的维度。例如尺寸是 (2, 3) 的张量进行 unsqueeze(1) 之后,得到的张量尺寸是 (2, 1, 3)。
扩展尺寸 - Tensor.expand ¶
d.expand(*shape) 将 d 扩展为 shape 描述的尺寸,shape 满足:
- shape 和 d.shape 的维度数目应该相等。
- 对长度不是 1 的维度,shape[i] 应该等于 d.shape[i]。
- 对长度是 1 的维度,shape[i] 可以大于等于 1。
上述那些长度是 1 的维度叫做「扩展维度」。返回的张量像是原来数据的一个映射,这个张量访问一个值时,不论扩展维度上的索引是多少,都视作是 0。换言之,这个维度上的所有值都是一样的。
例如 (100, 200, 1) 的张量使用 expand(100, 200, 10) 扩展,得到 (100, 200, 10) 的张量,访问 [98, 120, 9] 的时候就像在访问原来张量的 [98, 120, 0]。
Vanilla OS ¶
Vanilla Os 是个不可变的 Linux 发行版。
Vanilla 这个词是香子兰或者香草冰激凌的意思。发音接近瓦尼拉。
2022 年发布了第一个 alpha 版本。2024 年 7 月发布了大版本 2:Orchid。Orchid 是兰花的意思。
它制作了一个包管理器 apx,用 Golang 写成。
xdg-open 工具的配置 ¶
xdg-open 基于 MIME 归类文件。使用:
xdg-mime query filetype PATH/TO/FILE
查看一个文件的 MIME 类型,例如 text/html。
xdg-open 使用 .desktop 文件来启动应用程序,用:
ls /usr/share/applications/
查看安装了哪些 .desktop 文件,例如你安装了 firefox.desktop,它会启动 firefox 浏览器。
最后将 firefox 和 text/html 绑定到一起:
xdg-mime default firefox.desktop text/html
zellij ¶
是一个终端复用器。我最开始试用它只是因为它的终端颜色表现比 tmux 好。
它默认的快捷键不合我的口味:它们占有了很多前缀按键。非常容易跟其他软件冲突。但是它可以完全配置按键,通过修改配置文件改成类似 tmux 的快捷键方案:所有的快捷 键都前缀 Ctrl B,这样只要 Ctrl B 不跟其他的软件冲突就可以了。能做到这一点也是 zellij 质量良好的表现。zellij 是 Rust 作品。
ZK Note ¶
ZK note contains an unique ID and content and references.
Each ZK note should contains at most one thought.This makes each note not too long.
Add refernece makes notes linked into a graph.A linked graph is a library, and a set of not linked notes are garbage.
https://zettelkasten.de/introduction
zoxide ¶
用 pacman 安装。在 fish 的配置文件里用下面的命令初始化。
zoxide init fish | source
这样就可以用 z 代替 cd 了。基本使用接口都是一样的:
z ~/foo # 进入某个绝对路径 z foo/ # 进入某个相对路径 z .. # 进入上一级目录 z - # 进入上次在的目录
唯一不同的是:
z foo
这样的语法在 cd 上表示进入相对路径,但是 zoxide 表示在 z 的进入历史里查找最高分的目录,进入这个目录。如果只有一个目录匹配到,那就进入这个目录;多个可能的目录的时候,z 会使用启动交互式窗口来让你选择。
参考 ¶
https://github.com/ajeetdsouza/zoxide
基础拓扑 ¶
记号 ¶
采取尽量贴近 Python 语法的记号。
x: Set
x 是集合。
x.subsets()
包含 x 的子集的集合。
a.is_subset_of(b)
a 是 b 的子集。
EmptySet
空集。
s.contains(a)
a 包含在集合 s 中。
inter(a, b, c, ...)
集合的交集。
union(a, b, c, ...)
集合的并集合。
func(a, b, c, *t, ...)
将 a、b、c 和集合 t 中的元素都输入给 func。
a => b
a 推导出 b。
注意跟Python的变量了和函数概念,这里的记号都可以具有无限性。例如集合可以包含无限个元素。又例如「函数」可以有无限多个参数。
举例来说。设u是集合的集合,union(*u)则表示集合中元素的并,这个记号成立不需要u是有限集合。
拓扑空间的定义 ¶
设x: Set,设f是x.subsets()的子集。
如果:
- f.contains(EmptySet) and f.contains(x)
- f.contains(u) and f.contains(v) => f.contains(inter(u, v))
- u.all(ui -> f.contains(ui)) => f.contains(union(*u))
那么把(x, f)称拓扑空间,记作:(x,f): TopSpace。如果f.contains(a),那么称作a是「开集」。根据定义,「开集」都是x的子集,都是f的元素。
实际上f只是标明了哪些是「开集」,一般不需要给f起名。所以我们用记号x.open_sets()表示f,用x.is_open_set(u)表示x.open_sets().contains(f),此时可以直接写x: TopSpace。
对拓扑定义的第二点,由数学归纳法,可以很容易推导出,它等价于:设u是有限个ui集合的序列,则u.all(ui -> f.contains(ui)) => f.contains(inter(*u))。这跟定义的第三点条件几乎是对称的,但注意第三点实际不假设u是有限序列。我们也可以用如下的方法重新描述定义。
- 全集和空集是开集。
- 有限开集的交是开集。
- 无限开集的并是开集。
qutebrowser ¶
存活 ¶
- :help 打开帮助。
- 移动hjkl。
- 切换Tab:JK
- 在浏览链接列表上跳转:HL.
- Zoom:+-
- hint new tab: f在链接上打标签。类似amp的Jump模式。
- hint label links: F在tab上打标签。
参考 ¶
Link https://www.qutebrowser.org/