asciidoctor と JavaScript と Ploy.ly で自習用ノートを作る

# 概要
AsciiDoc と JavaScript と Ploy.ly の自習用ノートが以外といい感じなので紹介します。
オススメする訳でもなく、こういう用途にも使えますよという話です。

なお、教材は [技術者のための確率統計学](https://www.shoeisha.co.jp/book/detail/9784798157863) です。

JupyterNotebook 使用してもいいのですが、途中経過を含めた長めの計算式を書くことの方が多かったり、ある程度文書を章立てして管理整理したかったりだとかで、色々あって未採用です。 Sphinx (reStructuredText, Markdown) とかも知っていますが、未採用です。

## 環境
OS: Ubuntu 18.04
asciidoctor 1.5.8

※ もともと asciidoctor-mathematical が使いたかった故の Ubuntu を使っています。
※ MathJaX など標準機能の範囲でよければ Windows 環境にも構築できるのではないかと思います。
※ (Windowsだと、AsciidoctorJ あたりがパッケージ管理のしやすさ含めて導入しやすいのかなぁ。)

## メモの例: トップページ
![Screenshot_2019-08-21 技術者のための確率統計学.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326797/9a9ac58c-7de1-4314-b166-796250f460d6.png)

### AsciiDoc文書の抜粋
上の画像にある「問1 (p47)」の部分はこのトップページにあたる文書には含んでいません。実体は別ファイルで  `chapter_1-6-1.adoc` 中の `question` の部分を抜き出して生成しています。(PDF出力を切り捨てればもっとスッキリするだろうけど。)

```asciidoc
== Chapter 1 確率空間と確率変数

// === 演習問題 問1 (p47)
ifeval::["{backend}" == "html5"]
include::chapter_1-6-1.adoc[leveloffset=+1,tag=question]

link:chapter_1-6-1.html[演習問題 問1の回答]
endif::[]
ifeval::["{backend}" == "pdf"]
include::chapter_1-6-1.adoc[leveloffset=+1]
endif::[]


=== 演習問題 問2 (p47)

=== 演習問題 問3 (p49)

=== 演習問題 問4 (p49)

=== 演習問題 問5 (p49)

== Chapter 2 離散型の確率分布
```


## メモの例: 本文

![Screenshot_2019-08-22 演習問題 問1 (p47).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326797/286970ed-6a8b-dee5-f18c-db6af69b6bed.png)


### AsciiDoc文書の抜粋
JupyterNotebookっぽい使い方ですね。グラフは JavaScriptで書いたものを埋め込んでいます。
KaTeX向けのコードは別のパスにあるものを読み込みます。

(以下のヘッダ部分を除き)全体的に見て分かるかと思いますが、キャプチャーに表示されている内容と同程度の情報量です。
つまり、デザインなど体裁に関わる部分の指定はノート自体には含める必要はなくて、ただ書くことに集中できます。

```asciidoc
:stem:

ifeval::["{backend}" == "html5"]
ifdef::use-katex[]
include::{katex-inc-path}[]
endif::[]

ifndef::leveloffset[]
[cols="1d" frame="none" grid="none"]
|===
h| link:index.html[Up]
|===
endif::[]
endif::[]
```

見出しのページに自動的に引用していた箇所のコード。問1の前の部分は、普通は要らない。

マークアップでよく使うキーワードを定義して、 `{foo}` みたいな記法で文字を置き換えられるぐらいの話。
「おめが」とか「どっと」って入力して日本語変換候補のリストから選んで文字変換するのはダルイので、
ローマ字入力できるようにしているだけ。

```asciidoc
// tag::question[]
== 演習問題 問1 (p47)
:cap-omega: &#937;

:subset: ⊂
:isin: ∈

ifeval::["{backend}" != "pdf"]
:ZZ: ℤ
:cdot: &#8901;
:le: &#8804;
endif::[]

ifeval::["{backend}" == "pdf"]
:ZZ: pass:q[**Z**]
:cdot: ·
:le: ≦
endif::[]

.問1 (p47)
****
1から365の値を均等な割合で出す電子サイコロがある。この電子サイコロを30個集めて、
同時に目を出したときに得られる、30個の値の組み合わせを根源事象とする標本空間{cap-omega}を考える。
30個の値がすべて異なる根源事象を集めた部分集合 _A_ {subset} {cap-omega} とするとき、確率 _P_(_A_)は、
およそ30%となることを示せ (これは、30人のクラスで、全員の誕生日が異なる確率の近似的な
計算と考えられる。最終的な数値計算は、コンピュータープログラム等を用いても構わない)。
****

// end::question[]
```

回答本文。テーブルやら、コードハイライトやら、JavaScriptのグラフ埋め込みやらがあります。

* AsciiDocだと、数式は AsciiMath でも書くことができます。  
 AsciiMathは、使い方は知っていますが、好みではないので使っていません。  
 板書をリアルタイムにノートを取るとかだと、 AsciiMath の方がよいのかもしれませんね。
* 添え字付き文字 A<sub>i</sub> だとかは、AsciiDoc記法で書いています。  
  Markdown だと `A<sub>i</sub>` とかだるいですね。  
  LaTeXだと情報量が多くなるとページのリロードに時間がかかるので、良く使うマークアップが簡易に書けるのは良い事です。

```asciidoc
=== Solution 1
標本空間は {cap-omega} {isin} ++{++ (_z_~1~,...,_z_~30~) | _z_~k~ {isin} {ZZ}, 1 {le} _z_~k~ {le} 365 ++}++ となるから、
{vbar}{cap-omega}{vbar}=365^30^ となる。{vbar}__A__{vbar}= (365-0){cdot}(365-1){cdot}...{cdot}(365-29) となる。

[stem,latexmath]
++++
P(A) = \frac{365 \cdot 364 \cdots 336}{365^{30}}
++++

=== Solution 2
[cols="1a,8a"]
|===
| A~1~ | 1個目の電子サイコロの目が0個目の電子サイコロと異なる事象。
| A~2~ | 2個目の電子サイコロの目が1個目の電子サイコロの目と異なる事象。
| A~3~ | 3個目の電子サイコロの目が1、2個目の電子サイコロの目と異なる事象。
| ... |
| A~29~ | 29個目の電子サイコロの目が1～28の電子サイコロの目と異なる事象。
| A~30~ | 30個目の電子サイコロの目が1～29の電子サイコロの目と異なる事象。
|===

[stem,latexmath]
++++
\begin{aligned}
P(A_{30})
&= P(A_{29})\cdot\dfrac{365-29}{365} \\
&= P(A_{28})\cdot\dfrac{365-28}{365}\cdot\dfrac{365-29}{365} \\
&= \cdots \\
&= P(A_{1})\cdot\dfrac{365-1}{365}\cdots\dfrac{365-28}{365}\cdot\dfrac{365-29}{365} \\
&= \dfrac{365}{365}\cdot\dfrac{365-1}{365}\cdots\dfrac{365-28}{365}\cdot\dfrac{365-29}{365} \\
\end{aligned}
++++

[source,javascript]
----
let sum=1.
for (let i = 0; i < 30; i++) {
    let numerator = 365 - i
    let denominator = 365
    sum *= numerator/denominator
    console.log('%d / %d -> %f', numerator, denominator, sum)
}
----

[source,console]
----
$ node unique_birthday.js
365 / 365 -> 1
364 / 365 -> 0.9972602739726028
363 / 365 -> 0.9917958341152187

...snip...

337 / 365 -> 0.3190314625222229
336 / 365 -> 0.2936837572807312
$
----

ifeval::["{backend}" == "html5"]
[pass]
++++
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<div id="myDiv"><!-- Plotly chart will be drawn inside this DIV --></div>
<script>
  let ary_x = [];
  let ary_y = [];
  let sum=1.;
  for (let i = 1; i <= 60; i++) {
      let numerator = 365 - (i - 1)
      let denominator = 365
      sum *= numerator/denominator
      ary_x.push(i);
      ary_y.push(sum);
  }

  var trace1 = {
    x: ary_x,
    y: ary_y,
    type: 'scatter'
  };

  var data = [trace1];

  Plotly.newPlot('myDiv', data);
</script>
++++
endif::[]
```

## 参考

* [AtomとVSCodeのJupyter Notebook環境を比較した - pyhaya’s diary](https://pyhaya.hatenablog.com/entry/2018/12/19/231045)