VimでLaTeXの数学ノートを書く方法

しばらく前にQuora で、LaTeXで数学の講義ノートに遅れずについていく方法について質問に答えました。 そこで、VimとInkscape(図面用)を使用して、LaTeXでメモを取るワークフローを説明しました。 しかし、それ以来、多くのことが変わったため、新しいプロセスの説明を含むいくつかのブログ投稿を公開したいと思います。 これは最初の記事です。



私はLaTeXを使用して数学コースの第2学期にメモを取り始め、それ以来1700ページ以上を書きました。 概要がどのように見えるかの例を次に示します。















図面を含むこれらのメモは、講義で直接作成され、その後編集されません。 LaTeXで要約を効率的に書くには、4つのルールに従う必要があります。





この記事は最初のポイントについてです:LaTeXでメモを取ること。



VimとLaTeX



LaTeXでテキストと数式を書くには、Vimを使用します。 これは強力な汎用テキストエディターであり、高度に拡張可能です。 私はそれを使って、コード、LaTeX、Markdownテキスト...一般に、あらゆるテキストを書きます。 彼はかなり急な学習曲線を持っていますが、ベースをマスターした場合、通常のホットキーなしでエディターに戻ることはすでに困難です。 これは、LaTeXドキュメントを編集したときの画面の外観です。







左側にVimがあり、右側にZathuraのPDFビューアーがあります。これはVimスタイルのキーボードショートカットもサポートしています。 私はUbuntuでbspwmウィンドウマネージャーを使用しています。 プラグインとしてLaTeXはvimtexをインストールしました 。 構文の強調表示、目次、synctexなどを提供します。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'








最後の2行は変装を調整します。 これは、カーソルがこの行にないときにLaTeXコードが置き換えられるか、非表示になる関数です。 \ [



\]



$



を非表示にすると、それほど目立たなくなり、ドキュメントの概要がわかりやすくなります。 この関数は、アニメーションに示すように、 \bigcap



withに、 \in







などに置き換えます。







この設定を使用すると、講師がボードに書き込むのと同じ速さでLaTeXに書き込むというタスクを達成できます。 ここでスニペットが機能します。



スニペット



スニペットとは何ですか?



スニペットは、他のテキストによって呼び出される再利用可能な短いテキストです。 たとえば、記号を入力してTabキーを押すと、単語signは署名に変わります。







スニペットは動的にすることができます。 today



入力しtoday



Tab



と、 today



の単語は現在の日付に置き換えられ、 box



- Tab



は自動的にサイズが大きくなるフィールドになります。











あるスニペットを別のスニペット内で使用することもできます。







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
      
      





このコードの代わりに、 snip.rv



変数の値がドキュメントに挿入されます。 ブロック内では、スニペットの現在の状態にアクセスできます。たとえば、 t[1]



は最初のタブの場所に対応し、 fn



現在のファイル名などに対応します。



LaTeXスニペット



スニペットは、特に一部のより複雑なスニペットの作業を大幅にスピードアップします。 最も単純なものから始めましょう。



環境



環境を挿入するには、行の先頭にbeg



と入力するだけです。 次に、環境の名前。これは\end{}



コマンドに反映されます。 Tab



キーを押すと、カーソルが内部に配置されます。







コードは次のとおりです。



 snippet beg "begin{} / end{}" bA \begin{$1} $0 \end{$1} endsnippet
      
      





b



記号は、このようなスニペットが行の先頭でのみ機能A



ことを意味しますA



は自動拡張を意味します。つまり、 Tab



を押す必要はありません。 Tab



Shift



+ Tab



Tab



を押して移動するTab



$1



$2



、...と表示され、後者は$0



と表示され$0







インライン式と表示式



最も一般的に使用される2つのスニペットは、数学モードをトリガーするmk



dm



です。 1つ目はインライン式、2つ目は表示された式です。







式スニペットはスマートです。彼は、ドル記号の後にスペースを挿入するタイミングを知っています。 終了$の直後に単語の入力を開始すると、スペースが追加されます。 しかし、別の文字を入力しても、「$ p $ -value」の場合のようにスペースは追加されません。







このスニペットのコードは次のとおりです。



 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



は機能します。



表示される数式のスニペットは単純ですが、非常に便利です。 方程式は常にドットで終了します。







 <snippet dm "Math" wA \[ $1 .\] $0 endsnippet
      
      





下付き文字と上付き文字



別の便利なスニペットはインデックス用です。 a1



a_1



a_12



a_{12}



ます。







このスニペットのコードは、正規表現をトリガーとして使用します。 [A-Za-z]\d



としてエンコードされた文字が続く文字、または_



と2つの数字が続く文字を入力すると、フラグメントが展開されます: [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



comp



などの個別のスニペットが意図されています。







 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
      
      





分数



最も便利なスニペットの1つは、分数で機能します。 彼は次の置換を行います。



//



\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)}{}











最初の簡単なコード:



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





2番目と3番目の置換は、式3/



4ac/



6\pi^2/



a_2/



などに対応する正規表現を使用して行われます。



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





ご覧のとおり、正規表現は非常に長くなる可能性がありますが、すべてを説明する図を次に示します。







4番目と5番目のケースでは、スニペットは対応するブラケットを見つけようとします。 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



ます。







コードは、選択を反映する${VISUAL}



変数を使用します。



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





SympyとMathematica



別のクールですが、あまり使用されていないスニペットは、 sympyを実行して数式を評価します。 例: sympy



Tab



sympy | sympy



展開されsympy | sympy



sympy | sympy



およびsympy 1 + 1 sympy



Tab



2



ます。







 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('xyz t') k, m, n = symbols('km n', integer=True) f, g, h = symbols('fg h', cls=Function) init_printing() snip.rv = eval('latex(' + match.group(1).replace('\\', '') \ .replace('^', '**') \ .replace('{', '(') \ .replace('}', ')') + ')') ` endsnippet
      
      





Mathematicaでは、同様のことが可能です:







 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,.



などのベクトルを挿入しv,.



\vec{v}



およびv.,



\vec{v}



ピリオドとセミコロンの順序は重要ではないため、同時にクリックできます。 これらのスニペットは、講師がボードに書き込む速度と同じ速度で入力するため、時間を節約できます。







優先順位が低い場合にのみ、 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



になります。







fun



f: \R \to \R :



に変換しf: \R \to \R :



!>



\mapsto



cc



\subset











lim



\lim_{n \to \infty}



sum



\sum_{n = 1}^{\infty}



ooo



\infty



ます。











コース固有のスニペット



頻繁に使用されるものに加えて、特定のスニペットもあります。 これらは、 .vimrc



単一行としてロードされ.vimrc







 set rtp+=~/current_course
      
      





ここで、 current_course



は現在のコースへのシンボリックリンクです (これについては別の記事で詳しく説明します)。 このフォルダーには~/current_course/UltiSnips/tex.snippets



というファイルがあり、ここにコーススニペットを追加します。 たとえば、量子力学の場合、燭台とケトの量子状態を記録するスニペットがあります。



<a|



\bra{a}





<q|



\bra{\psi}





|a>



\ket{a}





|q>



\ket{\psi}





\braket{a}{b}



\braket{a}{b}







量子力学では多くの場合\psi



使用するので、braketのすべてのq



\psi



に自動的に置き換えました。







 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の2,000語があります。 したがって、「 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



コンテキストのコードがそれほど単純ではない理由です。 次のアニメーションは、これらの微妙な点を示しています。







その場でスペル修正



数式は要約の重要な部分ですが、私はほとんどの場合英語で印刷します。 1分あたり約80ワード、タイピングスキルはかなり優れていますが、多くのタイプミスを犯します。 それが、作業を妨げることなくスペルミスを修正するVimにバインディングを追加した理由です。 入力中にCtrl+L



Ctrl+L



と、前のスペルミスが修正されます。 次のようになります。







スペルチェックの私の設定:



 setlocal spell set spelllang=nl,en_gb inoremap <Cl> <cg>u<Esc>[s1z=`]a<cg>u
      
      





ここでは、前のスペルエラー[s



に移動し、最初のオプション1z=



を選択して`]a



を返します。 中央の<cg>u



コマンドを使用すると、修正をすばやく元に戻すことができます。



結論として



Vimスニペットのおかげで、LaTeXコードを書くのはもう面倒ではなく、むしろ面白くなります。 オンザフライでのスペルと組み合わせることで、数学の講義を便利かつ迅速に概説することができます。 次の記事では、イラストをデジタルで描画してLaTeXドキュメントに埋め込むなど、他のトピックについて説明します。



All Articles