Edited at

問題集作成のTips

More than 1 year has passed since last update.


この記事はTeX & LaTeX Advent Calendar 2016の16日目の記事です。 15日はmattskalaさんのCircuit-Macrosで回路図を書いてでした。 17日はhak7a3さんです。



はじめに

仕事を休んで一人で群馬県の湯宿(ゆじゅく)温泉に来ています。温泉から上がって夕食を食べた後,部屋で一人まったりこの記事を書いています。TeX & LaTeX Advent Calendar のために休みを取りました!(大嘘)。

さて,私は普段,東大受験専門塾の鉄緑会というところで中高生相手に数学の講師をしています。

また,数学のテキスト・問題集の作成の責任者もしています。

鉄緑会では,数学のみならず全教科のテキスト・問題集がTeXで作られています。ご参考までに,TeX Users Group 2013で発表した,鉄緑会における TeX の活用法を紹介したスライドを貼っておきます。

この記事では,数学の問題集を作成する際に工夫していることをまとめてみたいと思います。

出来上がった問題集が全く同じでも,TeXでどのように設計するかについては,色々と工夫する余地があります。


  1. 改訂作業(新規問題の採用や問題の入れ替え等)が楽になるように。

  2. 年度ごとの問題セット情報が残るように。

  3. コンパイル速度向上のために,ファイルアクセスの回数はなるべく少なく。

などです。


設計

以下,設計案をいくつか見ていき,メリット・デメリットを挙げ,少しずつ改善していきます。

今回作る問題集は,


  • 全20回

  • 各回,問題文を20題分出力したあと,それらの解答を出力する(第1回問題,第1回解答,第2回問題,第2回解答…の順)

  • 合計400問

  • 解答には図のpdfファイルを1つ\includegraphicsする。

とします。もちろん,実際の問題集では回ごとの問題数は変動しますし,先ほど述べた通りその際の作業のしやすさは重要な観点です。


一般に,命令名やファイル名に全角文字は使うべきでないとされていますが,本記事では設計の考え方を示すためにあえて分かりやすく日本語名を用いている部分もあります。



設計案1(まずはベタに)

まずは,何の工夫もなく設計してみます。


  • 1問につき,問題と解答を別ファイルで用意(20回✕20問/回✕2=800ファイル)


  • \inputで読み込むだけ

ファイル構成:


files/

 ├ problem01-001.tex

 ├ ︙

 ├ problem20-020.tex

 ├ solution01-001.tex

 ├ ︙

 └ solution20-020.tex

figures/

 ├ fig01-001.pdf

 ├ ︙

 └ fig20-020.pdf

workbook.tex

workbook.pdf



workbook.tex

\documentclass[dvipdfmx]{jsarticle}

\usepackage{graphicx}
\def\見出し#1{\par\noindent\textgt{#1}\par}

\begin{document}

\見出し{第1回第1問―問題文}\input{files/problem01-001.tex}

(省略)

\見出し{第20回第20問―解答}\input{files/solution20-020.tex}

\end{document}



メリット


  • ほぼない。強いて言えば,ファイル名を見れば第何回の第何問で使われているかすぐに分かる(ようにできる)。


デメリット


  • 問題を入れ替えたり途中に追加したりすると,ファイル名のリネームの作業が生じる(該当問題だけでなく,その回のその後の問題すべて)。


設計案1のまとめ

問題入れ替えの際にファイル名のリネーム作業が生じるというのは,思いのほか大きいデメリットです。

それを克服するためには,「第何回の第何問でどの問題を使うか」という情報と「実際の問題のファイル(ファイル名)」を切り離して管理するべきです。次の「設計案2」では,使用問題設定.texというファイルを用意することにより,管理の分離を実現します。


設計案2


  • 問題ファイルのファイル名は出題年度・出典等から決めたものにする。


  • 使用問題設定.texに,「第何回の第何問でどの問題を使うか」の情報を書く。


  • \回読み込みによって読み込みを回ごとに分ける。

ファイル構成:


files/

 ├ 2001東京大学理系第1問.tex

 ├ ︙(省略)

 └ 2016東京大学理系第6問.tex

figures/

 ├ 2001東京大学理系第1問-01.pdf

 ├ ︙(省略)

 └ 2016東京大学理系第6問-01.pdf

workbook.tex

workbook.pdf

使用問題設定.tex



使用問題設定.tex

\使用問題設定{01}{2001東京大学理系第1問}

︙(省略)
\使用問題設定{01}{2011東京大学理系第6問}
\使用問題設定{02}{2005東京大学理系第3問}
︙(省略)
\使用問題設定{20}{2016東京大学理系第6問}

\使用問題設定{n}{xxxx}は,「第n回にxxxx.texの問題を追加する」という意味です。\使用問題設定{n}{m}{xxxx}として「第n回の第m問にxxxx.texの問題を使う」ということにした方が分かりやすいのですが,問題の順番の変更のときに少し面倒になるので,{m}の部分はなくして,\使用問題設定が呼ばれた順に問題を追加するようにしています。これにより,問題の入れ替えは使用問題設定.texの2行を入れ替えるだけで実現できます。

↓各問題のファイル(子ファイル)


2001東京大学理系第1問.tex

\def\問題文{%

問題文問題文問題文
}
\def\解{%
解答解答解答
\includegraphics{2001東京大学理系第1問-01.pdf}
}

(ユーザが触れるファイルに\defを直接記述するのは避けるべき(ラップした命令を使うべき)でしょうが,今回は設計の概略を示すということで。)

↓親ファイル


workbook.tex

\documentclass[dvipdfmx]{jsarticle}

\usepackage{graphicx}

\makeatletter

\graphicspath{{figures/}}% 画像フォルダのパス

\def\@namelet#1{\expandafter\let\csname#1\endcsname}% \@namedef の\let 版

%%% カウンタの定義
\newcounter{回番号}
\newcounter{問題番号}

%%% 使用問題設定.tex 内で用いる\使用問題設定 の定義
\def\使用問題設定#1#2{%
\setcounter{回番号}{#1}
\@ifundefined{@第\arabic{回番号}回第1問のファイル名}
{% 新しい回の定義に入ったとき
\newcounter{\arabic{回番号}回の問題数}\setcounter{\arabic{回番号}回の問題数}{1}
}{% そうでないとき
\stepcounter{\arabic{回番号}回の問題数}
}
\@namedef{@第\arabic{回番号}回第\arabic{\arabic{回番号}回の問題数}問のファイル名}{#2}
}

%%% 使用問題設定.tex の読み込み
\input{使用問題設定.tex}

%%% 各問題の問題文・解答の読み込み・設定
\def\問題情報読み込み#1{%
\setcounter{問題番号}{#1}
\par
\input{files/\@nameuse{@第\arabic{回番号}回第\arabic{問題番号}問のファイル名}.tex}
\@namelet{@第\arabic{回番号}回第\arabic{問題番号}問の問題文}\問題文
\@namelet{@第\arabic{回番号}回第\arabic{問題番号}問の解答}\解
}

%%% \見出し の定義
\def\見出し#1{\par\noindent\textgt{#1}\par}

%%% 各問題の問題文を表示する命令の定義
\def\問題文表示#1{%
\見出し{\arabic{回番号}回第#1問―問題文}
\@nameuse{@第\arabic{回番号}回第#1問の問題文}
}

%%% 各問題の解答を表示する命令の定義
\def\解答表示#1{%
\見出し{\arabic{回番号}回第#1問―解答}
\@nameuse{@第\arabic{回番号}回第#1問の解答}
}

%%% 各回の内容を表示する命令の定義
\def\回読み込み#1{%
\setcounter{回番号}{#1}
\newcount\K

\K=0 \loop\ifnum\K<\value{\arabic{回番号}回の問題数}
\advance\K by1
\問題情報読み込み{\number\K}
\repeat

\K=0 \loop\ifnum\K<\value{\arabic{回番号}回の問題数}
\advance\K by1
\問題文表示{\number\K}
\repeat

\K=0 \loop\ifnum\K<\value{\arabic{回番号}回の問題数}
\advance\K by1
\解答表示{\number\K}
\repeat
}

\begin{document}

%%% 各回の表示
\回読み込み{01}
︙(省略)
\回読み込み{20}

\end{document}


(本当はプリアンブル部はスタイルファイルとして分けておきたいですが,これも概略を示すためということで。)


メリット


  • 問題の入れ替えが簡単(使用問題設定.texを書き換えるだけ)

  • 新年度になったら使用問題設定.texを新たに用意することにすれば,過年度のデータを簡単に残せる。実際には,\def\年度{2017}としておいて,\input{使用問題設定\年度 .tex}とすることになるでしょう。


  • \回読み込みによって読み込みを回ごとに分けているので,修正作業の際に便利(該当回以外をコメントアウトすればコンパイルが速い)。


改善できる点


  • 他人にファイルを渡すとき,問題のファイルとそれに付随する画像ファイルを見つけてきてまとめるのが面倒


まとめ

更新作業はこれでかなり楽になります。ただ,1つの問題に関するファイルがfilesフォルダとfiguresフォルダに分かれているので,他人にファイルを渡すときがやや面倒です。1つの問題に関するファイルは1つのフォルダにまとめた方が扱いやすくなります。そうしたのが次の「設計案3」です。


設計案3


  • 問題ごとにフォルダを用意し(フォルダ名は出典などにする),その中に「問題解答.tex」を配置。このファイル名は問題によらず同一。

  • 問題ごとのフォルダの中に「figures」フォルダを作り,画像フォルダのパスは問題や解答の表示を実行する度に変更する。

ファイル構成:


files/

 ├ 2001東京大学理系第1問/

 │ ├ figures/

 │ │ └ fig0001.pdf

 │ └ 問題解説.tex

 ├ 2001東京大学理系第2問/

 │ ├ figures/

 │ │ └ fig0001.pdf

 │ └ 問題解説.tex

 ︙(省略)

workbook.tex

workbook.pdf

使用問題設定.tex



使用問題設定.tex

\使用問題設定{01}{2001東京大学理系第1問}

︙(省略)
\使用問題設定{01}{2016東京大学理系第4問}
\使用問題設定{02}{2015東京大学理系第3問}
︙(省略)
\使用問題設定{20}{2014東京大学理系第5問}


workbook.tex

\documentclass[dvipdfmx]{jsarticle}

\usepackage{graphicx}

\makeatletter

\def\@namelet#1{\expandafter\let\csname#1\endcsname}% \@namedef の\let 版

%%% カウンタの定義
\newcounter{回番号}
\newcounter{問題番号}

%%% 使用問題設定.tex 内で用いる\使用問題設定 の定義
\def\使用問題設定#1#2{%
\setcounter{回番号}{#1}
\@ifundefined{@第\arabic{回番号}回第1問のフォルダ名}
{% 新しい回の定義に入ったとき
\newcounter{\arabic{回番号}回の問題数}\setcounter{\arabic{回番号}回の問題数}{1}
}{% そうでないとき
\stepcounter{\arabic{回番号}回の問題数}
}
\@namedef{@第\arabic{回番号}回第\arabic{\arabic{回番号}回の問題数}問のフォルダ名}{#2}
}

%%% 使用問題設定.tex の読み込み
\input{使用問題設定.tex}

%%% 各問題の問題文・解答の読み込み・設定
\def\問題情報読み込み#1{%
\setcounter{問題番号}{#1}
\par
\input{files/\@nameuse{@第\arabic{回番号}回第\arabic{問題番号}問のフォルダ名}/問題解説.tex}
\@namelet{@第\arabic{回番号}回第\arabic{問題番号}問の問題文}\問題文
\@namelet{@第\arabic{回番号}回第\arabic{問題番号}問の解答}\解
}

%%% \見出し の定義
\def\見出し#1{\par\noindent\textgt{#1}\par}

%%% 各問題の問題文を表示する命令の定義
\def\問題文表示#1{%
\見出し{\arabic{回番号}回第#1問―問題文}
\@nameuse{@第\arabic{回番号}回第#1問の問題文}
}

%%% 各問題の解答を表示する命令の定義
\def\解答表示#1{%
\見出し{\arabic{回番号}回第#1問―解答}
\@nameuse{@第\arabic{回番号}回第#1問の解答}
}

%%% 各回の内容を表示する命令の定義
\def\回読み込み#1{%
\setcounter{回番号}{#1}
\newcount\K

\K=0 \loop\ifnum\K<\value{\arabic{回番号}回の問題数}
\advance\K by1
\問題情報読み込み{\number\K}
\repeat

\K=0 \loop\ifnum\K<\value{\arabic{回番号}回の問題数}
\advance\K by1
\graphicspath{{files/\@nameuse{@第\arabic{回番号}回第\number\K 問のフォルダ名}/figures/}}% 画像フォルダのパス
\問題文表示{\number\K}
\repeat

\K=0 \loop\ifnum\K<\value{\arabic{回番号}回の問題数}
\advance\K by1
\graphicspath{{files/\@nameuse{@第\arabic{回番号}回第\number\K 問のフォルダ名}/figures/}}% 画像フォルダのパス
\解答表示{\number\K}
\repeat
}

\begin{document}

%%% 各回の表示
\回読み込み{01}
︙(省略)
\回読み込み{20}

\end{document}



設計案2からの変更点


  • 設計案2で
    \input{files/\@nameuse{@第\arabic{回番号}回第\arabic{問題番号}問のファイル名}.tex}
    としていたところを
    \input{files/\@nameuse{@第\arabic{回番号}回第\arabic{問題番号}問のフォルダ名}/問題解説.tex}
    とした。


  • \回読み込みの定義の中の\問題文表示{\number\K}\解答表示{\number\K}の前に,画像ファイルのパスを変更するために
    \graphicspath{{files/\@nameuse{@第\arabic{回番号}回第\number\K 問のフォルダ名}/figures/}}を追加した。


メリット・デメリット



  • メリット


    • 1つの問題に関するファイルが1つのフォルダにまとまるので,いくつかの問題のファイル一式を他人に渡すとき楽。




  • デメリット(?)


    • 別の問題で同じ画像ファイルを共有することが(可能だが)やや面倒になる。ただ,そういったケースは実際にはほぼない。




最後に

問題集作成の実務では,他にも沢山の作業が発生します。


  • 目次

  • 章扉

  • 出典(出題年度,出題校),指針,難易度などのデータ

  • ツメ

  • 付録

  • 索引

などなど。ただ,こういったものやファイル構成のTeXでの設計は,出来上がった問題集を使う生徒たちにとってはどうでもいいことであって,一番大事なのは問題集の中身です。その中身に,我々問題集作成者ができるだけ多くの時間をかけられるよう,自動化できる部分はできるだけ自動化しておきたいものです。

それでは,良いお年を!