自作マクロがうまく動きません

自作マクロがうまく動きません

- ぺけぽん の投稿
返信数: 13
こんにちは。
お世話になります。

今,TeXのマクロを勉強していまして,以下のようなものを作ってみました。
やりたいことは,長さのカウンタを1ptずつ増やしながらruleで階段状の塗りつぶした四角形を描くことです。(実用性はありません。あくまでも勉強用です。)
しかし,うまく動かず,2つの疑問点が生じました。疑問点は下の方に書きます。
まず,作成したマクロを示します。

プリアンブル部で

\makeatletter
\def\foo#1{%
\@tempdima=\z@
\loop
\advance\@tempdima 1pt
\rule{1zw}{\@tempdima}%
\ifdim\@tempdima<#1\relax
\repeat
}
\makeatother

としておいて,本文中で

\foo{1zh}

とすると,

「....(空白,幅は確保されているっぽい)...」

となってしまい,マクロが有効に機能していません。

疑問点は以下のとおりです。

(疑問点1)塗りつぶした四角形が表示されず,空白となってしまいます。

(疑問点2)\rule部分の挙動を確認しようとして,\fboxを使って確認しようとしました。すると,無限ループっぽい状態になり,「! TeX capacity exceeded, sorry [main memory size=5000000]」で落ちます。具体的には\ruleの部分を「\fbox{\rule{1zw}{\@tempdima}}」と変更すると,うまくいきません。

たぶん,勘違いなどがあるために動かないと思うのですが,ご教示いただけますと幸いです。
ぺけぽん への返信

Re: 自作マクロがうまく動きません

- Z. R. の投稿

\rule というのは LaTeX で用意された命令である(プリミティブではない)ので、当然それは TeX 言語で実装されています。

\DeclareRobustCommand\rule{\@ifnextchar[\@rule{\@rule[\z@]}}%
 \def\@rule[#1]#2#3{%
   \leavevmode
   \hbox{%
     \setlength\@tempdima{#1}%
     \setlength\@tempdimb{#2}%
     \setlength\@tempdimc{#3}%
     \advance\@tempdimc\@tempdima
     \vrule\@width\@tempdimb\@height\@tempdimc\@depth-\@tempdima}}

これを見ればすぐに原因がわかると思います。\rule\@tempcnta を使っているわけです。

同様に、\fbox\@tempcnta を使っています。

そもそも、\@temp~ というレジスタは、本来は LaTeX カーネルが自分の動作のために確保したものです。だから、それを“流用”するにしても、「LaTeX カーネルがそれらを利用している」という当然の事実を忘れてはいけないでしょう。

Z. R. への返信

(御礼)Re: 自作マクロがうまく動きません

- ぺけぽん の投稿
Z.R.さん,アドバイスありがとうございます。
下記のように訂正したら動きました。
\fboxを使ってもOKでした。
(寸法カウンタ\@dimhogeを新設)

\makeatletter
\def\foo#1{%
\newdimen\@dimhoge
\@dimhoge=\z@
\loop
\advance\@dimhoge 1pt
\rule{1zw}{\@dimhoge}%
\ifdim\@dimhoge<#1\relax
\repeat
}
\makeatother

TeXのカウンタは256種類しかないと聞き,よく使われている一時的なカウンタを流用した方が良いと考えていました。使おうとする命令そのものがTeXで記述されたものであれば,それらが内部で既に使われている可能性があるから,確認の上,使うようにということですね。勉強になりました。ありがとうございます。
ぺけぽん への返信

Re: (御礼)Re: 自作マクロがうまく動きません

- 北見 けん の投稿
> TeXのカウンタは256種類しかない

カウンタ(整数レジスタ)256個、長さレジスタ256個、...などですね。
現在の拡張された TeX 処理系だともっと多いようですが、
いずれにしても、
レジスタの新規割り当てを \foo の定義外に出しておかないと、
\foo を使うたびに新しいレジスタを割り当ててしまうので、あまりよくなさそうです。

\makeatletter

\newdimen\@dimhoge% <- ここにおくと、一回だけ実行される

\def\foo#1{%
% <- ここにおくと、\foo が使われるたびに実行される。
\@dimhoge=\z@
\loop
\advance\@dimhoge 1pt
\rule{1zw}{\@dimhoge}%
\ifdim\@dimhoge<#1\relax
\repeat
}

\makeatother
北見 けん への返信

Re: (御礼)Re: 自作マクロがうまく動きません

- ぺけぽん の投稿
北見 けん さん

アドバイス,ありがとうございます。
たしかに,マクロの定義内部にレジスタ宣言を書くと多くのレジスタが消費されてしまいますね。今後はそのあたりも気にして外に出せるものは出すようにしたいと思います。
話題がそれますが,処理ページの組版が終わったら不要なレジスタは開放するような仕組みは実装されていないんでしょうかね。今,自分のTeX環境(Ubuntu 16.04/TeX Live)で確認したら

This is e-pTeX, Version 3.14159265-p3.6-141210-2.6 (utf8.euc) (TeX Live 2015/Debian) (preloaded format=platex)

となっていたので,拡張されたTeXだから32768個までレジスタが使えるんでしたか。
ぺけぽん への返信

Re: (御礼)Re: 自作マクロがうまく動きません

- 本田 知亮 の投稿
賑やかしにすぎませんが.

>処理ページの組版が終わったら不要なレジスタは開放するような仕組みは実装

変数のスコープがページ単位とは限りませんので
実際のところは

グループ開始
...
\newdimen\hoge
...
グループ終了
%%ここにきたら\hogeに
%%割り当てられたレジスタが解放される

こんなところでしょうか.

えーと・・・
\alloc@とか\ch@ckを丹念に追いかけたり
\aftergroup使ったりでできる?

できるならすでに誰かやってそうな気はします.

本田 知亮 への返信

Re: (御礼)Re: 自作マクロがうまく動きません

- 北見 けん の投稿
グループ内でもグローバルな割り当てを許容したいですから、
次のいずれかの方法になるでしょうか。

・レジスタ割り当て番号を
 ローカル用とグローバル用に(偶数と奇数など)に分けておいて
 それぞれ別の割り当て命令を作っておく

・レジスタの割り当てと解放を番号順にするのをあきらめて
 フロート周りの freelist のように未割当番号を管理する

おもしろそうですね。
北見 けん への返信

Re: (御礼)Re: 自作マクロがうまく動きません

- 本田 知亮 の投稿
雪だるまなお方が
お書きあそばされてます.

https://gist.github.com/zr-tex8r/41c4120cef89bba77f85ced712d475ea

ローカルに番号を割り当てて
それでレジスタを使いまわしてるんだと思いますが,
すごい.

#helperの部分にも超絶な技巧が。。。
本田 知亮 への返信

Re: (御礼)Re: 自作マクロがうまく動きません

- 北見 けん の投稿
すごいですね。

chardef された疑似数値トークンに対して
インクリメントを実装することで
数値レジスタのように使っているのが面白いですね。
初めて見ました。
## レジスタをなるべく使いたくないという
## この文脈だからこそのテクニックですね。
## 一昔前の、レジスタを食いつぶしてしまうパッケージなどでは
## 使われたりしてたのかな、などと想像しています。

ローカルとグローバルの割り当てを混在させられる仕組みは
まだ追い切れていません。

本田 知亮 への返信

Re: (御礼)Re: 自作マクロがうまく動きません

- ぺけぽん の投稿
本田 知亮 さん

コメントありがとうございます。

いやはや,自分の考えが浅はかでした。
たしかに,ページ処理が終わっても残さなければならないカウンタはありますよね。
(章節番号とかページ番号とか。)

自分で話をふっておいてあれですが,3万以上もあるので,あまり気にしなくてもいいような気がしてきました(^^;
北見 けん への返信

Re: (御礼)Re: 自作マクロがうまく動きません

- 帯田 木偶太 の投稿
最初の質問に書かれたソースの \@tempdima を \@tempdimc に変更すると
期待どおりになるようです。

\rule の下請けである \@rule においては \@tempdimc も使われてはいますが、
最初の方で
    \setlength\@tempdimc{#3}%
となっているので、\@tempdimc に \@tempdimc 自身が代入されることになり、
期待どおりに動作するということでしょうね。

私はマクロを書くにあたっては、未だに旧来の(つまり拡張されていない)TeX でも
使えるように、という感覚が残っていて、スクラッチ・レジスターの類いは
極力使い回しています。メインの PC、サブの PC が不調になっても
部屋の隅に残っている MS-DOS 機でその場をしのげるように、なんて
考えているんですねぇ。まあそんなことは滅多にないでしょうが‥‥。
帯田 木偶太 への返信

Re: (御礼)Re: 自作マクロがうまく動きません

- ぺけぽん の投稿
帯田 木偶太 さん

コメントありがとうございます。
\@tempdimc は試しませんでしたが,こちらだと動くんですね。
私も互換性を重視する方なので,マクロを作るときは過去の環境でもできるだけ動くようにとは意識しています。
ぺけぽん への返信

Re: (御礼)Re: 自作マクロがうまく動きません

- 北見 けん の投稿
(帯田さん)# <- こちらのお名前はなかなか慣れません
> となっているので、\@tempdimc に \@tempdimc 自身が代入されることになり、
> 期待どおりに動作するということでしょうね。

これはかなり危うい感じですね。
“急カーブ注意”のマークをつけたくなります。

それはさておき、
自分が作ったのではないマクロは内部処理が気になりますから、
プリミティブでできることはプリミティブで済ますと安心です。
(TeX on LaTeX などというようですね)

\rule{1zw}{\@dimhoge}%

であれば、代わりに

\leavevmode\vrule width 1zw height \@dimhoge depth 0pt

が使えます。(\leavevmode は プリミティブではないですが)

今回の例ではこれがループで繰り返されるので、
\leavevmode はループに入るときの最初の1回だけ実行すればよいですね。
北見 けん への返信

Re: (御礼)Re: 自作マクロがうまく動きません

- ぺけぽん の投稿
北見 けん さん

ありがとうございます。
おっしゃる通り,マクロの中でば,プリミティブがいいかもしれませんね。
TeXブックを読み返そうと思うけど,実家に置いてきてしまって今,手元にないんです(涙