KTUG마당은 KTUG를 방문하는 모든 이용자가 대화를 나누고 소식을 전하는 곳입니다.
- 로그인 없이 자유롭게 글을 읽고 쓸 수 있는 철학은 처음과 같이 계속됩니다.
- Team Blog의 글을 이곳 게시판의 "정보글"로 모았습니다. Team blog는 기고자가 올린 글에 질문을 받는 부담을 줄이기 위하여 댓글을 허용하지 않았습니다. 그러나 이곳 게시판으로 모으면서 댓글을 달 수 있습니다. 게시물을 작성하실 때 댓글을 원하지 않으시면 댓글을 허용하시지 않으시기를 바랍니다. 또한 불필요한 소모성 댓글을 달지 않도록 주의하여 주시기를 바랍니다.
- TeX과 관련된 질문이나 답변은 QnA 마당을 이용하십시오. TeX과 관련된 질문은 지웁니다
- MathJax를 이용한 수식조판을 사용하실 수 있습니다. 여기를 참조하세요.
- 스팸 글을 막기 위하여 짧은 시간 내에 다시 글이 등록되는 IP를 막거나, 광고 글을 막기 위하여 금지어로 .com, .net 등을 설정하고 있습니다. 다소간의 불편함이 있으시더라도 양해 바랍니다.
- 금지어에서 stackexchange, stackoverflow, ctan, overleaf, , github, google.com, gmail.com, .org, .io, sil.org, wiki.com, tistory.com등은 해제하였습니다.
- 사용하는 편집기는 CKeditor입니다. 편집기에서 [enter]를 누르면 <p> 태그가 들어가고, 문단으로 생각하고 한줄을 비웁니다. 글줄만 바꾸려면 shift-enter 를 누르시면 <BR>가 들어가므로 용도에 맞게 나누어 쓸 수 있습니다.
kriss 님이 갖고 있는 문제
(http://www.ktug.org/xe/index.php?document_srl=231520&mid=KTUG_QnA_board), 조건부 텍스트를 구현하는 데에 \ifthenelse 같은 명령이 충분해 보이지 않습니다. 그 문제가 제가 고민했던 것과 비슷해 보여서 제가 expl3를 이용하여 만든 방법을 소개하고자 합니다.
일반 텍 사용자들이 expl3를 굳이 익힐 필요가 없다고 생각해 왔습니다. 그리고 텍이나 레이텍에 비해 이해하기가 좀 어렵기도 합니다. 왜냐하면 텍이나 레이텍 명령들은 단지 매크로 집합이기 때문에 필요한 만큼만 베껴 쓰면서 이해하고 응용하는 것으로 족하지만, expl3는 텍 프로그래밍 언어이기 때문에 기반 개념들을 전반적으로 이해해야 합니다. 좌우지간 expl3도 결국은 텍으로 만들어진 것입니다.
다시 조건부 텍스트 문제로 돌아와서, 한 가지 조건만 가려서 이것 또는 저것을 표현해야 한다면 boolean 변수를 사용하는 것만으로 족합니다. 하지만 여러 가지 조건을 써야 한다면 \newif 또는 \newboolean을 되풀이하여 쓰는 것이 성가시게 됩니다. 그래서 저는 조건들을 한두 명령만으로 제어할 수 있는 방법을 궁리했습니다.
예를 들어, A4로 조판할 때 또는 B5로 조판할 때 서로 다른 여백, 인쇄용으로 만들 때 재단선 포함하고 웹용으로 만들 때는 재단선 제외, 등등. 그런데 조건들이 서로 영향을 주는 경우도 있지만, 제가 예로 든 것과 같은 것들은 서로 무관합니다. 그래서 다음과 같은 형태로 적는 것만으로 충분하고 편할 것 같습니다.
\if{A}{ true ... }
\if{B}{ true ... }
때때로 "거짓"일 때 다르게 나타내야 할 수도 있습니다. 그것은 옵션 인자로 만드는 것이 편하겠습니다.
\if{A}{ true ...}[ false ... ]
\if{B}{ true ...}[ false ... ]
문제는 저 "A", "B" 같은 조건들을 어떻게 만들고 관리하느냐는 것입니다. 제 아이디어는 이렇습니다.
1) 조건들을 콤마 리스트 (콤마로 분리되는 목록 형태의 변수)에 넣는다. 하나 또는 둘 이상, 그리고 나중에 더 추가하는 것이 가능해야 한다.
2) 조어진 조건이 콤마 리스트에 포함되어 있으면 참 텍스트를, 거짓이라면 그리고 그 경우에 옵션 인자로 주어진 텍스트가 있다면 그것을 표시한다.
\documentclass{article}
\usepackage{xparse, expl3}
\usepackage{environ}
\usepackage{xcolor}
\def\True{Wovon man nicht sprechen kann, darüber muß man schweigen.}
\def\False{About that, of which one cannot speak, one must remain silent.}
\ExplSyntaxOn
\clist_new:N \g_content_type
\NewDocumentCommand \SetContentType { m } {
\clist_gput_right:Nn \g_content_type {#1} %인자를 콤마 리스트 변수에 계속 추가
}
\NewDocumentCommand \IfContentType { m +m o }{
\bool_set_false:N \g_tmpa_bool % "_tmpa_" 또는 "_tmpb_"가 붙은 것들은 사용자를 위해 expl3에 정의되어 있는 변수
\clist_map_inline:Nn \g_content_type % 변수에 포함된 낱말들을 하나씩 꺼내어
{
\str_if_eq:nnT {##1}{#1} % 인자와 비교하여
{
\bool_set_true:N \g_tmpa_bool % 같으면 "참"으로 설정
}
}
\bool_if:NTF \g_tmpa_bool %"참"이면 이것
{
\group_begin:
\color{blue} #2
\group_end:
}{
\IfValueT { #3 } %"거짓"인데 옵션 인자가 있다면 이것
{
\group_begin:
\color{gray} #3
\group_end:
}
}
}
\NewEnviron{IfThisType}[1]{ %환경으로 쓰고 싶다면, 대신 옵션 인자는 사용 불가
\IfContentType{#1}{\BODY}
}
\ExplSyntaxOff
\begin{document}
\SetContentType{question}
\SetContentType{answer, explanation}
% question이 변수에 포함되어 있으므로 true
\IfContentType{question}{\True}
% query가 변수에 포함되어 있지 않으므로 false
\IfContentType{query}{\True}[\False]
% answer가 변수에 포함되어 있으므로 true
\begin{IfThisType}{answer}
\True
\end{IfThisType}
% response가 변수에 포함되어 있지 않으므로 무시
\begin{IfThisType}{response}
\True
\end{IfThisType}
\end{document}
댓글 6
-
Kriss
2018.08.22 01:56
-
yihoze
2018.08.22 11:28
\ifContentType{\FOO} 처럼 문자열뿐만 아니라 변수를 인자로 전달해야 하는 경우도 있을 것 같습니다. 그 경우에는 다음과 같이 고치면 됩니다. \exp_args:Nnf는 (\expandafter처럼) 마지막 토큰을 확장합니다.
\exp_args:Nnf \str_if_eq:nnT {##1}{#1}
-
noname
2018.08.27 16:46
재미난 글 잘 봤습니다. \IfContentType을 조금 간단히 해보았습니다.
\NewDocumentCommand \IfContentType { m +m o }{ \clist_if_in:NnTF \g_content_type { #1 } { \group_begin: \color{blue} #2 \group_end: } { \IfValueT { #3 } { \group_begin: \color{gray} #3 \group_end: } } }
-
yihoze
2018.08.28 09:00
좋은 지적이십니다. 말씀하신 대로 \clist_if_in:NnTF를 쓰는 게 더 간결합니다. 실은 처음에 부정 조건이 필요한 경우도 고려해서 문자열을 비교하고 불린 변수를 설정하는 과정을 분리했는데요. 쉽게 말하자면, \IfContentType{}[...] 이렇게 쓰는 게 좀 불편하니까, \IfNotContentType{...} 이렇게도 쓰려고요. 그런데 실제로는 그렇게 써야 할 일이 아직까지 없었던 것 같습니다.
-
Kriss
2018.09.03 22:20
올려주신 코드를 공부함에 있어, 눈칫밥으로 대략적인 의미를 파악하는데는 어려움이 없었습니다. 그러나 이 코드를 내가 짤 수 있는가에 대해서는 물음표인지라, 결국 expl3를 먼저 공부해야겠다는 생각이 들었습니다.
그동안 공식 document만으로는 딱딱하고 왜 이짓을 해야하는지 모르겠고(특히 함수명에 Too much information을 적는 부분이 제일 의아했습니다) 효율성도 떨어져보여서 흥미를 금방 잃고 포기하기 일쑤였는데, 다행히 흥미로운 포스트(https://www.texdev쩜net/2014/02/02/programming-latex3-a-summary-so-far/) 발견하고, 짬짬이 시간나는대로 이 포스팅을 통해 expl3에 대해 아주 조금이나마 이해하고 나니, 올려주신 코드에 대해서도, expl3에 대해서도 많이 납득되었고 해볼만 할 것 같습니다.
감사합니다. 더 열심히 해보겠습니다.
-
yihoze
2018.09.04 09:32
오~ Joseph Wright, 이 분 디테일 쩌~네요. ^^ 역시나 LaTeX3 프로젝트 멤버시네요.
https://www . latex-project. . org/about/team/
제게도 큰 도움이 되겠습니다. 고맙습니다.
정말 감사합니다. ifthenelse로 코드를 수정하고 고치면서 이건 좀 아니다 싶은 부분이 많았는데 큰 도움이 될 것 같습니다.
이번주는 너무 바빠 작성해주신 코드를 분석할 수 없지만, 다음주부터 능력이 닿는데까지 공부하고 분석해봐야겠습니다.
그 후엔 pgfkeys의 양식으로 이미 작성된 수천개 이상의 개별 문항 파일을 expl3로 짠 양식으로 일괄 변환하는 것에 대한 고민도 해봐야겠네요..