在写yasf项目的过程中遇到这样的需求:为了构造SQL语句,我必须将若干个字符串拼接起来。一个凑合的办法是,定义一个足够大的静态缓冲区,然后使用诸如sprintf
这样的库函数来构造字符串。但是事实上,构造出来的字符串的总长度是没有上限的。因此,若使用固定长度的缓冲区则会带来缓冲区溢出的隐患(C语言最著名的缺陷之一)。
大部分现代的语言的字符串功能都远强于C,字符串拼接更是非常基本的功能。选择C作为开发语言,那就不得不重复造轮子。我总抱着自己能造出更好的轮子的虚幻的指望。经过若干次试验,我得到了一种比较满意的实现方式。于是我把它从yasf项目中抽了出来,做成一个独立的模块,放在GitHub上,命名为ulbuf (__u__ltra __l__ightweight __buf__fer)。
整个实现的代码不足百行,功能也是少之又少。我发现,只有这样才能保证最大的灵活性。
整个库的特点:
简单
API非常简单,共4个函数。其中,2个用于缓冲区的创建和销毁,1个是为了便于使用而提供的便捷函数。因此,最关键的函数只有1个。
API的参数和返回值的形式是经过仔细设计的。
灵活
在C语言中,用户常常通过增/减指针来表示向字符串中增添/删除字符。只要预留了空间,ulbuf仍允许他们这样做。
在最初的实现中,ulbuf还保存了“缓冲区中已含有的字节数”这一个信息。并且只能向缓冲区中追加内容。对于yasf项目而言,已经足够。
现在的实现中,ulbuf只负责管理缓冲区的容量(最多能容纳的字节数),不储存已含有的字节数信息。既能简化实现,又能保证使用的灵活性。这可能是体现“少即是多”的一个例子。
鲁棒
当用户提示ulbuf需要向缓冲区中写入n个字符时,ulbuf会在第n+1个字符的位置写入字符'\0'
。因此,缓冲区的内容永远是合法的C字符串。这样,使用C标准库操作缓冲区永远不会发生内存越界的错误。
协调
ulbuf创建的缓冲区是char *
类型,可以把它当作正常的字符串,提供给诸如strlen
、strchr
这类标准库函数。
只要预留了空间,那么也可以把缓冲区作为输出参数,提供给诸如strcpy
、sprintf
这类函数使用也是完全没有问题的。这类函数会在第n+1个字符的位置添上一个字符'\0'
,仅仅是把ulbuf已经写入的'\0'
覆写了一次,而没有发生内存越界访问。
高效
在我的平台上测试重复追加字符串的操作,ulbuf的运行速度略快于C++ STL的string
类。
因为STL通常是仔细编写的、深度优化的运行库,可见ulbuf的运行效率是很高的。(欲知具体情况,可以编译并运行代码仓库中的example.cc
程序。)
继续阅读 → yasf 是我正在写的一个SQLite的图形前端。目前写好了大体的图形界面。能够读取SQLite数据库(而不能编辑)。
以下为未成品的截图。图中的对话框用于创建新的索引(仅仅是界面)。
继续阅读 → 前天下午打牌,触景生情想起了一个概率论的问题:3个人打一副扑克(A23456789 10 JQK各4张+大小王=54张牌),随机发牌,每人拿18张。设随机变量X表示三个手中的炸弹(指4张点数相同的牌)的总数,求X的分布律。
这样的问题理论上应该可以使用古典概型来求解。但是我想了一会之后就放弃了。为了对这个问题有一个大概的了解,我写了一段程序模拟洗牌和发牌的过程并且统计其中的炸弹个数。
从程序的输出中我得到了一个有趣的结果。下面是某一百万次试验中得到的统计结果:
685121
259261
49135
5935
520
28
0
从上到下依次是X=0,1,2……的频数。6个炸弹以上的情况没有出现。
下面是条形统计图,可见,随着炸弹数增加,频数迅速减小。概率最大的事件是没有炸弹。炸弹个数不超过1个的频率高达94%,这和我平时打牌的常识相符合。
继续阅读 → Wercker 现在支持基于Docker 的自动部署。我不是很清楚Docker的运作原理。就我现在的理解,Docker是一个虚拟机快照,其中已经配置好了用户所需要的软件。每次运行时,就是解包这个虚拟机然后执行任务。
之前因为ArjenSchwarz/wercker-step-hugo-build 这个脚本不支持基于Docker的自动构建,所以我还在使用Wercker的旧的构建模式。
问题在于,在目前基于Docker的构建模式中,默认的虚拟机debian
只是一个空壳,像git和wget这样的软件统统都没有安装。
在构建的时候,脚本需要使用wget下载hugo程序。我曾试过在构建之前执行
但是似乎找不到这个软件包。今天我发现构建脚本更新了,不知道作者是不是也遇到了同样的问题,所以改而使用curl。
我还需要安装git。这个过程会下载很多软件包并安装。构建的时候做一次,部署的时候又做一次,浪费时间和计算资源,我觉得很不合理。
我觉得合理的做法是,在Docker的虚拟机中预先安装好git和hugo,这样每次构建的时候只需解包虚拟机并且运行就可以了,并不需要安装额外的软件包。只是我现在还不知道怎样实现这样的Docker功能。
目前最好的结果是,我找到了yesops/git 这个Docker,其中已经安装好了git和curl。这样我就不用再去劳烦apt-get
了,应该可以省去不少重复劳动,虽然不是人力劳动而是自动化的机器劳动。
继续阅读 → 周四将要进行《概率论与数理统计》的期末考试。为了表示纪念,研究一个和概率论相关的问题。
连续抛掷一枚硬币,直到出现连续的两个正面为止。问:抛掷次数的数学期望是多少?
继续阅读 → 明天将要进行《模拟电子线路》课程的期末考试。今日特地记一道题目以作纪念。
问题是这样的:反馈放大电路如图所示。请
判断反馈的组态; 估算闭环增益\(A_f\)。 已知:\(R_s\)的值已经很高,可以略去。
继续阅读 → 又是一年高考日。今年江苏省的高考数学卷中,有一道填空题是这样的:
设向量\(\vec{a_k} = \left(\cos{k\pi\over6}, \cos{k\pi\over6} + \sin{k\pi\over6}\right)\),则\[\sum_{k=0}^{11} \vec{a_k}\cdot\vec{a_{k+1}}={?}\]
这样的题似乎只需要你足够地胆大心细总是可以计算出来。
继续阅读 → 今天根据Hugo网站上的说明 ,把Hugo的构建和部署工作交给Wercker 自动完成。
要把一个Hugo站点发布在GitHub Pages 服务上,需要在GitHub创建两个repo:
源 repo ,用于存放站点源文件。目标 repo ,用于存放Hugo生成的静态站点。这个repo会被GitHub Pages作为www站点展示。以前我是按照另一篇说明 上的流程操作的,但是命令行的操作并不方便。具体的办法是:在本机运行hugo,生成静态站点。然后把源文件的改动推送到源repo里,再把生成的public
目录用Git的submodule或者subtree功能推送到目标repo里边。
使用Wercker自动部署,则工作要轻松许多:当源文件的改动推送到源repo里边以后,Wercker会自动在它的服务器启动构建任务。当master
分支成功构建后,就自动地把Hugo生成的静态站点推送到目标repo中。
所以目标repo的事情我就不用管了。如果想要改变部署目标(例如发布到其他的托管主机),只需要修改几条Wercker的设置,非常灵活。
使用自动化部署还给我的编辑流程提供了方便:我现在可以直接在GitHub的web界面上编辑内容。保存以后的更改也会自动地推送到GitHub Page上。另一方面,我可以充分地利用Git的分支功能:如果文章写到一半,可以保存到源repo的draft
分支(或者staging
分支,看看哪个名字好听而已)里面,而不是保存在本机。这允许我在多个位置进行持续地编辑,才算是有效地体现了GitHub的作用。
Wercker看起来不是很成熟。我读了它的文档,但是看起来十分地简略。最近它推出了一种新的构建模式,使用了Docker 的技术。只不过我搞了半天也没有搞定在这种模式下怎么进行自动构建和部署。所以目前仍然运行在旧的模式中。
也有人用的是Travis CI ,一个看起来更加成熟的持续集成工具,在GitHub的项目中非常流行。不过Wercker也有它的好处:用户可以创建自己的构建环境(称为box)和脚本(称为step),然后发布出去和其他人共享。我用来构建和部署Hugo的脚本,就是直接拿了别人的来用的,可以说是非常方便,不需要动什么脑筋。鉴于Wercker的解决方案已经足够方便,我现在就先不折腾了。
继续阅读 → 下面这段Python 3代码可以产生圆周率π的任意多个有效数字。
def pidigits ():
q, r, t, u, i = 1 , 180 , 60 , 168 , 2
while True :
y = r // t
yield y
q, r, t, u, i = 10 * q* i * (2 * i- 1 ), 10 * u * (q * (5 * i- 2 ) + r - y* t), t* u, u + 54 * (i+ 1 ), i+ 1
这是一个永不终止的生成器,每次产生一个十进制的有效数字,依次是3, 1, 4, 1, 5, 9, 2, 6, …
继续阅读 → 这学期我的课程中有两门叫作《模拟电子线路》和《数字逻辑电路》。其中介绍的各种电子电路,有的相当复杂,手工分析难而易错。进行电子电路实验可以获得准确的结果,但是对实验条件有很多要求,例如需要准备直流稳压电源、信号产生器、万用表和示波器等电子仪器,以及各种电路元件。借助计算机辅助分析,也就是电路仿真,可以快速获得准确的结果,因此用处很大。
Berkeley的SPICE 程序是大多数电路仿真软件的核心。在Windows平台上,有很多基于SPICE的商业软件,它们往往是附带了一套图形化电路原理图编辑器和其他的功能。比较出名的有OrCAD (PSpice) , Multisim , LTSpice 等。
Multisim是National Instruments的产品。它比较特别,可以进行实时的仿真。你可以在电路中放置开关,然后在仿真过程中切换开关的状态。如果电路中有七段数码管,那么你也可以观察到数字的实时变化。此外,Multisim具有大量的虚拟仪器,如示波器、瓦特计、BJT分析仪等。它们的操作界面模拟真实的电子仪器。
Multisim没有免费版本,学生版不提供给中国用户。贸泽电子提供了一个免费版本的Multisim ,但是没有分析(analysis)功能。
LTSpice是Linear Technology提供的免费的电路仿真软件。它的原理图界面比较丑,但是其他功能都还不错。瞬态分析、交流小信号分析、直流扫描分析等各种功能都齐全,只是没有Multisim的实时仿真功能,如果要了解电路运行情况随时间的变化,只能依靠瞬态分析来实现。
手机上也有电路仿真软件。在此我推荐使用EveryCircuit 。它的界面非常友好,也具有实时仿真的功能,并且在我看来,比Multisim更加好用。主要特点是:
用户界面很美观,自动布线的功能也很好用。 可以在仿真过程中动态调节元件参数(电阻、电容大小,电压源电压等)。 可以在原理图中直接读出电路状态,包括每个节点的电位(如果电位在变化,则以波形表示)、每个支路的电流(以动画的方式体现出电流的方向和相对大小,如果是恒定电流则还会标出具体数值)、电容的带电量、电灯泡的功率(以亮度表示)、MOSFET的沟道状态等。 示波器非常好用,无需像Multisim那样将示波器连入电路中,而只需指定要观察的量即可,而且仿真速度相对较慢,容易观察电路的变化。 在仿真过程中可以通过摇晃手机来加入噪声信号。某些非稳态电路可能需要噪声才能启动。 最新的版本中,增加了参数图形、频域分析的功能,还增加了几个集成元件(计数器、7段数码管、555定时器)。 **更新:**EveryCircuit的作者Paul Falstad还写了一个JavaScript版本的电路模拟器 。界面比EveryCircuit稍微粗糙一点,但是功能更加全面。(对浏览器的性能是个考验。)
继续阅读 →