読者です 読者をやめる 読者になる 読者になる

プログラマーのためプログラミングLaTeX

LaTeX

ちょっとした計算ならばLaTeXで済ましてしまおうっていう動機があって, 最近調べていたら割りと楽に色々書けるのを知ったのでメモしておきます.

注意としては, 私はLaTeXのマニアではないので(最近になってこういうの書くようになった), 色々変なところもあると思います.

私としてはぶっちゃけ取り敢えず動けばいいのですが, ツッコミは歓迎です. でもでもTeXnitianの方からの手厳しい指摘は心折れてしまうのでお手柔らかに...

素数のリストを出力してみる

与えられた数の個数分, 小さい方から素数を列挙するプログラムを考えます.
まずは普通に好きな言語で書いてみましょう.
私はPythonを選びました.
Cでも良かったのですが, 配列のappendがちょっとまどろっこしいので...
Rubyとか, シェルスクリプトあたりでもいいと思います(変換しやすさ的な意味で).
もう一つPythonを選んだ理由は, 波括弧が無いということです(LaTeXのそれとごっちゃになりそうなので).

def prime(n):
  x = 1
  k = 1
  primelist = [2]
  while k < n:
    x += 2
    a = 0
    for m in primelist:
      b = x
      while m < b:
        b -= m
      if b == m:
        a = 1
    if a == 0:
      primelist.append(x)
      k += 1
  return primelist

print(prime(100))
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541]

100番目の素数である, 541までが出力されます.

このスクリプトは簡単なもので, 誰にでも理解できると思います.
それまでの素数リストから数字を取ってきて, 割り切れたらフラグを立てておき, フラグが折れたままだったら素数リストに追加しています.

注意としては,

  • mod計算 % を展開する (b = x; while m < b: ... の部分)
  • 条件文は <= を使わず ==, <, > のみにする
  • ややこしい代入文は, +=, -=, *=, /= にばらす. (x = a * (b + c / d); → x = c; x /= d; x += b; x *= a; )

くらいです.



さて, ここから, LaTeXのファイルを作りましょう. 取り敢えず, Pythonソースコードをそのまま貼ってみました.

\documentclass{article}
\makeatletter
def prime(n):
  x = 1
  k = 1
  primelist = [2]
  while k < n:
    x += 2
    a = 0
    for m in primelist:
      b = x
      while m < b:
        b -= m
      if b == m:
        a = 1
    if a == 0:
      primelist.append(x)
      k += 1
  return primelist
\makeatother

\begin{document}
\prime{100}
\end{document}

Pythonの関数としてはOKでしたが, もちろんLaTeXとしてはコンパイルできません.
頑張って変換します.
まず, defを\defに, returnを消します.
primelistを\@primelistにします.

\def\prime(n):
  x = 1
  k = 1
  \@primelist = [2]
  while k < n:
    x += 2
    a = 0
    for m in \@primelist:
      b = x
      while m < b:
        b -= m
      if b == m:
        a = 1
    if a == 0:
      \@primelist.append(x)
      k += 1
  \@primelist

引数のnを#1にします.
x, k, a, b, mという, 使用している変数を, すべて\@付きにします.
@マークを付けることで, 変数をグローバルに使ってほしくないんだなと, 一瞬でわかります.
privateメンバーみたいなものです.
あと, マクロ全体を二重の波括弧で優しく包みます.

\def\prime#1{{
  \@x = 1
  \@k = 1
  \@primelist = [2]
  while \@k < #1:
    \@x += 2
    \@a = 0
    for \@m in \@primelist:
      \@b = \@x
      while \@m < \@b:
        \@b -= \@m
      if \@b == \@m:
        \@a = 1
    if \@a == 0:
      \@primelist.append(\@x)
      \@k += 1
  \@primelist
}}

破壊的な代入 -=, += を \advance .. by .. にします.
代入文の行末に\relaxを入れます(ぶっちゃけ要らないんですが, これを書かずに痛い目にあったことがあるので, セミコロンと思って書いてます).

\def\prime#1{{
  \@x = 1 \relax
  \@k = 1 \relax
  \@primelist = [2]
  while \@k < #1:
    \advance \@x by 2 \relax
    \@a = 0 \relax
    for \@m in \@primelist:
      \@b = \@x \relax
      while \@m < \@b:
        \advance \@b by -\@m \relax
      if \@b == \@m:
        \@a = 1 \relax
    if \@a == 0:
      \@primelist.append(\@x)
      \advance \@k by 1 \relax
  \@primelist
}}

\@primelistへの代入を, \edefを用いて書き直します. \@xの方は\theを用いて変数を文字列にします.

\def\prime#1{{
  \@x = 1 \relax
  \@k = 1 \relax
  \edef\@primelist{2}
  while \@k < #1:
    \advance \@x by 2 \relax
    \@a = 0 \relax
    for \@m in \@primelist:
      \@b = \@x \relax
      while \@m < \@b:
        \advance \@b by -\@m \relax
      if \@b == \@m:
        \@a = 1 \relax
    if \@a == 0:
      \edef\@primelist{\@primelist, \the\@x}
      \advance \@k by 1 \relax
  \@primelist
}}

while文を \@whilenum .. \do { ... } に, for文を \@for var:=list \do { ... } に, if文を \ifnum .. ... \fi します.

\def\prime#1{{
  \@x = 1 \relax
  \@k = 1 \relax
  \edef\@primelist{2}
  \@whilenum \@k < #1 \do {
    \advance \@x by 2 \relax
    \@a = 0 \relax
    \@for \@m:=\@primelist \do {
      \@b = \@x \relax
      \@whilenum \@m < \@b \do {
        \advance \@b by -\@m \relax
      }
      \ifnum \@b == \@m
        \@a = 1 \relax
      \fi
    }
    \ifnum \@a == 0
      \edef\@primelist{\@primelist, \the\@x}
      \advance \@k by 1 \relax
    \fi
  }
  \@primelist
}}

条件文の == を = にし, 波括弧の後ろに%でコメントをつけて完成です.
以下は, platexコンパイルできる, LaTeX文章です.
私のパソコンで2秒くらいコンパイルに時間がかかります.

\documentclass{article}
\makeatletter
\def\prime#1{{%
  \newcount\@a
  \newcount\@b
  \newcount\@x
  \newcount\@k
  \newcount\@m
  \@x = 1 \relax
  \@k = 1 \relax
  \edef\@primelist{2}%
  \@whilenum \@k < #1 \do {%
    \advance \@x by 2 \relax
    \@a = 0 \relax
    \@for \@m:=\@primelist \do {%
      \@b = \@x \relax
      \@whilenum \@m < \@b \do {%
        \advance \@b by -\@m \relax
      }%
      \ifnum \@b = \@m
        \@a = 1 \relax
      \fi
    }%
    \ifnum \@a = 0
      \edef\@primelist{\@primelist, \the\@x}%
      \advance \@k by 1 \relax
    \fi
  }%
  \@primelist
}}
\makeatother

\begin{document}
First 100 prime numbers are: \prime{100}.
\end{document}


これくらいの処理なら, Pythonなどのサブセットから自動的に置換できそうです. LaTeXが意外と身近な言語に思えてくると思います.

気になるポイントを抑えておきます.

  • \newcountって何? → Cでの変数宣言みたいなもんです. カウンターを作ります.
  • \relaxって何? → 心が落ち着きます
  • なんで全体を二重括弧なの? → そういうものだと思っておいてください. 私はある事情により, こういうローカル変数を使うようなものではこう書いていますが, あまり他の人のスクリプトで見たことない気がしますので, 一般的ではないのかもしれません.
  • \@whilenum, \@for って何? TeXじゃないの? → 違います. LaTeXのマクロです. TeXには\loop\repeatというものがあり, これを使うこともできますが, 普段LaTeXの文章しか書かないので, TeXに固執することもないでしょう(執拗に拘る人いますよね...).
  • \do って何? → \@whilenum, \@forマクロのために必要です.これらのマクロのソースコード http://www.tex.ac.uk/ctan/macros/latex/base/ltcntrl.dtx を見ると, \doでパターンマッチさせていることが分かります.
  • \ifnumは? → それはTeXのプリミティブです. intの比較 <, =, > の演算子が使えます. (tex.web l.9780)
  • \edef って何? → 多くのスクリプト言語における代入と同じです.定義と言います.
  • \advanceって何? → 足し算と引き算ができます. 同様に, \multiplyと\divideが使えます. これらはTeXのプリミティブです.

これくらい解説しておけば, 大丈夫でしょう.

階乗を計算してみる

お次は, 階乗を計算してみます. さっきの素数リストよりも簡単です. やはりPythonで書いてみます.

def factorial(n):
  m = 1
  k = 1
  while m < n:
    m += 1
    k *= m
  return k

print(factorial(10))
3628800

さあ, これもLaTeXのコードに落とします.

\documentclass{article}
\makeatletter
def factorial(n):
  m = 1
  k = 1
  while m < n:
    m += 1
    k *= m
  return k
\makeatother
\begin{document}
\factorial{10}
\end{document}

def, return, 変数を処理します.

\def\factorial#1{{
  \@m = 1
  \@k = 1
  while \@m < #1:
    \@m += 1
    \@k *= \@m
  \the\@k
}}

代入を処理します.

\def\factorial#1{{
  \@m = 1 \relax
  \@k = 1 \relax
  while \@m < #1:
    \advance \@m 1 \relax
    \multiply \@k \@m \relax
  \the\@k
}}

whileを\@whilenumにします.

\def\factorial#1{{
  \@m = 1 \relax
  \@k = 1 \relax
  \@whilenum \@m < #1 \do {
    \advance \@m 1 \relax
    \multiply \@k \@m \relax
  }
  \the\@k
}}

\newcount と波括弧の後をコメントアウトして完成です. 以下のコードはコンパイルを通り, 期待する出力を得ます.

\documentclass{article}
\makeatletter
\def\factorial#1{{%
  \newcount\@m \@m = 1 \relax
  \newcount\@k \@k = 1 \relax
  \@whilenum \@m < #1 \do {%
    \advance \@m 1 \relax
    \multiply \@k \@m \relax
  }%
  \the\@k
}}
\makeatother
\begin{document}
10 factorial is \factorial{10}.
\end{document}


ね, 簡単でしょう?
ちなみにこのfactorial, 短くするとこんな風にできます.

\def\factorial#1{{\newcount\m\m=1\newcount\k\k=1\loop\ifnum\m<#1\advance\m1\multiply\k\m\repeat\the\k}}

わーいヾ(╹◡╹๑)ノ"♡


ところが, 皆さんご存知のように, このコードでは13の階乗を計算しようとすると桁が足りなくなります.

! Arithmetic overflow.
\factorial ...@m by 1 \relax }\multiply \@a by #1
                                                  \relax \the \@a }
l.19 \factorial{13}

?
! Emergency stop.
\factorial ...@m by 1 \relax }\multiply \@a by #1
                                                  \relax \the \@a }
l.19 \factorial{13}

No pages of output.

のようなエラーになりました. これを解決するには任意精度の整数の計算が必要になりますが, めんどそうなので辞めます(๑´◕﹏◕`)

フィボナッチ数を計算してみる

お次はフィボナッチ数ですっ! 与えられた引数番目のフィボナッチ数を返します. http://oeis.org/A000045 に従って, fib(0) = 0, fib(1) = 1とします.

def fib(n):
  i = 0
  a = 0
  b = 1
  c = 1
  while i < n:
    c = a + b
    a = b
    b = c
    i += 1
  return a

print(fib(0))
print(fib(1))
print(fib(2))
print(fib(3))
print(fib(4))
print(fib(5))
print(fib(6))
print(fib(7))
print(fib(8))
print(fib(9))
print(fib(10))
0
1
1
2
3
5
8
13
21
34
55

さあ, LaTeXに落としますよ.

\documentclass{article}
\makeatletter
def fib(n):
  i = 0
  a = 0
  b = 1
  c = 1
  while i < n:
    c = a + b
    a = b
    b = c
    i += 1
  return a
\makeatother
\begin{document}
\fib{0},
\fib{1},
\fib{2},
\fib{3},
\fib{4},
\fib{5},
\fib{6},
\fib{7},
\fib{8},
\fib{9},
\fib{10}
\end{document}

def, return, 変数を処理します.

\def\fib#1{{
  \@i = 0 \relax
  \@a = 0 \relax
  \@b = 1 \relax
  \@c = 1 \relax
  while \@i < #1:
    \@c = \@a + \@b \relax
    \@a = \@b \relax
    \@b = \@c \relax
    \advance \@i by 1 \relax
  \the\@a
}}

whileを\@whilenumにします.

\def\fib#1{{
  \@i = 0 \relax
  \@a = 0 \relax
  \@b = 1 \relax
  \@c = 1 \relax
  \@whilenum \@i < #1 \do {
    \@c = \@a + \@b \relax
    \@a = \@b \relax
    \@b = \@c \relax
    \advance \@i by 1 \relax
  }
  \the\@a
}}

おっと, 足し算を処理してませんでした.

\def\fib#1{{
  \@i = 0 \relax
  \@a = 0 \relax
  \@b = 1 \relax
  \@c = 1 \relax
  \@whilenum \@i < #1 \do {
    \@c = \@a
    \advance \@c by \@b \relax
    \@a = \@b \relax
    \@b = \@c \relax
    \advance \@i by 1 \relax
  }
  \the\@a
}}

\newcountを入れて, 波括弧の後にコメントをつけて完成!

\documentclass{article}
\makeatletter
\def\fib#1{{%
  \newcount\@i \@i = 0 \relax
  \newcount\@a \@a = 0 \relax
  \newcount\@b \@b = 1 \relax
  \newcount\@c \@c = 1 \relax
  \@whilenum \@i < #1 \do {%
    \@c = \@a
    \advance \@c by \@b \relax
    \@a = \@b \relax
    \@b = \@c \relax
    \advance \@i by 1 \relax
  }%
  \the\@a
}}
\makeatother
\begin{document}
Fibonacci numbers are \fib{0}, \fib{1}, \fib{2}, \fib{3}, \fib{4}, \fib{5}, \fib{6}, \fib{7}, \fib{8}, \fib{9}, \fib{10}.
\end{document}


ふぃぼっ(੭ु✿╹◡╹)੭ु⁾⁾ふぃぼっ

Project EulerLaTeXで解いてみる

やりましょう! まずは一番 http://projecteuler.net/problem=1

def eulerone(n):
  i = 1
  a = 0
  while i < n:
    if i % 3 == 0 or i % 5 == 0:
      a += i
    i += 1
  return a

print(eulerone(1000))

ここで, 剰余を展開しました. 今回は割って掛けて比較することにしました.

def eulerone(n):
  i = 1
  a = 0
  while i < n:
    f = 0
    b = i
    b /= 3
    b *= 3
    if i == b:
      f = 1
    b = i
    b /= 5
    b *= 5
    if i == b:
      f = 1
    if f == 1:
      a += i
    i += 1
  return a

print(eulerone(1000))

LaTeXに変換すると, こうなりました.

\documentclass{article}
\makeatletter
\def\eulerone#1{{%
  \newcount\@i \@i = 1
  \newcount\@a \@a = 0
  \newcount\@b
  \newcount\@f
  \@whilenum \@i < #1 \do {%
    \@f = 0
    \@b = \@i
    \divide \@b by 3
    \multiply \@b by 3
    \ifnum \@i = \@b
      \@f = 1
    \fi
    \@b = \@i
    \divide \@b by 5
    \multiply \@b by 5
    \ifnum \@i = \@b
      \@f = 1
    \fi
    \ifnum \@f = 1
      \advance \@a by \@i
    \fi
    \advance \@i by 1
  }%
  \the\@a
}}
\makeatother
\begin{document}
Answer of Project Euler 1 is \eulerone{1000}.
\end{document}


次, Project Euler2 http://projecteuler.net/problem=2

def eulertwo(n):
  a = 0
  b = 1
  c = 1
  d = 0
  while a < n:
    c = a + b
    a = b
    b = c
    if a % 2 == 0:
      d += a
  return d

print(eulertwo(4000000))

LaTeXにすると

\documentclass{article}
\makeatletter
\def\eulertwo#1{{%
  \newcount\@a\@a = 0
  \newcount\@b\@b = 1
  \newcount\@c\@c = 1
  \newcount\@d\@d = 0
  \@whilenum \@a < #1 \do {%
    \@c = \@a
    \advance \@c by \@b
    \@a = \@b
    \@b = \@c
    \ifodd\@a\else
      \advance \@d by \@a
    \fi
  }%
  \the\@d
}}
\makeatother
\begin{document}
Answer of Project Euler 2 is \eulertwo{4000000}.
\end{document}


いやぁ, 素晴らしい.
問題に対する回答をLaTeXで打つのに, それの計算過程がその文章のソースの中にあるなんて素敵ですね.

3の倍数と3がつく数字の時だけ\itになる

元ネタは http://d.hatena.ne.jp/zrbabbler/20110815/1313398638 です. TeXに拘ること無く, 自由にやってみましょう.
まずはPythonで書いてみます. 世界のナベアツさんのネタは40までなので, 40まで動けばいいことにします.

def nabeazz(n):
  i = 1
  n += 1
  while i < n:
    if i % 10 == 3 or i / 10 == 3 or i % 3 == 0:
      print("{\\it %d}" % i)
    else:
      print(i)
    i += 1

nabeazz(40)
1
2
{\it 3}
4
5
{\it 6}
7
8
{\it 9}
10
11
{\it 12}
{\it 13}
14
{\it 15}
16
17
{\it 18}
19
20
{\it 21}
22
{\it 23}
{\it 24}
25
26
{\it 27}
28
29
{\it 30}
{\it 31}
{\it 32}
{\it 33}
{\it 34}
{\it 35}
{\it 36}
{\it 37}
{\it 38}
{\it 39}
40

剰余, if .. or .. をばらします.

def nabeazz(n):
  i = 1
  n += 1
  while i < n:
    b = i + 7
    a = b
    a /= 10
    a *= 10
    if a == b:
      print("{\\it %d}" % i)
    else:
      a = i
      a /= 3
      a *= 3
      if a == i:
        print("{\\it %d}" % i)
      else:
        a = i / 10
        if a == 3:
          print("{\\it %d}" % i)
        else:
          print(i)
    i += 1

nabeazz(40)

LaTeXに落とします.

\documentclass{article}
\makeatletter
def nabeazz(n):
  i = 1
  n += 1
  while i < n:
    b = i + 7
    a = b
    a /= 10
    a *= 10
    if a == b:
      print("{\\it %d}" % i)
    else:
      a = i
      a /= 3
      a *= 3
      if a == i:
        print("{\\it %d}" % i)
      else:
        a = i / 10
        if a == 3:
          print("{\\it %d}" % i)
        else:
          print(i)
    i += 1
\makeatother
\begin{document}
\nabeazz{40}
\end{document}

def, 引数, 変数を処理します.

\def\nabeazz#1{{
  \@n = #1
  \@i = 1
  \@n += 1
  while \@i < \@n:
    \@b = \@i + 7
    \@a = \@b
    \@a /= 10
    \@a *= 10
    if \@a == \@b:
      print("{\\it %d}" % i)
    else:
      \@a = \@i
      \@a /= 3
      \@a *= 3
      if \@a == \@i:
        print("{\\it %d}" % i)
      else:
        \@a = \@i / 10
        if \@a == 3:
          print("{\\it %d}" % i)
        else:
          print(\@i)
    \@i += 1
}}

代入文を処理します.

\def\nabeazz#1{{
  \@n = #1
  \@i = 1
  \advance \@n 1
  while \@i < \@n:
    \@b = \@i
    \advance \@b 7
    \@a = \@b
    \divide \@a 10
    \multiply \@a 10
    if \@a == \@b:
      print("{\\it %d}" % i)
    else:
      \@a = \@i
      \divide \@a 3
      \multiply \@a 3
      if \@a == \@i:
        print("{\\it %d}" % i)
      else:
        \@a = \@i
        \divide \@a 10
        if \@a == 3:
          print("{\\it %d}" % i)
        else:
          print(\@i)
    \advance \@i 1
}}

while, ifを処理します. 条件 == を = にします.

\def\nabeazz#1{{%
  \@n = #1
  \@i = 1
  \advance \@n 1
  \@whilenum \@i < \@n \do {%
    \@b = \@i
    \advance \@b 7
    \@a = \@b
    \divide \@a 10
    \multiply \@a 10
    \ifnum \@a = \@b
      print("{\\it %d}" % i)
    \else
      \@a = \@i
      \divide \@a 3
      \multiply \@a 3
      \ifnum \@a = \@i
        print("{\\it %d}" % i)
      \else
        \@a = \@i
        \divide \@a 10
        \ifnum \@a = 3
          print("{\\it %d}" % i)
        \else
          print(\@i)
        \fi
      \fi
    \fi
    \advance \@i 1
  }%
}}

print文を処理します. \theを用いて文字列にします.

\def\nabeazz#1{{%
  \@n = #1
  \@i = 1
  \advance \@n 1
  \@whilenum \@i < \@n \do {%
    \@b = \@i
    \advance \@b 7
    \@a = \@b
    \divide \@a 10
    \multiply \@a 10
    \ifnum \@a = \@b
      {\it \the\@i} %
    \else
      \@a = \@i
      \divide \@a 3
      \multiply \@a 3
      \ifnum \@a = \@i
        {\it \the\@i} %
      \else
        \@a = \@i
        \divide \@a 10
        \ifnum \@a = 3
          {\it \the\@i} %
        \else
          {\the\@i} %
        \fi
      \fi
    \fi
    \advance \@i 1
  }%
}}

カウンターを宣言して終わりです.

\documentclass{article}
\makeatletter
\def\nabeazz#1{{%
  \newcount\@n
  \newcount\@i
  \newcount\@a
  \newcount\@b
  \@n = #1
  \@i = 1
  \advance \@n 1
  \@whilenum \@i < \@n \do {%
    \@b = \@i
    \advance \@b 7
    \@a = \@b
    \divide \@a 10
    \multiply \@a 10
    \ifnum \@a = \@b
      {\it \the\@i} %
    \else
      \@a = \@i
      \divide \@a 3
      \multiply \@a 3
      \ifnum \@a = \@i
        {\it \the\@i} %
      \else
        \@a = \@i
        \divide \@a 10
        \ifnum \@a = 3
          {\it \the\@i} %
        \else
          {\the\@i} %
        \fi
      \fi
    \fi
    \advance \@i 1
  }%
}}
\makeatother
\begin{document}
{\Large\nabeazz{40}}
\end{document}


めちゃくちゃ簡単ですね!!! Pythonのコードを書き始めてから10分たってないですん!!!
これは偶然なのですが, 40だけ次の行に来てるのがじわじわきますねw
元の問題ではフォントをComputer Modern Funny Italicにしろという指定ですが, 本質的な問題ではないのでほうっておきます.
他の皆さんは一般に何桁でも判定できるようにとか, \newifつかったりして頑張ってはりますね. 参考に偉大なる先人達の参考URLを貼っておきます. 皆さんすごい!

まとめ

TeXのマクロ\ifnumと, LaTeXのマクロ\@whilenum, \@forのおかげで, Pythonのサブセットから本当に機械的にLaTeXのコードに変換できることを示しました.
マクロマクロしてる使い方よりも, こちらの方が私たちの手に馴染んでいるし, 普段のスタイルに近くて気が楽です.
こう言う風に簡単にできるんだよって言うことを示さないと, コミュニティーは大きくならない気がしますん. ユーザーは多いのに...
便利なものは使ったらいいんですよ. TeXに拘る必要はありません. TeXで動かんくても知ったこっちゃないんやけ.
なお, PythonのサブセットからLaTeXに変換するsedスクリプトを書く計画は, 筆者の多忙により却下されました.
それでは良いLaTeXをヾ(❀╹◡╹)ノ゙❀

追記(2012/11/02)

素数リストのプログラム改善版
30の倍数で考えたのと, sqrtの適当な一次近似(x/40+10)で抑えました. 500まで位なら30秒以内で計算できます. 200までなら1秒も掛からず計算できてハッピーです.

\makeatletter
\newcount\@a
\newcount\@b
\newcount\@x
\newcount\@y
\newcount\@k
\newcount\@m
\newcount\@l
\newcount\@ll
\newcount\@lll
\def\prime#1{{%
  \@x = 1
  \@k = 3
  \@l = 1
  \@l = 1
  \@ll = 1
  \@lll = 1
  \ifnum #1 < 1
    \edef\@primelist{}%
  \else
    \ifnum #1 < 2
      \edef\@primelist{2}%
    \else
      \ifnum #1 < 3
        \edef\@primelist{2, 3}%
      \else
        \edef\@primelist{}%
        \@whilenum \@k < #1 \do {%
          \ifodd \@l
            \advance \@x 2
            \ifodd \@ll
              \ifodd \@lll
                \advance \@x 4
              \fi
            \else
              \ifodd \@lll \else
                \advance \@x 4
              \fi
            \fi
          \else
            \advance \@x 4
            \ifodd \@ll \else
              \ifodd \@lll \else
                \advance \@x -2
              \fi
            \fi
          \fi
          \@y = \@x
          \divide \@y 40
          \advance \@y 11
          \@a = 0
          \@for \@m:=\@primelist \do {%
            \ifx \@m \@empty \else
              \ifnum \@a = 0
                \ifnum \@m < \@y
                  \@b = \@x
                  \divide \@b \@m
                  \multiply \@b \@m \relax
                  \ifnum \@b = \@x
                    \@a = 1
                  \fi
                \fi
              \fi
            \fi
          }%
          \ifnum \@a = 0
            \edef\@primelist{\@primelist, \the\@x}%
            \advance \@k 1
          \fi
          \advance \@l 1
          \ifodd \@l \advance \@ll 1
          \ifodd \@ll \advance \@lll 1 \fi \fi
        }%
        \edef\@primelist{2, 3, 5\@primelist}%
      \fi
    \fi
  \fi
  \@primelist
}}
\makeatother

追記(2012/11/07)

ありがとう みんなありがとう.
なんかアクセス増えた.
色々書くうちに, ローカル変数を使うのは賢明ではない事に気がついた.
レジスターが256しか使えないので, その関数を何回も呼ぶとすぐにいっぱいになってしまう.
関数名@x みたいにしてやるのが, ぶつからなくていいと思う.
Knuth, なんでカウンターを消すコマンド作らなかったかなぁ...

追記(2012/11/08)

ありがとう ありがとう.
想定していましたが, TeXnitianの方から用語の間違いを指摘されてしまったので, お詫び申し上げます.

以上一連の記事はマニアック過ぎて反響が薄いようだけど, 個人的には良記事だと思います.

あと, 逆トラバ. このブログ見て何か行動して下さった方がいらっしゃるみたいで. そういうの, 素直に嬉しいんですよ.

あとあと, 最近\begingroupを使わざるをえないケースがあって, やっとこうやって使うのか!って分かった. 一皮むけた気がした.