標籤:

用LaTeX編寫習題集有哪些宏包可以使用?

下面是我從一些書的目錄截的圖,想像中的習題集大概就這些個樣式。習題、提示、答案都分開,且按習題的順序排列。

另外,請問一下這種目錄如何生成?


一個簡單的小結見 LaTeX 排版課本,怎麼將課後習題的題目和答案分開?

但已有的這些宏包都是一個題目一個解答的形式,缺少提示環境。貌似有必要再來輪一個。

第一和第三張圖片的格式類似,第二張圖片的格式貌似是輸入的時候,理論、問題和提示在一起輸入,最後再將他們分成三部分,要多費些周折。我們僅實現第三張圖片的效果。原理在上面的回答中已經提到,不再贅述。

% !TeX program = XeLaTeX
% !TeX encoding = UTF-8

documentclass{ctexbook}

usepackage{verbatim}
usepackage{hyperref}

setcounter{secnumdepth}{0}

makeatletter
ExplSyntaxOn

%% 問題統一編號

ewcounter { problem }

%% 問題列表
NewDocumentEnvironment { problems } { }
{
section { 習題 }
@@_list_parameter:
list
{ heproblem . }
{ cs_set_eq:NN makelabel @@_problem_label:n }
cs_set_eq:NN hint @@_hint_env:w
cs_set_eq:NN solution @@_solution_env:w
cs_set_eq:NN problems @@_sub_problems:
}
{ endlist }
%% 提示和解答環境的定義
cs_new_protected:Npn @@_declear_env:nnN #1#2#3
{
cs_new_protected_nopar:cpx { @@_ #1 _env:w }
{
exp_not:N @@_list_check:
exp_not:c { @@_ #1 _begin: }
}
cs_new_protected_nopar:cpx { @@_ #1 _begin: }
{
exp_not:N @@_begin:NNNNnnn
{ exp_not:c { g_@@_ #1 _bool } }
{ exp_not:c { c_@@_ #1 _file_str } }
{ exp_not:c { g_@@_ #1 _iow } }
{ exp_not:c { @@_ #1 _begin: } }
{#1}
{ ool_if:NT #3 { exp_not:c { @@_ #1 s_heading: } } }
{ exp_not:n {#2} }
}
cs_new_protected_nopar:cpx { print #1 s }
{ msg_term:n { NO ~ str_upper_case:n {#1} ! } }
}
@@_declear_env:nnN { hint } { 習題提示 } c_false_bool
@@_declear_env:nnN { solution } { 習題解答 } c_true_bool
%% 提示和解答環境的內部實現
cs_new_protected:Npn @@_begin:NNNNnnn #1#2#3#4#5#6#7
{
@@_initial:NNNNnnn #1#2#3#4 {#5} {#6} {#7}
@@_open:NNNNnn #1#2#3#4 { #5 s } {#6}
}
cs_new_protected:Npn @@_initial:NNNNnnn #1#2#3#4#5#6#7
{
ool_new:N #1
str_const:Nx #2 { c_sys_jobname_str . #5 s.tex }
iow_new:N #3
AfterLastShipout { iow_close:N #3 }
cs_gset_protected_nopar:cpn { print #5 s }
{
ool_if:NT #1
{
ool_gset_false:N #1
iow_now:Nx #3 { c_backslash_str end { #5 s } }
iow_close:N #3
cs_gset_protected_nopar:Npn #4
{ @@_open:NNNNnn #1#2#3#4 { #5 s } {#6} }
}
chapter {#7}
file_input:n {#2}
}
cs_new_protected_nopar:cpn { #5 s }
{
@@_list_parameter:
list
{ }
{ cs_set_eq:Nc makelabel { @@_ #5 _label:n } }
cs_set_eq:NN problems @@_sub_problems:
}
cs_new_eq:cN { end #5 s } endlist
l_if_empty:nF {#6}
{ @@_heading_initial:NNn #6#3 { #5 s } }
}
%% 用章的編號分隔答案列表
cs_new_protected:Npn @@_heading_initial:NNn #1#2#3
{
cs_new_protected_nopar:Npn #1
{
int_compare:nNnF { value { chapter } } = g_@@_chapter_int
{
int_gset:Nn g_@@_chapter_int { value { chapter } }
iow_now:Nx #2
{
c_backslash_str end {#3} iow_newline:
iow_newline:
c_backslash_str section { CTEXthechapter } iow_newline:
c_backslash_str begin {#3}
}
}
}
}
cs_new_protected:Npn @@_open:NNNNnn #1#2#3#4#5#6
{
ool_gset_true:N #1
iow_open:Nn #3 {#2}
l_if_empty:nF {#6} { @@_write_heading:N #3 }
iow_now:Nx #3 { c_backslash_str begin {#5} }
cs_gset_protected_nopar:Npn #4
{ @@_write:Nn #3 {#6} }
#4
}
cs_new_protected:Npn @@_write_heading:N #1
{
int_gset:Nn g_@@_chapter_int { value { chapter } }
set@display@protect
iow_now:Nx #1 { c_backslash_str section { CTEXthechapter } }
}
int_new:N g_@@_chapter_int
%% 將提示和解答寫入外部文件
cs_new_protected:Npn @@_write:Nn #1#2
{
set@display@protect
#2
exp_args:NNo iow_now:Nn #1 { g_@@_curr_item_tl }
cs_set_protected_nopar:Npn verbatim@processline
{ iow_now:Nx #1 { he verbatim@line } }
cs_set_eq:NN do char_set_catcode_other:N
dospecials
char_set_catcode_active:n { 13 }
verbatim@start
}
%% 編號格式,支持 problem -&> hint -&> solution -&> problem 的超鏈接
%% 帶星編號用 item[*] 的形式輸入
cs_new_protected:Npn @@_problem_label:n #1
{
str_if_eq:nnTF {#1} { @itemlabel }
{ @@_problem_label_aux:n {#1} }
{
cs_set_eq:NN prefix @@_label_prefix:nn
@@_problem_label_aux:n
{ protect prefix {#1} { @itemlabel } }
}
}
cs_new_protected:Npn @@_label_prefix:nn #1#2
{ hbox_overlap_left:n {#1} #2 }
cs_new_protected:Npn @@_problem_label_aux:n #1
{

efstepcounter { problem }
set@display@protect
l_gset:Nx g_@@_curr_item_tl
{ c_backslash_str item [ {#1} { @currentHref } ] }
set@typeset@protect
exp_args:No @@_hyper_link:nn
{ @currentHref .hint }
{ @@_label_format:n {#1} }
}
cs_new_protected:Npn @@_hint_label:n #1
{ @@_label:nnnn { .hint } { .solution } #1 }
cs_new_protected:Npn @@_solution_label:n #1
{ @@_label:nnnn { .solution } { } #1 }
cs_new_protected:Npn @@_label:nnnn #1#2#3#4
{
cs_set_eq:NN prefix @@_label_prefix:nn
@@_hyper_anchor:n { #4#1 }
@@_hyper_link:nn
{ #4#2 }
{ @@_label_format:n {#3} }
}
l_new:N g_@@_curr_item_tl
cs_new_protected:Npn @@_label_format:n #1
{
ormalfont fseries #1 }
cs_new_protected_nopar:Npn @@_hyper_link:nn
{ hyper@link { link } }
cs_new_protected_nopar:Npn @@_hyper_anchor:n #1
{ Hy@raisedlink { hyper@anchor {#1} } }
%% 設置列表參數,item 間無額外垂直間距,編號正常縮進等,相當於 enumitem 包的 wide
cs_new_protected_nopar:Npn @@_list_parameter:
{
cs_set_protected_nopar:cpn
{ @list int_to_roman:n { @listdepth + 1 } }
{
skip_zero:N opsep
skip_zero:N parsep
skip_zero:N itemsep
skip_zero:N partopsep
dim_zero:N labelwidth
dim_zero:N leftmargin
dim_zero:N
ightmargin
dim_set:Nn labelsep { .5em }
dim_set_eq:NN listparindent parindent
dim_set:Nn itemindent { labelsep + parindent }
@@_fix_first_item_indent:
}
}
cs_new_protected_nopar:Npn @@_fix_first_item_indent:
{
ox_if_empty:NF @labels
{
hbox_set:Nn @labels
{
ox_use_clear:N @labels
skip_horizontal:n { labelsep + labelwidth - itemindent }
}
}
}
%% 小題環境
cs_new_protected_nopar:Npn @@_sub_problems:
{
@@_list_parameter:
list
{ ( arabic { enumiv } ) }
{ usecounter { enumiv } }
}
%% 提示和解答環境應該在 item 之後使用
cs_new_protected_nopar:Npn @@_list_check:
{
if@inlabel
indent par
global @inlabelfalse
else:
if@newlist @noitemerr fi:
fi:
}

ExplSyntaxOff
makeatother

egin{document}

frontmatter

ableofcontents

mainmatter

chapter{天地玄黃}

egin{problems}

item 證明勾股定理。
egin{hint}
運用面積法。
end{hint}
egin{solution}
略。
end{solution}

item[*]
egin{problems}
item 證明正弦定理。
item 證明餘弦定理。
end{problems}
egin{hint}
egin{problems}
item 運用面積法。
item 運用面積法。
end{problems}
end{hint}
egin{solution}
egin{problems}
item 略。
item 略。
end{problems}
end{solution}

end{problems}

chapter{宇宙洪荒}

egin{problems}

item 證明正弦定理。
egin{hint}
運用面積法。
end{hint}
egin{solution}
略。
end{solution}

item 證明餘弦定理。
egin{hint}
運用面積法。
end{hint}
egin{solution}
略。
end{solution}

end{problems}

ackmatter

printhints
printsolutions

end{document}


在編寫大型文檔時,我們通常將文檔按章拆成多個子文檔,然後在主文檔中用 inputinclude 命令載入。另外我們也希望各個子文檔也可以獨立地編譯,這個只要用 docmute 包,並將導言區內容作為一個獨立文件載入就可以了。

如果希望習題、提示、答案這三者在同一個地方輸入,而在不同地方輸出,可以讓各章習題也作為一個獨立文件。我們只需要先後三次載入同一個文件,而每次都自動忽略掉不需要的部分。忽略某些環境的功能可以通過 comment 包實現。

這樣處理的第一個好處是比較簡單,容易掌握和定製。第二個好處 PDF 的反向搜索將跳轉到正確的文件,方便編輯和修改習題答案。

先看最後得到的目錄頁:

以下代碼在 CTeX 2.9.2 中測試通過。需要將所有文件內容都以 UTF8 編碼保存,建議用 XeLaTeX 命令編譯。

文檔的結構如下:

  • preamble.tex:各文件共用的導言區內容

  • book.tex:主文件
  • chap1.tex,chap2.tex:各章內容
  • exer1.tex,exer2.tex:各章習題

首先是 preamble.tex 文件的內容(其中載入了 docmute 和 comment 包):

% -*- coding: utf-8 -*-
usepackage[UTF8]{ctexcap}
CTEXsetup[number={第chinese{section}節}]{section}

usepackage{hyperref}
hypersetup{colorlinks,linkcolor=blue,bookmarksnumbered,unicode=true}

usepackage{docmute}

usepackage{comment}

ewcounter{question}

ewcommand{envq}{parigskip
efstepcounter{question}arabic{question}. ignorespaces}

ewenvironment{question}{envq}{}

ewcounter{hint}

ewcommand{envh}{parigskip
efstepcounter{hint}arabic{hint}. ignorespaces}

ewenvironment{hint}{envh}{}

ewcounter{answer}

ewcommand{enva}{parigskip
efstepcounter{answer}arabic{answer}. ignorespaces}

ewenvironment{answer}{enva}{}

ewcommand{inputquestion}[1]{%
excludecomment{hint}%
excludecomment{answer}%

enewenvironment{question}{envq}{}%
setcounter{question}{0}%
input{#1}%
}

ewcommand{inputhint}[1]{%
excludecomment{question}%
excludecomment{answer}%

enewenvironment{hint}{envh}{}%
setcounter{hint}{0}%
input{#1}%
}

ewcommand{inputanswer}[1]{%
excludecomment{question}%
excludecomment{hint}%

enewenvironment{answer}{enva}{}%
setcounter{answer}{0}%
input{#1}%
}

其次是 book.tex 文件的內容(注意各習題文件都載入了三遍):

% -*- coding: utf-8 -*-
documentclass[b5paper,11pt,openany]{book}

input{preamble}

egin{document}

ableofcontents

input{chap1}

section*{習題一}addcontentsline{toc}{section}{習題一}
inputquestion{exer1}

input{chap2}

section*{習題二}addcontentsline{toc}{section}{習題二}
inputquestion{exer2}

chapter*{習題提示}addcontentsline{toc}{chapter}{習題提示}

section*{第一章}addcontentsline{toc}{section}{第一章}
inputhint{exer1}

section*{第二章}addcontentsline{toc}{section}{第二章}
inputhint{exer1}

chapter*{參考答案}addcontentsline{toc}{chapter}{參考答案}

section*{第一章}addcontentsline{toc}{section}{第一章}
inputanswer{exer1}

section*{第二章}addcontentsline{toc}{section}{第二章}
inputanswer{exer2}

end{document}

然後是 chap1.tex 和 chap2.tex 的文件內容(這兩個文件也可以單獨編譯):

% -*- coding: utf-8 -*-
documentclass[b5paper,11pt]{book}

input{preamble}

egin{document}

chapter{測試測試}

section{小節標題}

正文正文正文正文正文

section{小節標題}

正文正文正文正文正文

end{document}

最後是 exer1.tex 和 exer2.tex 的文件內容(這兩個文件也可以單獨編譯):

% -*- coding: utf-8 -*-
documentclass[b5paper,11pt]{article}

input{preamble}

egin{document}

egin{question}
問題問題問題問題
end{question}

egin{hint}
提示提示
end{hint}

egin{answer}
答案答案
end{answer}

egin{question}
問題問題問題問題
end{question}

egin{hint}
提示提示
end{hint}

egin{answer}
答案答案
end{answer}

end{document}


加在preamble:

usepackage{exsheets}

RenewQuSolPair

{question}[name=Question]

{solution}[name=Solution]

SetupExSheets{

question/print = true ,

solution/print = true ,

counter-format = qu ,

counter-within = section ,

totoc = true

}

makeatletter

@addtoreset{question}{section}

makeatother

正文里:

egin{question}

blabla

end{question}

egin{solution}

blabla

end{solution}


推薦閱讀:

為什麼Latex輸出的PDF文檔是空白的?
LaTeX 輸入矩陣裡面的元素時,使用 dfrac 後,發現字母有重疊怎麼辦?
LaTeX 使用 listings 宏包插入代碼時,如何將代碼的字體設定為 Monaco?
xelatex編譯加粗楷體為什麼會失敗?
TeXLive如何查看一個宏包的目錄結構(TDS)?

TAG:排版 | LaTeX |