1700页数学笔记火了!全程敲代码,硬核小哥教你上手LaTeX+Vim(附代码)

2024-04-13 23:58

本文主要是介绍1700页数学笔记火了!全程敲代码,硬核小哥教你上手LaTeX+Vim(附代码),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

640?wx_fmt=png

本文经AI新媒体量子位(公众号ID:qbitai )授权转载,转载请联系出处。

本文约4000字建议20分钟

本文为你整理2019年最值得关注的34个Python开源项目。


又出现一位“神仙”本科生!


数学课上,全程键盘手打1700页笔记。


速度紧追老师板书,公式、图形一个不落。


效果?请看下图:


640?wx_fmt=gif


不仅排版媲美教科书,而且还能够批注,检索关键词……


笔记被他Po到网上之后,便引来大量围观。


不到一天,相关推文就已经有2000多赞,Hacker News论坛上盖了200多楼。


甚至有网友评论称:“你就是我们需要的英雄!”


他是怎么做到的呢?秘密武器就是:LaTeX+Vim!


640?wx_fmt=gif


这位来自欧洲的小哥非常强烈安利Vim文本编辑器,他说:


用LaTeX写数学公式,我选Vim编辑器。它强大、通用、可扩展性很强。只要是基于文本的任务我都用它,写代码、编辑LaTeX、写markdown都是。虽然入门阶段的学习曲线超级陡峭,但只要掌握了基本的操作方式,就会欲罢不能。


下面就让我们看一下他完成这一壮举的具体流程,文中提到的工具下载地址,我们都附在了最后。


快速上手教程


我们先看看小哥的工作环境配置。


他用Vim编辑LaTeX的场景,就像下面这样:


640?wx_fmt=png


左边是Vim,右边是pdf阅读器Zathura,它也有类似Vim的快捷键。


小哥用的操作系统是Ubuntu,使用bspwm作为窗口管理器。在Vim中,使用的LaTex插件是vimtex,它有语法高亮显示、目录视图、同步对象等功能。


然后,使用vim-plug做如下配置:


 

Plug 'lervag/vimtex'
let g:tex_flavor='latex'
let g:vimtex_view_method='zathura'
let g:vimtex_quickfix_mode=0
set conceallevel=1
let g:tex_conceal='abdmg'


最后两行控制的是“隐藏”功能。开启了这个功能,除了你光标所在的那一行之外,文本里夹杂的LaTeX代码就都会隐藏或者替换成其他符号。


比如说在下面动图里,隐藏了[,],$之后,没有了它们的干扰,整个文档就更易读。这个功能还会用∩替代\bigcap,∈替代\in等等。


640?wx_fmt=gif


设置完成,接下来就到了整个教程的精华所在:


用LaTeX记笔记,怎么才能像老师写板书一样快?


这就是片段(snippets)发挥作用地方了。


片段!片段是什么?


片段是一小段可复用的文本,由其他文本触发。


例如,输入sign,再按下Tab键,这个单词就会自动扩展为一段签名:


640?wx_fmt=gif


片段也可以是动态的:输入today并按下Tab键,它就会变成当前的日期。


640?wx_fmt=gif


而输入box按Tab,就会出现一个框,还会随着输入文字自动变大。


640?wx_fmt=gif


片段,甚至可以嵌套在另一个片段里用:


640?wx_fmt=gif


怎么创建片段?使用UltiSnips


管理片段的插件UltiSnips,小哥是这样配置的:


Plug 'sirver/ultisnips'

 

let g:UltiSnipsExpandTrigger = '<tab>'
let g:UltiSnipsJumpForwardTrigger = '<tab>'
let g:UltiSnipsJumpBackwardTrigger = '<s-tab>'


关于sign片段的代码如下:


 

snippet sign "Signature"
Yours sincerely,

Gilles Castel
endsnippet


对于动态的片段,你可以将代码放在``之间, 在片段扩展的时候,就会运行。下面的例子,就是用 bash 格式化当前日期:date+%f。


 

snippet today "Date"
`date +%F`
endsnippet


你也可以在!p ...代码块里使用Python,比如上面box片段的代码就是这样的:


 

snippet box "Box"
`!p snip.rv = '┌' + '─' * (len(t[1]) + 2) + '┐'`
│ $1 │
`!p snip.rv = '└' + '─' * (len(t[1]) + 2) + '┘'`
$0
endsnippet


这些 Python 代码块将被变量 snip.rv 的值替换。在这些代码块中,你可以访问代码段的当前状态,例如t[1]包含第一个制表位,fn是当前文件名等等。


LaTex片段


使用片段编写LaTeX,要比纯手工编写快得多。特别有些非常复杂的片段能帮你大大节约时间,有效防止抓狂。


下面是一些非常有用且容易上手的片段:


环境


想插入一个环境,只需要在一行的开头输入beg。然后键入环境的名称,这个名称在\end{}命令中也是一样。按下Tab键,就能够将光标放置在新创建的环境中。


640?wx_fmt=gif


这个片段的代码如下:


snippet beg "begin{} / end{}" bA

 

\begin{$1}
    $0
\end{$1}
endsnippet


其中,b表示这个片段只会在代码行的开头展开,A代表自动展开,也就是说不用按Tab键了。制表位(Tab stop)——也就是你可以通过按Tab 和Shift+Tab跳转到的位置——用$1、 $2、......来表示,最后一个用$0。


行内和数学显示


在记数学笔记的过程中,最常用的两个片段是mk和dm。


它们负责启动数学模式。第一个片段用于“行内数学”,第二个用于“显示数学”。


640?wx_fmt=gif


代码行内的数学片段是“智能的”:它知道什么时候在$符号后面直接输入一个单词,它会自动加个空格。但如果输入一个非单词的字符,它就不会添加空格了,比如在““$p$-value”情况下,是这样的:


640?wx_fmt=gif


这个片段的代码如下:


snippet mk "Math" wA

 

$${1}$`!p
if t[2] and t[2][0] not in [',', '.', '?', '-', ' ']:
    snip.rv = ' '
else:
    snip.rv = ''
`$2
endsnippet


第一行末尾的w,意味着这个片段会在单词边界处扩展,例如,hellomk不会扩展,但是hello mk会。


用于显示数学的片段更简单,也更加方便;有了它,你可能再也不会忘记用句号结束方程了。


640?wx_fmt=gif


代码:


snippet dm "Math" wA

 

\[
$1
.\] $0
endsnippet


小写和上标


另一个很有用的片段就是下标。能够把a1改为a1,把a_12改为a{12}。


640?wx_fmt=gif


这个片段的触发器是使用正则表达式。有两种情况会扩展片段。一是你键入一个字符,后面跟着一个数字,比如[A-Za-z]\d;另一种是,一个字符后面有并跟着两个数字,比如[A-Za-z]\d\d。


snippet '([A-Za-z])(\d)' "auto subscript" wrA

 

`!p snip.rv = match.group(1)`_`!p snip.rv = match.group(2)`
endsnippet

snippet '([A-Za-z])_(\d\d)' "auto subscript2" wrA
`!p snip.rv = match.group(1)`_{`!p snip.rv = match.group(2)`}
endsnippet


当你使用括号将正则表达式的一部分装在一个组中时,例如(\d\d),你可以在 Python中通过match.group (i)来使用它们扩展片段。


至于上标,可以使用td,它就会变成^{}。然而,对于平方、立方和其他一些常见的片段,可以使用专门的代码片段,如 sr、cb等等。


效果图:


640?wx_fmt=gif


代码:


 

snippet sr "^2" iA
^2
endsnippet

snippet cb "^3" iA
^3
endsnippet

snippet compl "complement" iA
^{c}
endsnippet


snippet td "superscript" iA
^{$1}$0
endsnippet


分数


分数是一个用起来最方便的一个片段,扩展的形式如下:


/ / → frac {}{}
3 / → frac {3}{}
4 pi ^ 2 / → frac {4 pi ^ 2}{}
(1 + 2 + 3) / → frac {1 + 2 + 3}{}
(1 + (2 + 3) /)→(1 + frac {2 + 3}{})
(1 + (2 + 3)) / → frac {1 + (2 + 3)}{


640?wx_fmt=gif


第一个片段的代码很简单:


 

snippet // "Fraction" iA
\\frac{$1}{$2}$0
endsnippet


第二个和第三个示例,可以使用正则表达式来匹配3/、4ac/、6pi^2/、a2/等表达式。


 

snippet '((\d+)|(\d*)(\\)?([A-Za-z]+)((\^|_)(\{\d+\}|\d))*)/' "Fraction" wrA
\\frac{`!p snip.rv = match.group(1)`}{$1}$0
endsnippet


看了上边这些,你可能觉得正则表达式太难了。没关系,下面有一个解释得非常直观的图表:


640?wx_fmt=png


在第四和第五种示例下,要换一种方法。使用UltiSnips的正则表达式引擎解决不了的,Python可以:


 

priority 1000
snippet '^.*\)/' "() Fraction" wrA
`!p
stripped = match.string[:-1]
depth = 0
i = len(stripped) - 1
while True:
    if stripped[i] == ')': depth += 1
    if stripped[i] == '(': depth -= 1
    if depth == 0: break;
    i -= 1
snip.rv = stripped[0:i] + "\\frac{" + stripped[i+1:-1] + "}"
`{$1}$0
endsnippet


这里最后要分享的关于分数的片段,能根据你的选择,来生成一个分数。


你可以先选择一些文本,然后按Tab键,继续输入、然后再按Tab键。


640?wx_fmt=gif


代码中,使用${VISUAL}变量来表示所选的内容。


 

snippet / "Fraction" iA
\\frac{${VISUAL}}{$1}$0
endsnippet


Sympy和Mathematica


还有一个很酷但用得不多的片段,是使用Sympy来计算数学表达式。例如,输入sympy,然后按下Tab,可以扩展为sympy | sympy,输入sympy 1 + 1 sympy,按下Tab,可以扩展为2。


640?wx_fmt=gif


片段代码:


 

snippet sympy "sympy block " w
sympy $1 sympy$0
endsnippet

priority 10000
snippet 'sympy(.*)sympy' "evaluate sympy" wr
`!p
from sympy import *
x, y, z, t = symbols('x y z t')
k, m, n = symbols('k m n', integer=True)
f, g, h = symbols('f g h', cls=Function)
init_printing()
snip.rv = eval('latex(' + match.group(1).replace('\\', '') \
    .replace('^', '**') \
    .replace('{', '(') \
    .replace('}', ')') + ')')
`
endsnippet


用Mathematica,也可以做类似的事情:


640?wx_fmt=gif


片段代码:


 

priority 1000
snippet math "mathematica block" w
math $1 math$0
endsnippet

priority 10000
snippet 'math(.*)math' "evaluate mathematica" wr
`!p
import subprocess
code = 'ToString[' + match.group(1) + ', TeXForm]'
snip.rv = subprocess.check_output(['wolframscript', '-code', code])
`
endsnippet


后缀片段


除了上边这些之外,后缀片段也很值得分享。例如phat→hat{p}和zbar→overline{z}。还有类似的后缀向量,例如v,.→vec{v}和v.,→vec{v}。.和,的顺序没关系,所以可以同时按下它们两个。


640?wx_fmt=gif


这些片段真的可以节省时间,可以按照和老师写板书一样的顺序来记。


注意,bar和hat前缀也依然可以用,只要以较低的优先级添加它们就行。


这些片段的代码是:


 

priority 10
snippet "bar" "bar" riA
\overline{$1}$0
endsnippet

priority 100
snippet "([a-zA-Z])bar" "bar" riA
\overline{`!p snip.rv=match.group(1)`}
endsnippet


 

priority 10
snippet "hat" "hat" riA
\hat{$1}$0
endsnippet

priority 100
snippet "([a-zA-Z])hat" "hat" riA
\hat{`!p snip.rv=match.group(1)`}
endsnippet


 

snippet "(\\?\w+)(,\.|\.,)" "Vector postfix" riA
\vec{`!p snip.rv=match.group(1)`}
endsnippet 


其他片段


此外,小哥还有大约100个常用的片段(下载地址附于文末),大多数都很简单。比如,输入!>变成\mapsto,输入->变成\to等等。


640?wx_fmt=gif


fun变成f: \R \to \R :,!>变成\mapsto,->变成\to,cc变成\subset。


640?wx_fmt=gif


lim变成\lim{n \to \infty},sum变成\sum{n = 1}^{\infty},ooo变成\infty。


640?wx_fmt=gif

640?wx_fmt=gif


特定课程的片段


除了一些常用的片段,也可以针对特定的课程设定片段。例如,在量子力学这门课中,可以设定一些关于bra/ket符号的片段。


<a|→\bra{a} <ψ|→\bra{\psi}="" a="">→\ket{a}
|ψ>→\ket{\psi}
→\braket{a}{b}


640?wx_fmt=gif


代码:


 
 

snippet "\<(.*?)\|" "bra" riA
\bra{`!p snip.rv = match.group(1).replace('q', f'\psi').replace('f', f'\phi')`}
endsnippet

snippet "\|(.*?)\>" "ket" riA
\ket{`!p snip.rv = match.group(1).replace('q', f'\psi').replace('f', f'\phi')`}
endsnippet

snippet "(.*)\\bra{(.*?)}([^\|]*?)\>" "braket" riA
`!p snip.rv = match.group(1)`\braket{`!p snip.rv = match.group(2)`}{`!p snip.rv = match.group(3).replace('q', f'\psi').replace('f', f'\phi')`}
endsnippet


上下文


在编写这些片段时需要考虑的一件事是,“这些片段会与常用的文本冲突吗?”


例如,在英语中大约有72个单词包含sr,这意味着当输入disregard这个词时,sr会扩展到^2,出现一个di^2egard。


这个问题的解决方案是,为代码片段添加上下文


通过使用 Vim 的语法突出显示,可以确定UltiSnips是否应该扩展片段,这取决于你使用的是数学还是文本。


 

global !p
texMathZones = ['texMathZone'+x for x in ['A', 'AS', 'B', 'BS', 'C',
'CS', 'D', 'DS', 'E', 'ES', 'F', 'FS', 'G', 'GS', 'H', 'HS', 'I', 'IS',
'J', 'JS', 'K', 'KS', 'L', 'LS', 'DS', 'V', 'W', 'X', 'Y', 'Z']]

texIgnoreMathZones = ['texMathText']

texMathZoneIds = vim.eval('map('+str(texMathZones)+", 'hlID(v:val)')")
texIgnoreMathZoneIds = vim.eval('map('+str(texIgnoreMathZones)+", 'hlID(v:val)')")

ignore = texIgnoreMathZoneIds[0]

def math():
    synstackids = vim.eval("synstack(line('.'), col('.') - (col('.')>=2 ? 1 : 0))")
    try:

        first = next(
            i for i in reversed(synstackids)
            if i in texIgnoreMathZoneIds or i in texMathZoneIds
        )
        return first != ignore
    except StopIteration:
        return False
endglobal


现在,你可以将context “math()”添加到只希望在数学上下文中展开的片段中。


 

context "math()"
snippet sr "^2" iA
^2
endsnippet


请注意,“数学上下文”是一个微妙的东西。 有时你可以使用\text{…}在数学环境中添加一些文本。在这种情况下,你不需要扩展片段。但是,在以下情况下: \[ \text{$...$} \],它们可以扩展。 这就是为什么math上下文的代码有点复杂。下面的动图说明了这些微妙之处。


640?wx_fmt=gif


除了上述一些片段,你也可以根据自己的需要,来自己添加一些插件或者片段,来提高自己的效率。


用笔还是用电脑?


纯手打记下1700页数学笔记,awesome都不够形容了这位小哥了,堪称理工科学生中的“英雄”。


640?wx_fmt=png


并非所有人都赞同小哥的做法,强大的高科技工具在传统面前常常会被质疑。


有部分网友认为手写比电脑打字印象深刻,而且要达到这位小哥的熟练程度,恐怕LaTeX和Vim得练习好几年。


既然用笔更方便,为什么还要用电脑来记笔记呢?原因很简单:字太丑!


640?wx_fmt=png


如果记下来的内容连自己看的欲望都没有,怎么复习课堂笔记呢?至少用电脑记下来的排版工整,让人赏心悦目。


虽然国外网友争论不休,但在国内只要一个条件就可以彻底否决这个方法:不让带电脑进课堂。


对此,你怎么看?


工具传送门


Linux和Mac系统自带Vim。


Windows用户安装Vim:
https://ftp.nluug.nl/pub/vim/pc/gvim81.exe


Vim插件管理:
https://github.com/junegunn/vim-plug


Vim上的LaTeX插件:
https://github.com/lervag/vimtex


窗口平铺管理器:
https://github.com/baskerville/bspwm


管理Vim片段工具:
https://github.com/SirVer/ultisnips


如果你用不惯Vim,还有Emacs、Atom、VS Code、Sublime,它们都有LaTeX插件,总有一款文本编辑器适合你。


LaTeX常见数学符号输入方法:
https://en.wikibooks.org/wiki/LaTeX/Mathematics


想要熟悉更多的LaTeX使用方法,就需要系统地学习,平时多加练习也必不可少。


博文链接:

https://castel.dev/post/lecture-notes-1/


编辑:王菁

校对:洪舒越


640?wx_fmt=png640?wx_fmt=jpeg

这篇关于1700页数学笔记火了!全程敲代码,硬核小哥教你上手LaTeX+Vim(附代码)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/901580

相关文章

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

uva 10014 Simple calculations(数学推导)

直接按照题意来推导最后的结果就行了。 开始的时候只做到了第一个推导,第二次没有继续下去。 代码: #include<stdio.h>int main(){int T, n, i;double a, aa, sum, temp, ans;scanf("%d", &T);while(T--){scanf("%d", &n);scanf("%lf", &first);scanf

uva 10025 The ? 1 ? 2 ? ... ? n = k problem(数学)

题意是    ?  1  ?  2  ?  ...  ?  n = k 式子中给k,? 处可以填 + 也可以填 - ,问最小满足条件的n。 e.g k = 12  - 1 + 2 + 3 + 4 + 5 + 6 - 7 = 12 with n = 7。 先给证明,令 S(n) = 1 + 2 + 3 + 4 + 5 + .... + n 暴搜n,搜出当 S(n) >=

uva 11044 Searching for Nessy(小学数学)

题意是给出一个n*m的格子,求出里面有多少个不重合的九宫格。 (rows / 3) * (columns / 3) K.o 代码: #include <stdio.h>int main(){int ncase;scanf("%d", &ncase);while (ncase--){int rows, columns;scanf("%d%d", &rows, &col

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能

代码随想录冲冲冲 Day39 动态规划Part7

198. 打家劫舍 dp数组的意义是在第i位的时候偷的最大钱数是多少 如果nums的size为0 总价值当然就是0 如果nums的size为1 总价值是nums[0] 遍历顺序就是从小到大遍历 之后是递推公式 对于dp[i]的最大价值来说有两种可能 1.偷第i个 那么最大价值就是dp[i-2]+nums[i] 2.不偷第i个 那么价值就是dp[i-1] 之后取这两个的最大值就是d