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を押す必要はありません。 TabShift + Tab Tabを押して移動するTab$1$2 、...と表示され、後者は$0と表示され$0

インライン式と表示式


最も一般的に使用される2つのスニペットは、数学モードをトリガーするmkdmです。 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 

下付き文字と上付き文字


別の便利なスニペットはインデックス用です。 a1a_1a_12a_{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を使用します。 最も一般的なもの(正方形、立方体、その他いくつか)では、 srcbcompなどの個別のスニペットが意図されています。



 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 Tabsympy | sympy展開されsympy | sympy sympy | sympyおよびsympy 1 + 1 sympy Tab2ます。



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



優先順位が低い場合にのみ、 barhatプレフィックスが機能することに注意してください。 これらのスニペットのコードは次のとおりです。

 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 :!>\mapstocc\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+LCtrl+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ドキュメントに埋め込むなど、他のトピックについて説明します。

Source: https://habr.com/ru/post/J445066/


All Articles