Pythontexでランダムに単語テストを作成する


はじめに

pythontexに関する記述はあまり見ないので、それの説明。

pythontexっていうのは、簡単にいうとplatex上でpythonコードが動くよっていう話。

古のplatexでも、lualatexでも動くからうまく使えれば便利。

今日はこれを利用して単語テストを作ってみたよ。

満たしたい条件は

1.あらかじめ単語リストを作っておき、そこからランダムに選んで並べて問題を作る

2.答えも一緒に作れるようにする

3.コマンドみたいに呼べれば最高

の3点。

では行ってみよう。

pycodeの部分

まずはlatex上で動かす、pythonのコード部分の作成。
読み込む単語リストの形式はこんな感じ。

word_list.csv(一部)
51  
money   金
apple   りんご
table   机
light   光
left    左
right   右
one 1
two 2
three   3
text    テキスト
free    自由
busy    忙しい
beautiful   美しい
read    読む
break   壊す
eat 食べる
wash    洗う
hand    手
old 年を取った
young   若い
window  風
banana  バナナ
cook    料理をする
.
.
.
why なぜ

最初の1行目が読み込む単語数。二行目から単語が一行ずつあって、英語と日本語の間はタプルで区切ってある。

これを読み込んでいく。

test.tex
\begin{pycode}
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import random

class WordTest:
  def __init__(self,filename,first,last,questions):
    self.filename = filename # 読み込むファイル名
    self.first = first # 問題番号最初
    self.last = last # 問題番号最後
    self.questions = questions # 問題数

    self.tango_list = [] # ファイルの単語データはここにいれる
    with open(filename,encoding="shift_jis") as f:
      num = f.readline() # 単語の個数
      for i in range(int(num)):
        text = f.readline() # 一行ずつ読み込み
        texts = text.split("\t") # 文字列をタブで区切ってリスト化
        self.tango_list.append(texts)

  def random_test(self):
    '''呼び出されるたびに、ランダムのテストを出力'''
    self.tango_num = list(range(self.first,self.last+1)) # 元の問題番号は1初めに調整
    self.tango_num = random.sample(self.tango_num,len(self.tango_num)) # ランダムに並び替える
    print(r"\twocolumn[{\large 単語テスト} \\ ]")
    print(r"\begin{tabular}{llr}")
    for i,q in enumerate(self.tango_num[:self.questions]): #先頭から問題数分だけの単語を表示
      if (i+1) % 51 == 0 : # 50問で折り返し処理
        print(r"\end{tabular}")
        print(r"\newpage")
        print(r"\begin{tabular}{llr}")
      print(str(i+1) + " & ")
      print(self.tango_list[q-1][0] + " & ") # 元も問題番号は1から
      print(r"( \hspace{50pt} )"+ r"\\")
    print(r"\end{tabular}")

  def ansewr(self):
    '''現在保存されているランダムテストの答えを出力'''
    print(r"\twocolumn[{\large 答え}]")
    print(r"\begin{tabular}{ll}")
    for i,q in enumerate(self.tango_num[:self.questions]):
      if (i+1) % 51 == 0 :
        print(r"\end{tabular}")
        print(r"\newpage")
        print(r"\begin{tabular}{llr}")
      print(str(i+1) + " & ")
      print(self.tango_list[q-1][1] + r"\\")
    print(r"\end{tabular}")

# newcommandがしやすいようにカプセル化する
def make_wordtest(objname,filename,first,last,questions):
  globals()[objname] = WordTest(filename,first,last,questions)
  return ""
def random_test(objname):
  globals()[objname].random_test()
  return ""
def ansewr(objname):
  globals()[objname].ansewr()
  return ""
\end{pycode}

これで読み込む。使用するライブラリはrandomだけ。

WordTestクラスからオブジェクトを作って、random_testでテストの本体作成、ansewrで答えを作成。

latexのnewcommandを利用しやすくするために、関数でカプセル化をしておいた。

ちなみにglobals()[文字列]は文字列のグローバル変数を作成できる禁じ手。
print(r"")はエスケープシーケンスを無視させて出力させる。print出力がtexのデータ扱いされるから、pythontexではよく使う。

関数化された部分がreturn ""なのはこれをしとかないと、勝手にNoneが出力されるから。

tex本体

pycodeの部分が出来たので、texデータ本体の作成。

word_test.tex
\documentclass[a4j,twocolumn,9pt]{ltjsarticle}

% PythonTeX
\usepackage[]{pythontex}
\input{test}
\newcommand{\readlist}[2]{\py{make_wordtest("#1","#2",1,51,51)}}
\newcommand{\maketest}[1]{\py{random_test("#1")}}
\newcommand{\makeansewr}[1]{\py{ansewr("#1")}}


% %余白調整
\usepackage[margin=15mm]{geometry}

\title{}
\author{tukiusa}
\date{\today}
\pagestyle{empty}



\begin{document}
\readlist{try}{word_list.csv}
\maketest{try}
\newpage
\makeansewr{try}
\end{document}

こんな感じで完成。

使ったのはLuaLatex。なので最初のdocumentclassの部分がltjsarticleになってる。

文字の大きさは9ptにしてある。この余白の設定だとちょうどA4半分に50問のせれる感じになる。

そして、pythonが使えるようにpythontexを読み込んで、
input{test}でpycodeの本体が記入されてるtest.texを読み込み。
さっきカプセル化した関数をtex上で呼べるようにnewcommandで設定。

\readlistでtryっていうオブジェクトをword_list.csvから作成。
\maketestでテスト本体を出力。
\makeansewrで答えを出力。

あとは、これらをdocument環境内で呼んで完成!!

このtexファイルをlualatex → pythontex → lualatexの順で処理をすれば、

とちゃんと出力されてる

さいごに

結構簡単に要件を満たすことが出来た。別に単語テストだけではなく、問題をランダムにしたテストの作成ならいろんな場所で使えると思うのでぜひ、興味があれば試してみてください。

エクセルとかlatexだけでも似たようなことは出来るっぽいけど、コードの可視性、応用が効くとかの長所はあるはず・・・