置頂介紹文

這裡主要放一些關於數學和 LaTeX 的文章, 文章內的檔案一律用 xelatex 編譯而成。
如果你想下載我的 tex 檔回去修改編譯, 請將 preamble.7z 解壓縮後和 tex 檔放在同一個資料夾中即可編譯。
部落格內的文章也有部份是網路或書籍中的資料經過統整編輯而成, 如有侵權請告知。
有任何問題也歡迎留言或 E-mail 給我。

2017年11月17日 星期五

用 LaTeX 做數學考卷 (三) : 選擇題篇 - 【追加】手動設定選項

這篇是 用 LaTeX 做數學考卷 (三) : 選擇題篇 的追加設定, 請先閱讀之前的內容。

鑑於有時候因著選項數的不同, 我們會想要用不同的排版 (例如說只有 4 個選項時, 不想讓他們分成第一行 3 個, 第二行 1 個選項), 所以我們可以在文章中手動修改。

但是我還想不到讓 LaTeX 自動因著選項而調整的方法, 所以先這樣解。

方法是在 preamble 時先設定一個 \if \else \fi 的判斷式, 程式碼如下:

\newif\ifchoices

......

\NewEnviron{choices}
{\setcounter{choices}{0}%
\let\choice\choicetemp
\settowidth{\choiceslen}{\vbox{\halign{##\hfil\cr\BODY\crcr}}}

\ifchoices

\ifdim\choiceslen>.5\linewidth
\setlength{\choiceslen}{\linewidth}%一行一選項
\else
\ifdim\choiceslen>.25\linewidth
\setlength{\choiceslen}{.5\linewidth}%一行三選項
\else
\setlength{\choiceslen}{.25\linewidth}%一行五選項
\fi
\fi

\else

\ifdim\choiceslen>.5\linewidth
\setlength{\choiceslen}{\linewidth}%一行一選項
\else
\ifdim\choiceslen>.333\linewidth
\setlength{\choiceslen}{.5\linewidth}%一行兩選項
\else
\ifdim\choiceslen>.25\linewidth
\setlength{\choiceslen}{.333\linewidth}%一行三選項
\else
\ifdim\choiceslen>.2\linewidth
\setlength{\choiceslen}{.25\linewidth}%一行四選項
\else
\setlength{\choiceslen}{.2\linewidth}%一行五選項
\fi
\fi
\fi
\fi

\fi

\let\choice\choicefinal
\setcounter{choices}{0}%
\begin{flushleft}
\bgroup\BODY\hfill\egroup
\end{flushleft}}


首先用 \newif 設定一個新的判斷式 \ifchoices, 然後在「段落 A2」的指令中設定選項長度及換行的部份修改程式碼如上, 那麼只要在文稿中加入 \choicesture, 之後的選項就會改成一行只有 1, 2, 4 選項, 加上 \choicesfalse, 之後的選項就是一行中可以有 1, 2, 3, 4, 5 個。

如果在文稿中完全不加入此指令, 則預設值為 5 個選項的情形。

2017年1月18日 星期三

LaTeX 自訂巨集 - 題目與解答

看這篇之前, 可以參考一下這篇《用 LaTeX 做數學考卷 (六) : 答案卷與解答篇 -- 文字標籤與解答對應》, 不過這篇還是會把有用到的指令完整介紹。

這次我們的目標是要設計出下圖這樣的東西, 並且在文稿內要將題目和解答放在一起, 讓 LaTeX 自動幫我們分出題目和解答, 並且將解答自動編號排序下來。



因為這裡有大量的指令要介紹, 所以我們先列出一個大綱:
  1. 用 enumitem 套件來自訂條列式環境的樣式並設定隨文條列式 (inline) 的用法
  2. 用 counter 來做到計數自動化
  3. 用 label 和 ref 將輸入的文字排版到自訂的位置
  4. 用 loop 讓 latex 自動計算題數來排版解答
  5. 結語

這裡我們就先把上面那個文件的完整指令碼貼出來, 再慢慢來看

\documentclass[12pt]{article}
\usepackage[CJKnumber]{xeCJK}%中文
\setCJKmainfont[BoldFont={cwTeX Q Hei Bold}]{cwTeX Q Ming Medium} % 設預設中文字型及預設粗體
%\setCJKmainfont{思源黑體 TW}
%\setCJKmainfont{思源黑體}
\setCJKfamilyfont{kai}{cwTeX Q Kai Medium}% 楷書          
\setCJKfamilyfont{hei}{cwTeX Q Hei Bold}% 黑體
\setCJKfamilyfont{ming}{cwTeX Q Ming Medium}% 明體
\setCJKfamilyfont{yuan}{cwTeX Q Yuan Medium}% 圓體
\setCJKfamilyfont{fsong}{cwTeX Q Fangsong Medium}% 仿宋體

\newcommand{\kai}[1]{{\CJKfamily{kai}#1}}% 用 \kai{使用楷書}
\newcommand{\hei}[1]{{\CJKfamily{hei}#1}}% 用 \hei{使用黑體}
\newcommand{\ming}[1]{{\CJKfamily{ming}#1}}% 用 \ming{使用明體}
\newcommand{\yuan}[1]{{\CJKfamily{yuan}#1}}% 用 \yuan{使用圓體}
\newcommand{\fsong}[1]{{\CJKfamily{fsong}#1}}% 用 \fsong{使用仿宋體}

\newcommand{\Z}{\ming{。}}
\newcommand{\Q}{\ming{、}}
\newcommand{\ZB}{\hei{。}}
\newcommand{\QB}{\hei{、}}

\usepackage{amsmath, amsfonts, amssymb, bbding}%數學符號
\usepackage{graphicx}%圖形
\usepackage[colorlinks=true, linkcolor=black]{hyperref}%讓\label和\ref的連動在pdf 檔中產生超連結, 點擊即可跳頁
\usepackage{tcolorbox}

\everymath{\displaystyle}

\usepackage[inline]{enumitem}%與beamer相衝

\newlist{assessment}{enumerate}{1}%自我評量條列環境
\setlist[assessment]{labelindent=1\parindent, leftmargin=*,label*=\CrossOpenShadow\;\arabic*)}

\newlist{ill}{enumerate*}{1}
\setlist[ill]{label=(\arabic*)}

\newlist{ilc}{enumerate*}{1}
\setlist[ilc]{label=(\Alph*)}

%計數器(純數字) \no
\newcounter{nocounter}
\setcounter{nocounter}{1} 
\newcommand{\no}{\arabic{nocounter}\addtocounter{nocounter}{1}}

%自我評量解答與標籤(自動) \ansass{<answer>} (放在要填入的位置上) 要有hyperref套件
\newcounter{assessmentlabel}\setcounter{assessmentlabel}{0}
\makeatletter
\DeclareDocumentCommand{\ansass}{m g}{%
  \protected@write \@auxout {}{\string \newlabel {assessment\theassessmentlabel}{{\IfValueTF{#2}{#1, \par #2}{#1}}{\thepage}{\IfValueTF{#2}{#1, \par #2}{#1}}{assessment\theassessmentlabel}{}}}%
  \hypertarget{assessment\theassessmentlabel}{} \stepcounter{assessmentlabel}
}

\makeatother
%自我評量答案, 直接在格子中填入 \ansassis 即可
\newcounter{assessmentans}\setcounter{assessmentans}{0}
\newcommand{\ansassis}{\ref{assessment\theassessmentans}\stepcounter{assessmentans}}
%重複輸入, 一直到第一個{}內的數字才停止
\newcounter{assloop}
\setcounter{assloop}{1}
\newcommand\ansassloop[2]{
  \loop \ifnum\numexpr\value{assloop}-1 < #1
   #2%
    \stepcounter{assloop}%
  \repeat
}

\newcommand{\showtheanswer}{
\everymath{\textstyle}
\begin{footnotesize}
\begin{tcolorbox}[colback=white,title=\CrossOpenShadow 自我評量參考答案 \CrossOpenShadow]
\ansassloop{\theassessmentlabel}{(\no) \ansassis ; \;}
\end{tcolorbox}
\end{footnotesize}
\everymath{\displaystyle}
}

\begin{document}

\section{自我評量}
\begin{assessment}
\item \ansass{$\frac{28}{11}$} 求 $0.\overline{23}+2.3\overline{13}$ 的值\Z
\item \ansass{ACD} 下列哪些數值是有理數?
\begin{ilc}
\item $0.1\overline{3}$
\item $3+\sqrt{2}$
\item $0$
\item $\frac{\sqrt{75}}{\sqrt{12}}$
\item $\pi$
\end{ilc}
\item \ansass{$16$} $n\in\mathbb{N}$, $\frac{n}{5}<\sqrt{11}<\frac{n+1}{5}$, 則 $n$ 值為何?
\item \ansass{$6$} 已知 $\sqrt{16+\sqrt{252}}$ 的整數部份為 $a$, 小數部份為 $b$, 試求 $2a+b-\frac{3}{b}$ 的值\Z
\item \ansass{ACE} 若 $a$, $b$, $c$, $l$, $m$, $n$ 是實數, $a>b>c$, $l>m>n$, $a+b+c=0$, 則下列何者為真? \\
\begin{ilc}
\item $a>0$
\item $b>0$
\item $c<0$
\item $al>0$
\item $al+bm+cn>0$
\end{ilc}
\item \ansass{$6$, $(3,2)$} 設 $a$, $b$ 為正實數且 $2a+3b=12$, 則 $ab$ 的最大值為何? 此時 $(a,b)$ 分別為何?
\item \ansass{$0\leq\alpha\leq 17$, $-19\leq\beta\leq 9$, $1\leq\gamma\leq 109$, $\frac{1}{5}\leq\delta\leq\frac{3}{2}$} $x$, $y\in\mathbb{R}$, 若 $(x-2)^2\leq 1$ 且 $|y-\frac{7}{2}|\leq\frac{13}{2}$, 試求下列各式的範圍\Z \\
\begin{ill}
\item $\alpha=2x+y+1$, 
\item $\beta=x-2y$, 
\item $\gamma=x^2+y^2$, 
\item $\delta=\frac{3}{y+5}$\Z
\end{ill}
\item \ansass{$(-2,5)$} 若不等式 $|ax+1|\geq b$ 的解為 $x\geq 3$ 或 $x\leq -2$, 則數對 $(a,b)$ 為何?
\item \ansass{$-\frac{2}{3}<x<2$} 解不等式 $|x|+2|x-1|<4$\Z
\item \ansass{$20$, $7040$, $7039$} 令 $x=\sqrt{7}+\sqrt{3}$, $y=\sqrt{7}-\sqrt{3}$, 則 \\
\begin{ill}
\item $x^2+y^2$, 
\item $x^6+y^6$, 
\item $(\sqrt{7}+\sqrt{3})^6$ 的整數部份為何?
\end{ill}
\end{assessment}
\showtheanswer

\end{document}

接著就按著大綱來介紹完整的指令了。
  1. 用 enumitem 套件來自訂條列式環境的樣式並設定隨文條列式 (inline) 的用法
    我們藉由這個主題來介紹自訂 enumerate 的用法

    \usepackage[inline]{enumitem}%與beamer相衝

    \newlist{assessment}{enumerate}{1}%自我評量條列環境
    \setlist[assessment]{labelindent=1\parindent, leftmargin=*,label*=\CrossOpenShadow\;\arabic*)}

    \newlist{ill}{enumerate*}{1}
    \setlist[ill]{label=(\arabic*)}

    \newlist{ilc}{enumerate*}{1}
    \setlist[ilc]{label=(\Alph*)}

    \usepackage[inline]{enumitem} 使用套件 enumitem, 並設定 inline 以便設定隨文條列式。

    \newlist 定義新的條列式, 第一個 {} 是指對應的原條列式內容 (itemize; enumerate; description), enumerate* 則是隨文條列式 (inline); 第二個 {} 是指可用的(巢狀式)階層數。

    定義完之後"一定要"設定條列式的樣式 (\setlist), 至少要定義 \label, enumitem 可以讓我們非常自由的定義 \label 的樣式, 最簡單的樣式就是 \arabic (阿拉伯數字), 也可以用 \alph: 小寫英文字母; \Alph: 大寫英文字母; \Roman: 大寫羅馬數字; \roman: 小寫羅馬數字。

    leftmargin=* 用來設定左方留白, labelindent=1\parindent 則用來設定左方縮排, \parindent 是指段落開始時的縮排距離。

  2. 用 counter 來做到計數自動化
    在程式碼中我們用了大量的計數器 (counter), 因此我們一併介紹計數器的功能和用法。

    \newcounter{nocounter}\setcounter{nocounter}{1}
    \newcommand{\no}{\arabic{nocounter}\addtocounter{nocounter}{1}}

    用 \newcounter 設定一個計數器, 然後設定起始值 (\setcounter)。

    定義一個新的指令 (\newcommand{\no}) 將計數器以用阿拉伯數字顯示 (\arabic{}), 並在同時將計數器加 1 (\addtocpunter{}), 也可以用 \stepcounter{nocounter} 來讓計數器加 1。

    然而這個做法是讓文件顯示出數字, 如果要把數字讓 latex 拿來做內部的處理 (也就是說讓 latex 認得這個數字) 的話, 就要在這個計數器前加上\the, 變成 \thenocounter, 或是用指令 \value{nocounter}。

  3. 用 label 和 ref 將輸入的文字排版到自訂的位置
    這是整篇文章最關鍵的動作, 他可以讓文稿和編譯出來的稿件呈現截然不同的樣式, 用來呈現題目和答案分開的套件還有 answer 套件, 不過因為這個套件的靈活度相對較低, 所以我們自己寫。

    %自我評量解答與標籤(自動) \ansass{<answer>} (放在要填入的位置上) 要有hyperref套件
    \newcounter{assessmentlabel}\setcounter{assessmentlabel}{0}
    \makeatletter
    \DeclareDocumentCommand{\ansass}{m g}{%
    \protected@write \@auxout {}{\string \newlabel {assessment\theassessmentlabel}{{\IfValueTF{#2}{#1, \par #2}{#1}}{\thepage}{\IfValueTF{#2}{#1, \par #2}{#1}}{assessment\theassessmentlabel}{}}}%
    \hypertarget{assessment\theassessmentlabel}{} \stepcounter{assessmentlabel}
    }

    先設定一個計數器 assessmentlabel (因為我們不會在文稿中用到它, 所以故意讓名稱很長, 以免以後定義其他的會重複), 這個計數器比較特別的是他是從 0 開始的。

    定義一個新的指令 \ansass (這裡用 xfithen 套件裡的 \DeclareDocumentCommand 是純粹因為我懶得改, 這裡其實用 \newcommand 就可以了), 這個指令很長, 主要是將 \ansass{} 裡的文字貼上一個標籤 (label)。

    為了不要手動設定標籤, 所以我們利用計數器讓標籤能自動編號 (assessment\theassessmentlabel), 如上一條目所說的, 用 \theassessmentlabel 才能真的讓 latex 看懂這個數字, 所以指令最後就加上 \stepcounter{assessmentlabel} 讓計數器加 1。

    於是, 每當我們輸入一次 \ansass{}, 就會自動貼上標籤 assessment0, assessmnet1, .... 依序下去。而 \ansass{} 這個指令只會貼標籤, 是不會在文稿上顯示出任何文字的, 要顯示文字, 我們要用指令 \ref{}。(例如 \ref{assessmnet1}, 當然這很麻煩, 所以我們用自動計數+指令來簡化它)

    %自我評量答案, 直接在格子中填入 \ansassis 即可
    \newcounter{assessmentans}\setcounter{assessmentans}{0}
    \newcommand{\ansassis}{\ref{assessment\theassessmentans}\stepcounter{assessmentans}}

    接著我們定義一個新的計數器和指令, 讓 ref 也能自動編號的把剛剛所貼的標籤顯示出來。這個指令很單純, \ansassis 就是讓 \ref{} 的 {} 裡自動填上 assessment0, assessmnet1, ....。

  4. 用 loop 讓 latex 自動計算題數來排版解答
    雖然已經簡化了指令, 可是我們還是不想自己手動數我們到底輸入了幾個 \ansass 再重複的輸入 \ansassis 來編輯解答, 所以我們接下來再設定一個迴圈 (loop)。

    \newcounter{assloop}
    \setcounter{assloop}{1}
    \newcommand\ansassloop[2]{
    \loop \ifnum\numexpr\value{assloop}-1 < #1
    #2%
    \stepcounter{assloop}%
    \repeat }

    定義 \ansassloop{}{} 這個指令, 這個指令需要兩個參數, 第一個 {} 放數字, 代表要重複的次數, 第二個 {} 裡放要重複的文字。舉例來說, 如果輸入 \ansassloop{5}{xyz}, 那麼就會輸出 xyzxyzxyzxyzxyz。

    解釋一下指令內部。\ifnum 是條件式, 可用來比較數字大小, \numexpr 用來計算數字 (在 latex 裡直接輸入數字運算像 6+2 是沒有用的), 而條件式就是 assloop 這個計數器的值 \value{assloop} 減 1 < #1 (事實上就是計數器的值小於等於 #1) 的話, 就顯示 #2 並同時將計數器加 1, 然後重複 (\reapeat 回 \loop), 一直重複到計數器的值大於 #1 才停止。

    這個指令解決了重複輸入的問題, 接下來要解決手動數 \ansass 個數的問題。

    \ansassloop{\theassessmentlabel}{(\no) \ansassis ; \;}
    是的, 就這麼一行, 簡單的說, 就是把 \ansassloop 的第一個 {} 裡放上 \theassessmentlabel, 所以只要 assessmentlabel 這個計數器的值為何, (\no) \ansassis ; \; 就會重複幾次。而 assessmentlabel 這個計數器的值恰恰正好是文稿裡 \ansass 的個數了。(其實如果你很仔細的看, 你會發現這句話有點怪怪的, 至於是什麼問題以及為什麼我要這麼做, 這裡就賣個關子吧。)

    另外, 這個方法沒辦法用在 \item 上, 也就是說在 \ansloop 第二個 {} 放 \item 是沒有用的, 如果要用 \item, 必須改用 multido 這個套件。

  5. 結語
    最後簡單的把一些沒有說到的內容再補充一下。首先, 參考解答的方框我是用 tcolorbox 排版的, 可以去找他的介紹, 美化版面相當好用。

    \Z 和 \Q 這個指令是我用來強迫不論任何使用任何字體, 句號和頓號都要用 cwTeX 的字體排版 (也就是置於文章句末的下方)。

    \everymath{\textstyle} 和 \everymath{\displaystyle} 用來改變隨文數學式的大小。

    \CrossOpenShadow 這個十字架符號是來自於 bbding 這個 package。