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
지금까지 논의를 정리하였습니다.
번호 | 제목 | 글쓴이 | 날짜 | 조회 수 |
---|---|---|---|---|
559 | luatexko의 자동조사 [1] | nanim | 2015.04.19 | 3117 |
558 | texlive and kotex for iPad 2 (iPhone) [6] | W12aRd | 2012.02.08 | 84104 |
557 | QnA 게시판에 공지글 생성 건의 [2] | mcpark | 2015.04.07 | 3073 |
556 | 다가 올 파이데이를 미리 기념하며 [17] | 작나 | 2015.03.05 | 3722 |
555 | 텍 사용자들이 파이 데이를 기념해야 하는 이유 [1] | 작나 | 2015.03.14 | 3090 |
554 | 파이 데이 기념? [6] | nanim | 2015.03.06 | 3636 |
553 | 플레인텍에 이미지 넣기 [6] | 작나 | 2015.03.03 | 3065 |
552 | 루아텍 코어덤프... [2] | 작나 | 2015.03.11 | 3129 |
» | 재미로 해보는 매크로 작성 [20] | nanim | 2015.03.02 | 3788 |
550 | 재미로 해보는 매크로 작성 [중급?] [2] | nanim | 2015.03.04 | 3082 |
549 | xparse의 argument option | nanim | 2015.03.04 | 3361 |
548 | 그땐 그랬지 [3] | 작나 | 2015.02.28 | 3098 |
547 | texdoc [7] | nanim | 2015.02.26 | 3082 |
546 | 크누스 교수의 수표 받는 법 [1] | 작나 | 2015.02.23 | 3025 |
545 | 연분수 조판 2탄 [9] | 작나 | 2015.02.17 | 3206 |
544 | plain.tex으로 배우는 텍 프로그래밍 [4] | 작나 | 2015.02.23 | 3060 |
543 | 본고딕(Source Han Sans) 소개: 오픈 소스 한중일 통합 서체 [11] | 에드 | 2014.07.16 | 18467 |
542 | 옷, 디자인이 바뀌었군요! | Dennis | 2015.02.22 | 3024 |
541 | 설 명절 잘 보내십시오 [5] | Progress | 2015.02.17 | 3009 |
540 | 연분수 조판 시리즈 마지막 [1] | 작나 | 2015.02.19 | 3184 |
제가 생각하는 (expl3 계열의) 가장 우아한 해결책은 다음과 같습니다만... 문제에서 요구한 조건에 맞지 않은 패키지를 사용했으므로 정답에서는 제외... 그렇지만 expl3로 이런 게 가능하다는 거...