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>가 들어가므로 용도에 맞게 나누어 쓸 수 있습니다.
자유글 재미로 해보는 매크로 작성
2015.03.02 08:26
연습문제 1:
입력받은 숫자에 세 자리마다 쉼표를 추가하는 매크로 \mynumformat을 작성하시오.
다음 파일이 정상적으로 컴파일되어 1,000 10,000 100,000이 표시되도록 preamble에 매크로 정의를 적어넣습니다.
\documentclass{minimal}
\begin{document}
\mynumformat{1000}
\mynumformat{10000}\mynumformat{100000}
\end{document}
- siunitx나 numprint 등 이미 있는 패키지를 사용하지 않습니다.
- 소수 표현을 문제삼지 않습니다. 인자는 항상 정수입니다.
- \usepackage{expl3,xparse}, \usepackage{luacode}는 인정합니다.
금일 오후 6시까지 제출된 답안에 대하여 엄정 심사(?)하여 엄청난 기념품을 증정하... 게 될지 어떨지...
참고: http://faq.ktug.org/faq/LittleTree/ReadingTeXbook/2006-08#26 "숫자에 콤마 넣기"
댓글 20
-
nanim
2015.03.02 09:23
-
작나
2015.03.02 10:34
% 참고: http://lua-users 닷 org/wiki/FormattingNumbers
\usepackage{luacode}
\begin{luacode*}
function comma_value(n)
local match = string.match
local left,num,right = match(n,'^([^%d]*%d)(%d*)(.-)$')
return left..(num:reverse():gsub('(%d%d%d)','%1,'):reverse())..right
end
\end{luacode*}
\def\mynumformat#1{\luaexec{tex.sprint(comma_value(#1))}}
-
DohyunKim
2015.03.02 11:47
위 expl3 코드가 하는 일을 풀어보면 대체로 아래처럼 될 거 같습니다.
\def\mynumformat#1{%
\def\mytmpa{}% 문자열을 역순으로 넣을 곳
\mynumformati#1\end
}
\def\mynumformati#1{%
\ifx#1\end
\expandafter\mynumformatii
\else
\edef\mytmpa{#1\mytmpa}% 문자열 역순으로 넣기
\expandafter\mynumformati
\fi
}
\def\mynumformatii{%
\count255=0
\def\mytmpb{}% 쉼표 추가하여 재역순으로 넣을 곳
\expandafter\mynumformatiii\mytmpa\end
}
\def\mynumformatiii#1{%
\ifx#1\end
\expandafter\mynumformativ\mytmpb\end
\else
\edef\mytmpb{#1\mytmpb}% 재역순으로 넣기
\advance\count255 by 1
\ifnum\count255=3
\edef\mytmpb{,\mytmpb}% 세자리마다 쉼표 추가
\count255=0
\fi
\expandafter\mynumformatiii
\fi}
\def\mynumformativ#1#2\end{\ifx#1,\else#1\fi #2} % 첫머리 쉼표 제거 -
DohyunKim
2015.03.02 12:32
위 루아 코드 비슷하게 작동하게 하려면 첫번째 명령을 수정합니다.
\def\mynumformat#1{%
\mynumformatx#1\end
}
\def\mynumformatx#1{%
#1%
\def\mytmpa{}% 문자열을 역순으로 넣을 곳
\mynumformati
}마지막 명령도 수정합니다.
\def\mynumformativ#1\end{#1} % 그대로 출력
-
nanim
2015.03.02 13:07
뒤집지 않고 fifo
\newcount\m \newcount\n \newcount\r
\def\fifo#1{\ifx\ofif#1\ofif\fi\process#1\fifo}
\def\ofif#1\fifo{\fi}
\def\mynumformat#1{\n=0
\def\process##1{\advance\n by1}%
\fifo#1\ofif
\m=\n \r=\n \divide\m by3 \multiply\m by3 \advance\r by-\m
\ifcase\r \or \r=2\or \r=1\fi \m=0
\def\process##1{\advance\r by1 \advance\m by1
##1\ifnum\r=3 \ifnum\m=\n \else,\fi\r=0 \fi}%
\fifo#1\ofif} -
nanim
2015.03.02 16:46
regex 방법에서 첫머리 콤마도 regex로 지우면 되겠네요.
\NewDocumentCommand \mynumformat { m }
{
\tl_set:Nn \l_tmpa_tl { #1 }
\tl_reverse:N \l_tmpa_tl
\regex_replace_all:nnN { (\d\d\d) } { \1, } \l_tmpa_tl
\regex_replace_once:nnN { ,$ } { } \l_tmpa_tl
\tl_reverse:N \l_tmpa_tl
\tl_use:N \l_tmpa_tl
} -
에드
2015.03.03 10:31
정규식도 되는군요! ㄷㄷㄷ -
Progress
2015.03.02 23:33
엉엉, 이걸 "재미로" 하는 게 가능합니까? -
어떤이
2015.03.03 09:45
재밌네요. nanim님의 마지막 코드는 파이썬으로 번역하면 대략 다음과 같은 것이군요.
import re def mynumformat(s): l = list(s) l.reverse() s = "".join(l) s = re.sub("(\d\d\d)", r"\1,", s) s = re.sub(",$", "", s) l = list(s) l.reverse() s = "".join(l) return s print(mynumformat("1000")) print(mynumformat("10000")) print(mynumformat("100000"))
-
어떤이
2015.03.03 10:00
제가 공부가 짧아 expl3로 구현은 못하겠지만, 다음과 같은 파이썬 코드를 expl3로 옮길 수 있나요?
def mynumformat(s): l = list(s) l.reverse() l_l = len(l) t_l = [] i = 1 for c in l: t_l.append(c) if i % 3 == 0 and i < l_l: t_l.append(",") i += 1 t_l.reverse() s = "".join(t_l) return s print(mynumformat("1000")) print(mynumformat("10000")) print(mynumformat("100000"))
얼핏 문서를 보니 토큰 리스트에서 토큰을 하나씩 가져올 수도 있고, 빈 토큰 리스트를 만들고 토큰을 하나씩 추가하는 것도 가능해 보이기는 하던데요. -
nanim
2015.03.03 10:41
제시하신 코드를 "충실히" 직역하였습니다.
\ExplSyntaxOn
\NewDocumentCommand \mynumformat { m }
{
\tl_set:Nn \l_tmpa_tl { #1 }
\tl_reverse:N \l_tmpa_tl
\int_set:Nn \l_tmpa_int { \tl_count:N \l_tmpa_tl }
\tl_clear:N \l_tmpb_tl
\int_set:Nn \l_tmpb_int { \c_one }
\tl_map_inline:Nn \l_tmpa_tl
{
\tl_put_right:Nn \l_tmpb_tl { ##1 }
\bool_if:nT
{ \int_compare_p:n
{ \int_mod:nn { \l_tmpb_int } { 3 } = \c_zero }
&&
\int_compare_p:n
{ \l_tmpb_int < \l_tmpa_int }
}
{
\tl_put_right:Nn \l_tmpb_tl { , }
}
\int_incr:N \l_tmpb_int
}
\tl_reverse:N \l_tmpb_tl
\tl_use:N \l_tmpb_tl
}
\ExplSyntaxOff다만 expl3 입장에서 말하자면 출력을 위한 토큰열에 put_right (append)하였다가 나중에 reverse하는 것보다는 put_left (prepend)하면 마지막의 \tl_reverse:N 호출 한 번을 줄일 수 있겠습니다.
-
어떤이
2015.03.03 16:07
감사합니다. 네이밍 컨벤션에서부터 상수 사용법까지 저에게 죻은 예제가 되었습니다. expl3의 코딩 스타일은 함수형 언어를 닮은 부분이 많군요. 아주 재미 있습니다.
-
DohyunKim
2015.03.03 10:08
속도면에서는 lua의 승리네요. 아래 코드를 실행해보면 제 시스템에서
lua 1.1초
fifo 2초 (두 버전의 평균)
꼬리재귀 3.8초
expl3 \infty
\newcount\mycnt \mycnt=0
\loop
\advance\mycnt 1
\ifnum\mycnt<10000
\mynumformat{123456789012345678901234567890123456789012345678901234567890}
\endgraf
\repeat -
nanim
2015.03.03 11:30
당연하다면 당연한... 결과라고 생각합니다.
expl3가 극악의 실행속도를 보여주는 이유는 l3regex 때문으로 짐작됩니다.
expl3의 low-level 코딩, 이를테면 l3quark 꼬리재귀 방식으로 코딩한다면 속도 개선이 현저할 거 같고요, 일반적인(l3regex를 사용하지 않는) expl3 코드라면 (그래봤자 다른 것보다는 느리겠지만) 결과를 볼 수 있을 정도는 되리라 봅니다.
-
Progress
2015.03.05 09:22
다음과 같이 입력했을 때
\mynumformat{012}
\mynumformat{0123}
\mynumformat{012345}
\mynumformat{0123456}
\mynumformat{01234567}
\mynumformat{012345678}
\mynumformat{0123456789}
\mynumformat{01234567890} -
DohyunKim
2015.03.05 11:40
한 가지 방법은 number를 강제하는 것입니다. 위 꼬리재귀 방식의 첫번째 매크로를 수정해보면,
\def\mynumformat#1{%
\expandafter\mynumformatx\number#1 \end
}단점은 텍의 숫자 범위가 한정돼 있어서 엄청 큰 수를 받아들이지 못한다는 것. 더 나은 방법을 기다려봅니다.
-
nanim
2015.03.05 12:11
당장 생각나는 무식한 방법은... 앞머리에 나오는 '0'들을 다 지워버리는 것인데요,
다음 함수를 처음 \tl_reverse:N 하기 전에 한 번 불러주는 겁니다.
\cs_new:Npn \remove_heading_zeros:n #1
{
\tl_set:Nn \l_tmpa_tl { #1 }
\tl_set_eq:NN \l_tmpb_tl \l_tmpa_tl
\tl_map_inline:Nn \l_tmpa_tl
{
\tl_if_eq:nnTF { ##1 } { 0 }
{
\exp_args:NNx \tl_set:Nn \l_tmpb_tl { \tl_tail:N \l_tmpb_tl }
}
{
\tl_map_break:
}
}
\tl_set_eq:NN \g_tmpa_tl \l_tmpb_tl
}mynumformat은 다음과 같이 하고요.
\NewDocumentCommand \mynumformat { m }
{
\tl_set:Nn \g_tmpa_tl { #1 }
\remove_heading_zeros:n { #1 }
\tl_reverse:N \g_tmpa_tl
\int_set:Nn \l_tmpa_int { \tl_count:N \g_tmpa_tl }
\tl_clear:N \g_tmpb_tl
\int_set:Nn \l_tmpb_int { \c_one }
\tl_map_inline:Nn \g_tmpa_tl
{
\tl_put_left:Nn \g_tmpb_tl { ##1 }
\bool_if:nT
{ \int_compare_p:n
{ \int_mod:nn { \l_tmpb_int } { 3 } = \c_zero }
&&
\int_compare_p:n
{ \l_tmpb_int < \l_tmpa_int }
}
{
\tl_put_left:Nn \g_tmpb_tl { , }
}
\int_incr:N \l_tmpb_int
}
\tl_use:N \g_tmpb_tl
} -
DohyunKim
2015.03.05 14:26
머리 부분 0을 없애는 거라면, 위 꼬리재귀 방식의 첫번째 매크로를 원래 것으로 되돌리고 대신 두번째 매크로를 고치면 되겠습니다.
\def\mynumformat#1{%
\mynumformatx#1\end
}
\def\mynumformatx#1{%
\ifx#10\expandafter\mynumformatx
\else#1\def\mytmpa{}\expandafter\mynumformati
\fi
} -
nanim
2015.03.05 22:03
l3regex:
\NewDocumentCommand \mynumformat { m }
{
\tl_set:Nn \l_tmpa_tl { #1 }
\regex_replace_once:nnN { ^([0]*) } { } \l_tmpa_tl
\tl_reverse:N \l_tmpa_tl
\regex_replace_all:nnN { (\d{3}) } { \1, } \l_tmpa_tl
\regex_replace_once:nnN { ,$ } { } \l_tmpa_tl
\tl_reverse:N \l_tmpa_tl
\tl_use:N \l_tmpa_tl
} -
nanim
2015.03.09 14:27
지금까지 논의를 정리하였습니다.
번호 | 제목 | 글쓴이 | 날짜 | 조회 수 |
---|---|---|---|---|
1146 | KTUG 문고에 투고합니다 [6] | 텍사랑 | 2024.09.21 | 88 |
1145 | 색인에서 연속하는 페이지 번호 [3] | yihoze | 2023.11.16 | 140 |
1144 | 예전 샘플 문서 갱신: 금강경 | noname | 2024.05.15 | 142 |
1143 | daft text [1] | noname | 2024.04.29 | 145 |
1142 | [Lilypond] W. A. Mozart, Laudate Dominum [3] | noname | 2024.04.07 | 146 |
1141 | LyX 2.4에 대한 각주 | noname | 2024.06.05 | 149 |
1140 | 색인어에 한자 넣기 [4] | 판돌이 | 2024.09.10 | 154 |
1139 | simplebnf v1.0.0: Backus-Naur form (BNF) 문법을 간단히 표시해주는 패키지 [2] | Zeta | 2023.11.24 | 156 |
1138 | 수능 국어 문제 [3] | noname | 2024.09.09 | 161 |
1137 | 매큔-라이샤워 표기법 키보드 밖의 부호 [2] | noname | 2024.05.31 | 168 |
1136 | Codehigh 패키지 [7] | Zeta | 2023.11.23 | 174 |
1135 | 게임 테이블 그리기 2 | ischo | 2024.02.14 | 175 |
1134 | 꺾쇠란 무엇인가? [5] | noname | 2024.09.01 | 180 |
1133 | [유틸리티] latexindent [1] | noname | 2024.03.30 | 185 |
1132 | 잊을 만하면 다시 돌아오는 로또 [4] | yihoze | 2023.12.05 | 197 |
1131 | 며칠 전에 올렸던 문서 [5] | noname | 2024.08.23 | 209 |
1130 | 영어로 작성된 텍 파일의 철자 검사하기 [2] | yihoze | 2023.11.03 | 220 |
1129 | [취미] 심심할 때는 괘를 뽑아보세요 [1] | noname | 2024.04.16 | 220 |
1128 | 님의침묵 | noname | 2023.03.01 | 223 |
1127 | [KTUG 문고] 이태준, 복덕방 [3] | noname | 2024.05.21 | 227 |
제가 생각하는 (expl3 계열의) 가장 우아한 해결책은 다음과 같습니다만... 문제에서 요구한 조건에 맞지 않은 패키지를 사용했으므로 정답에서는 제외... 그렇지만 expl3로 이런 게 가능하다는 거...