米米的博客

做了一点微小的工作

Seen here is the Norwich City Council’s first computer, being delivered to the City Treasurer’s Department in Bethel Street, Norwich in 1957. The City of Norwich, and its forward-thinking Treasurer, Mr A.J. Barnard, were pioneers in the application of computer technology to the work of UK local authorities and businesses. In 1953-4, Mr Barnard and his team began looking for an electronic system to handle its rates and payroll. They began discussions with Elliott Brothers of London in 1955, and the City Council ordered the first Elliott 405 computer from them in January 1956. It was delivered to City Hall in February 1957 and became operational in April 1957. The event was celebrated by a demonstration of the machine in front of the Lord Mayor of Norwich and the press on 3 April 1957. (Norfolk Record Office, ACC 2005/170)

Below is a picture of the new $5 Raspberry Pi Zero at the same location. The Raspberry Pi is a tiny and affordable computer, designed and built in the UK, that you can use to learn programming through fun, practical projects. I own 2 older models.

Wonders never cease.


本文转载自:UK Computing: Elliott 405 (1957) vs $5 Raspberry Pi Zero (2015) by Ben Ward • Findery
图片来源:
Norfolk Record Office
BlazePress — 58 years later.

2019 年 8 月,Hexo 的 NexT 主题正式加入 PJAX 功能。最初的 PR 一共包含 35 个 commit,约 600 行代码改动。不过这个数字有些夸张,其中约有 200 行是在适配 PJAX 过程中,发现一些插件对于 PJAX 不友好,顺手修改了。此后根据收到的用户反馈,又用了不下十个 PR,修复了 PJAX 中全部已知问题。

由于在同一时期,NexT 已有弃用 jQuery 的计划,因此没有采用广泛使用的 jquery-pjax,而是用了一个较为小众的库:moOx/pjax,并且根据需求修改了一些功能。
适配 PJAX 的要点总结来说有以下几条。

将 PJAX 刷新区域分离

对于一个网站中的全部网页,往往具有一些在每个页面中相同的部分,以及在每个页面中不同的部分。相同部分可能包括 <head> 中加载的 <script>,以及导航栏、页脚等组件。不同部分则是页面的正文部分等。PJAX 的刷新区域应当只包含这些不同的部分。因此,将页面中的内容进行分类非常重要。对于使用各种模版引擎生成的网站,这一操作往往并不复杂,并且还有些额外的好处,例如可以对相同的内容进行缓存,加快网站的生成时间。

选择器

一般 PJAX 会根据设定的选择器来确定刷新页面中的哪些区域。这时需要确保选择器的唯一性。例如,如果文章内容由 Markdown 渲染生成,那么可能会在各个 Heading 处产生带有 id 的元素。需要避免它们与 PJAX 刷新区域的 id 冲突,那么可以考虑使用 class 进行选择。

一致性

另一个问题是 PJAX 刷新过程中,页面状态的一致性。NexT 主题在适配 PJAX 时就遇到了这个问题。在侧边栏中有两个区域:文章目录和站点概览。与之对应的是两个 <button>,点击一个 <button> 便会显示其对应的区域,隐藏另一个;这一过程由 className 控制。但有一些页面,例如首页,是没有文章目录的。如果从一个文章页面通过 PJAX 刷新回到首页,就可能导致侧边栏中的两个区域都被隐藏,看上去没有内容,并且 <button> 的状态与侧栏中两个区域的状态不符合。解决方案有两种:

  • 在每次 PJAX 刷新后,根据情况「点击」其中一个按钮,确保侧边栏显示正确;
  • 将控制显示的 className 移动到刷新区域外,例如设置为 <body>className

第二种方案可以减少 DOM reflow,保证 PJAX 刷新时渲染一步到位。

重新加载脚本

一些常见的 PJAX 插件都自带了重新加载脚本的功能,但为了精确地控制,这一部分可以考虑自行实现。

页面中的脚本大致可以分为三类:

  1. 在每个页面中都存在,但只需要加载一次,重复加载反而有可能导致问题(例如音乐播放器,看板娘,背景动画等)
  2. 在每个页面中都存在,并且 PJAX 刷新时需要重新加载(例如访问量统计,FancyBox 等)
  3. 仅在部分页面中存在,不使用时没有必要加载(例如 MathJax,网站评论区等)

第一类脚本

对于第一类脚本,将其放在 PJAX 刷新区域之外即可,不需要进行任何其它的处理。这里额外提一下 addEventListener 的问题。一般的 PJAX 插件会给出 PJAX 刷新完成时的事件,需要正确的使用。而 DOMContentLoadedload 事件都只会触发一次。
此外,应当避免重复注册相同的 addEventListener。如果每个新页面都使用匿名的回调函数注册一个相同的事件,最后一个事件将触发所有的回调函数,场面一定非常壮观。要避免这个问题,可以:

  • 不使用匿名函数作为回调函数,而是将其封装,通过 function 进行声明,然后将其作为 addEventListener 的参数,这可以保证其只触发一次;
  • 或者在必要时使用 removeEventListener

第二类脚本

对于第二类脚本,需要在 PJAX 刷新完成后,重新执行。最简单的方法是复制这个脚本的属性和内容,然后将其移除,再通过 replaceChild 方法将复制插入到原来的位置。
为了使 PJAX 区分第一类和第二类脚本,避免错误地加载,NexT 使用的方法是为全部第二类脚本加上 data-pjax 属性。这样,重新加载脚本的代码如下所示

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
const elements = document.querySelectorAll('script[data-pjax]');
elements.forEach(function(element) {
const code = element.text || element.textContent || element.innerHTML || '';
const script = document.createElement('script');
if (element.id) {
script.id = element.id;
}
if (element.className) {
script.className = element.className;
}
if (element.type) {
script.type = element.type;
}
if (element.src) {
script.src = element.src;
// Force synchronous loading of peripheral JS.
script.async = false;
}
if (element.dataset.pjax !== undefined) {
script.dataset.pjax = '';
}
if (code !== '') {
script.appendChild(document.createTextNode(code));
}
element.parentNode.replaceChild(script, element);
});

复制 type 是为了避免执行非 JS 脚本,例如 MathJax 的配置。复制 dataset.pjax 则是为了确保在多次的 PJAX 刷新中,script[data-pjax] 选择器始终有效。

第三类脚本

比较麻烦的是第三类脚本。对于那些可以重复进行初始化的插件而言,其「本体」只需要加载一次,此后的 PJAX 刷新过程只需要再次进行初始化。典型的例子是 Valine 和 MathJax。对于 Valine 而言,每个新页面中,重新执行 new Valine() 即可。而对于 MathJax,初始化也是类似的。无论如何,最佳的实现应当保证:

  • 如果用户通过 PJAX 浏览的页面中,都不包含数学公式,那么无需加载 MathJax,减少网络请求;
  • 如果用户浏览到了第一个包含数学公式的页面,那么需要加载 MathJax;
  • 在此后用户浏览的所有页面中,如果包含数学公式,那么只需要调用以下方法,重新进行渲染
    1
    2
    3
    4
    MathJax.startup.document.state(0);
    MathJax.typesetClear();
    MathJax.texReset();
    MathJax.typesetPromise();

当然,你也可以通过其它方式实现同样的效果。如果要省事的话,可以将第三类脚本按照第二类的方式进行处理。唯一的不同是需要将其放置在 PJAX 刷新区域之内,否则可能造成额外的网络请求和资源消耗。

总结

总而言之,PJAX 适配过程中最大的难点是重新加载脚本的问题。想要做到完美而没有任何疏忽和遗漏无疑是困难的。这依赖于大量的测试来检验系统的鲁棒性。

Proof by intimidation Trivial!

Proof by cumbersome notation The theorem follows immediately from the fact that when .

Proof by inaccessible literature The theorem is an easy corollary of a result proven in a hand-written note handed out during a lecture by the Yugoslavian Mathematical Society in 1973.

Proof by ghost reference The proof my be found on page 478 in a textbook which turns out to have 396 pages.

Circular argument Proposition 5.18 in [BL] is an easy corollary of Theorem 7.18 in [C], which is again based on Corollary 2.14 in [K]. This, on the other hand, is derived with reference to Proposition 5.18 in [BL].

Proof by authority My good colleague Andrew said he thought he might have come up with a proof of this a few years ago...

Internet reference For those interested, the result is shown on the web page of this book. Which unfortunately doesn't exist any more.

Proof by avoidance Chapter 3: The proof of this is delayed until Chapter 7 when we have developed the theory even further.
Chapter 7: To make things easy, we only prove it for the case , but the general case in handled in Appendix C.
Appendix C: The formal proof is beyond the scope of this book, but of course, our intuition knows this to be true.

本文转载自:Mathematicx - Facebook

你相信吗,仅仅利用一张日落的照片,你就能得出地球的半径大小!Princeton 大学的 Robert Vanderbei 在 2008 年的一篇论文《The Earth is Not Flat——Can a photo of the sunset over Lake Michigan reveal the shape of our planet ?》中对一张摄于密歇根湖的日落照片进行了分析,不但证实了地球是圆的,还依据照片上的内容对地球半径进行了估算。

事情的起因就是上面这张很平常的日落照片,以及这样一个大家平时并没有太在意的问题:太阳露出水面的部分应该是一个标准的弓形,但为什么在日出日落时,我们所看到的太阳是一个橄榄球一样的形状?大家或许会很快想到,发光体的下半部分其实是日光反射在水面上造成的。随之产生的是另一个问题:为什么它的下半部分要比上半部分小一些呢?

这是因为 —— 想到这个问题的答案并不容易 —— 地球是圆的。下图就是人站在地球上看日出的一个比例夸张版示意图,其中 O 为地球的中心,A 为人眼的位置,AB 为视平线,B 点为水天交界处。由于太阳距离我们相当遥远,因此我们把太阳光看作是一束理想的平行光线。我们把直接射入人眼的太阳光与 AB 的夹角记为,把经过水面上的一点 C 反射进入人眼的光线与 AB 的夹角记为。从图上可见,视角小,也就是说太阳在水面上的镜像比本身要小一些。

究竟比小多少呢?对照片进行精确地测量,可知太阳的直径相当于照片中的 317 个像素,而露出水面的部分高 69 像素,水中的倒影则只有 29 像素。众所周知太阳的视直径(看太阳的视角)为,因此我们就得到

如果再已知人眼(或者说相机)离水面的垂直距离 h 为 1.8 米,那么根据这些数据我们就足以估算出地球的半径了。不妨把记为,把记为,把人眼到水天相接处的距离 AB 记为 D,把人眼到反射点的距离 AC 记为 d,入射角和反射角记为,最后用 r 来表示地球半径。那么问题来了:试给出 r 的表达式并计算其值。

阅读全文 »
0%