目录

阅读代码

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:谢谢。

Attachments/ten_of_this.png

在吃什么

稳重:你在吃什么?

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 博客

https://blog.jpeach.org

我发现了这个博客,它每一篇都很短,但是确实是一些值得记录的东西。博客的标题是「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,可以方便地配置和修改配置变量。

复制剪切粘贴

C-y 是粘贴,y 表示 yank(召回)。

C-w(wipe)是剪切选中的部分,M-w 是复制。复制和剪切的区别只有是不是删除原本的内容。所以被绑定到 C-w 和 M-w

放大缩小字体

  1. C-x C-= 放大字体。
  2. C-x C-- 减小字体。
  3. C-x C-0 恢复字体大小。

除了前缀 C-x,跟一般编辑器的习惯差不多。

也可以使用鼠标滚轮。

切换到制定行

M-g g 跳转到指定行。M-g 绑定了一系列移动指令,常用的有:

  1. n:next-error
  2. p:previous-error
  3. 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 的简称。

每个 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.

libgc - C 和 C++ 的垃圾回收器

https://www.hboehm.info/gc/

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

总结:

卸载

卸载时先 umount 挂载点:

sudo umount /mnt/vdisk/vd0

再关闭 luks 设备:

sudo cryptsetup close /dev/mapper/vdisk0

释放环形设备:

sudo losetup -d /dev/loop0

这就完成了卸载。

重新挂载

重新挂载的过程跟安装相差不多,只是不用再 luksFormat 和 mkfs 了。

霞鹜文楷

颜值很高,而且制作了等宽字体。

Attachments/lxgw-wenkai-0.png

micro 编辑器

micro 是个单模态编辑器。在写需要很多中文字的文档的时候,单模态更方便一些,micro 现在是我的编辑器的补充。

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 字体一次发布了许多变体。它们用稀有气体的名字命名:

我并不很了解字体设计的术语,但是这些字体按照元素顺序表现出一些规律,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-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)是算法的核心。

有三种常见的策略:

不论怎样的查找策略,都会让剩下的内存块愈来愈小。因此我们需要把 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

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 满足:

上述那些长度是 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

https://zellij.dev

是一个终端复用器。我最开始试用它只是因为它的终端颜色表现比 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 语法的记号。

  1. x: Set

    x 是集合。

  2. x.subsets()

    包含 x 的子集的集合。

  3. a.is_subset_of(b)

    a 是 b 的子集。

  4. EmptySet

    空集。

  5. s.contains(a)

    a 包含在集合 s 中。

  6. inter(a, b, c, ...)

    集合的交集。

  7. union(a, b, c, ...)

    集合的并集合。

  8. func(a, b, c, *t, ...)

    将 a、b、c 和集合 t 中的元素都输入给 func。

  9. a => b

    a 推导出 b。

注意跟Python的变量了和函数概念,这里的记号都可以具有无限性。例如集合可以包含无限个元素。又例如「函数」可以有无限多个参数。

举例来说。设u是集合的集合,union(*u)则表示集合中元素的并,这个记号成立不需要u是有限集合。

拓扑空间的定义

设x: Set,设f是x.subsets()的子集。

如果:

  1. f.contains(EmptySet) and f.contains(x)
  2. f.contains(u) and f.contains(v) => f.contains(inter(u, v))
  3. 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是有限序列。我们也可以用如下的方法重新描述定义。

  1. 全集和空集是开集。
  2. 有限开集的交是开集。
  3. 无限开集的并是开集。

qutebrowser

存活

参考

Link https://www.qutebrowser.org/