キーボードのsが壊れた!そんなときは

やりたいこと

キーボードのsが壊れても、毎回sをコピペしてこなくても良いようにしたいです。

実装

ソースコード中で改行したら出力の方でも改行するようにするコマンド\obeylines(および有志の方がそれを改良した\xobeylines)を参考にします。
これは改行文字のカテゴリーコードを変えてアクティブ文字にし、\defを使って改行文字に対して通常の制御綴のように定義する、という方法によって実現しています。

今回は改行文字^^Mの代わりに?をアクティブ文字にして、sを打ちたいときに?を打てば良いようにします。

{
\catcode`\?=\active
\gdef\quedekaihi{\catcode`\?=\active \def ?{s}}
}

入力:

\quedekaihi

He i? periwo.

出力:
He is periwo.

実装においてもsを打つ回数は一回で済むので、これならラクチン、sが壊れても安心ですね!

追記

sをどこかからコピペしてくるのは甘えだ!というクレームが入ったので修正します。sのASCIIコードが73なので、次のようにすると同じになります。

{
\catcode`\?=\active
\gdef\quedekaihi{\catcode`\?=\active \def ?{^^73}}
}

これでsを一度も打たなくて良くなりましたね!

さらに追記

上は実は\sqrtなどの制御綴は書けない(再定義とかでごまかすことを想定していた)のですが、そもそも?なんて経由しなくても、sと書きたいときに^^73と書けば良いっぽいです:
入力:

^^24^^5c^^73^^71^^72^^74^^7b^^33^^7d^^24

これは以下と同等:

$\sqrt{3}$

出力:$\sqrt{3}$

怖……

続編

これのさらなる応用です。ぜひご覧ください。
shukutoiya.hateblo.jp

ぺりをだねぇ

数式の開始・終了時にPERIWOを書きたい

やりたいこと

$2+3=5$と書いたら、出力が$PERIWO2+3=5PERIWO$になるようにすることが目標です。

obeylines

ソースコード中で改行したら出力の方でも改行するようにするコマンド\obeylines(および有志の方がそれを改良した\xobeylines)を参考にします。
これは改行文字のカテゴリーコードを変えてアクティブ文字にし、\defを使って改行文字に対して通常の制御綴のように定義する、という方法によって実現しています。

※先にこっちを見た方が理解がしやすいかもしれません。shukutoiya.hateblo.jp

ネックとなるポイント

数式開始時点と終了時点で違うことをやっているので、$を単純に定義するだけでは実装できません。なので違う文字を挟んで解決することにします。

実装1

{
\catcode`\$=\active
\catcode`\@=3
\gdef\dolltope{\catcode`\$=\active \def ${\ifmmode \mathcal{PERIWO}@\else @\mathcal{PERIWO}\fi}}
}

入力:

\dolltope
$2+3=5$

$f(x)=\begin{cases} 1 & \text{$x$は有理数} \\ 0 & \text{$x$は無理数}\end{cases}$

出力:

(最初の{は、カテゴリーコードの変更を局所的にするためです。)
@に(通常の)$と同じ機能を与えていることがポイントです。
数式モード中に$を読み込んだ時には\mathcal{PERIWO}@を、そうでないときは@\mathcal{PERIWO}を返すように設定することによって実装しています。

実装2

@を挟むのはやはり行儀が悪い(いまさら何をという感じもしますが)ので、$のままで解決したいです。

{
\catcode`\$=\active
\gdef\dolltoper{\catcode`\$=\active 
\def ${\ifmmode \mathcal{PERIWO}\bgroup \catcode`\$=3 $\egroup \else \bgroup \catcode`\$=3 $\egroup\mathcal{PERIWO}\fi}}
}

入力:

\dolltoper
$2+3=5$

$f(x)=\begin{cases} 1 & \text{$x$は有理数} \\ 0 & \text{$x$は無理数}\end{cases}$

出力:エラー

! TeX capacity exceeded, sorry [input stack size=10000].
$...ode `\$=3 $\egroup \else \bgroup \catcode `\$=
                                                  3 $\egroup \mathcal {PERIW...

無限ループに入っているのだと思いますが、何が起こっているかはよくわかりません。解析の際にカテゴリーコードの変更がどこで反映されるかあたりが謎なんでしょうか。

実装3

一方、これは成功します。

{
\gdef\periwodollar{$}
\catcode`\$=\active
\gdef\dolltoperi{\catcode`\$=\active \def ${\ifmmode \mathcal{PERIWO}\periwodollar \else \periwodollar\mathcal{PERIWO}\fi}}
}

入力:

\dolltoperi
$2+3=5$

$f(x)=\begin{cases} 1 & \text{$x$は有理数} \\ 0 & \text{$x$は無理数}\end{cases}$

出力:

\periwodollarは「カテゴリーコード3の$」と定義されている、つまり\defは中身のカテゴリーコードを確定させる、と現時点では考えていますが、この認識が正しいかどうかはよくわかりません。

次の目標

よく考えてみると\def\hoge{fuga}の }をどうやって認識しているかはそんなに自明な話ではない(\edefならまだしも、\defはその時点では完全展開しないので)ということに気づきました。\defまわりの構文解析は闇が深そうです。

ぺりをだねぇ

補助ファイルに%を出力する(仮)

LaTeXを使って、補助(テキスト)ファイルを作る際に%を出力したいときの解決策として、catcodeに訴えるという方法を思いつき実行しました。
(他に楽な方法があるかもしれないですが、とりあえず覚書として。習いたてのことばかり使っているので、マナーの悪いコードかもしれないです)

\makeatletter

\catcode`\%=11

\newcounter{currentnumber}
\setcounter{currentnumber}{1}
\newcounter{endnumber}
\setcounter{endnumber}{10}

\newcommand{\periwo}[1]{
 \if@filesw
  \newwrite\@peri\relax
  \immediate\openout\@peri #1.tex\relax
 \fi
 \if@filesw
  \immediate\write\@peri{%%%% コメントアウトも}
  \immediate\write\@peri{%%%% 出力できるよ}
  \immediate\write\@peri{\string\begin{theorem}{#1}}
  \immediate\write\@peri{}
  \immediate\write\@peri{\string\end{theorem}}
 \fi
 \if@filesw
  \immediate\closeout\@peri
 \fi
 \stepcounter{currentnumber}
}

\newcommand{\PERIWO}{
  \periwo{\thecurrentnumber}
}

\def\periwoloop{
  \PERIWO
  \ifnum\thecurrentnumber>\theendnumber\relax
  \else\expandafter\periwoloop\fi
}

\periwoloop

を(\begin{document} と\end{document} の間に書いて実行すると)
1.tex, 2.tex, ..., 10.texが生成され、
n.texの中身は

%%%% コメントアウトも
%%%% 出力できるよ
\begin{theorem}{n}

\end{theorem}

になります。

ぺりをだねぇ

TeXで危うくPCを壊しかけた話

ある日、Excelで作業をしていてファイルを保存しようとしたら、Cドライブがいっぱいだと言われて保存できず。
そんなこともあるんだな~~と思い、基礎化学実験のレポート(手書きレポートの写真をそのままpdfにしただけなので少し重い)をいくつか消してその場をしのぎ、とりあえず保存するも、数分後に再び保存に失敗。
小手先のゴミ捨てではダメなのかと反省し、データサイズの内訳(デスクトップ○○GB、ダウンロード○○GB、ミュージック○○GB、……と教えてくれるアレ)を確認しながら、古いデータを一旦USBに移すなどして20GB程度あけ、PCに平穏が戻る……はずだった。

1時間もたたないうちに再び保存に失敗。再びデータサイズの内訳を見ると、また空き容量が0GBに戻っているではないか。
さすがに何かがおかしいことを完全に確信し、ふと冷静になる。

デスクトップに170GBもあるわけなくね?

昔のデータもあるとはいえ、塵も積もれば山となるにしては山がデカすぎる。さっき整理したときも細かいファイルばかりで、中くらいのサイズのファイルもそんなになかったはず……

そこで一つ一つのフォルダのサイズを順に確認していくと、TeX実験というフォルダーのサイズの桁が3つ~4つくらい周りと違うではないか。

ここでやっと心当たりが。あいつだ


その数日前、展開制御の実験をしていました。
自分自身を呼び出すことを繰り返して適切な条件をみたしたら終了するコマンド\periwo です。
ブログをよく読んでくださっている方は見覚えがあると思います。そう、

shukutoiya.hateblo.jp

です。
これを応用させている中で事件は起きました。ご察しのとおり、無限ループです。

通常の無限ループだとTeXworksの左上にデカデカと鎮座しているタイプセット停止ボタンを押せば(経験上)止まるはずなのだが、今回は何かが違う。
無限ループだと気づいたころにはもう押しても反応がない!!
ボタンも反応しなければ、タスクマネージャーも開けず、焦るばかり。
なんとか強制終了に成功し、ほっと一安心。一瞬で精神が削られ、TeXで遊ぶ気力も消えてその日はもう寝ることに。


原因はどう考えてもこれだが、まだ具体的に何が起きているかはわかっていない状態で、TeX実験のフォルダを開く。

すると、perio.sty 22KBなどが並んでいる中に一つ、macrotest.log 151GB。
無限ループが止まらなかった理由も納得です。具体的にlogファイルに何が書き出されたかはわからなかったが、展開待ちのトークンの数が増えていくタイプの無限ループはおそらく同様になるのだろう(実験する気はありません)。

※ちなみにlogファイルを開くことは不可能でした。デカすぎて。



皆さんも無限ループには十分に注意してください。万が一起こした場合は補助ファイルを削除することをお忘れなく。


ぺりをだねぇ

逆から読んでも①

文字列を逆順にしたいです。

\makeatletter

\def\inverse#1{\@inverse #1\@nil}
\def\@inverse#1#2\@nil{%
  \def\blank{}%
  \def\endhantei{#2}%
  \ifx\blank\endhantei #1\else\@inverse #2\@nil #1\fi
}

\makeatother

原理

\inverse{あいう}

\@inverse あいう\@nil 

(#1 <- あ, #2 <- いう)

\@inverse いう\@nil

(#1 <- い, #2 <- う)

\@inverse う\@nil いあ

(#1 <- う, #2 <- )

ういあ

結果

入力:

\inverse{}

出力:ぺ

入力:

\inverse{しんぶんし}

出力:しんぶんし

入力:

\inverse{ぺりをでぃすたす}

出力:すたすぃでをりぺ

クイズ

入力:

\inverse{ぺりを でぃすたす}

の出力は何になるでしょう?(「を」と「で」の間は半角スペースです)
























出力:すたすぃでをりぺ

「をりぺでぃすたす」でも「すたすぃで␣をりぺ」でもありません。(␣は半角スペースの意)

制御綴直後の空白が吸収されるためです。これの解決は(全く考えていないが直感的には)ちょっと大変そうなのでまた今度……

ぺりをだねぇ

\noexpand何もわからん・続編

マクロ設定

\def\A#1#2{#1 is #2}
\def\B{\BB\BB}
\def\BB{foo}
\def\C{bar}

前回の結果

\edef\hoge{\noexpand\B\C} % -> \def\hoge{\B bar}
\expandafter\A\hoge % -> \A\B bar -> \B is bar -> \BB\BB is bar -> foofoo is bar

出力:foofoo is bar

\edef\hoge{\noexpand\B\noexpand\B\C} % -> \def\hoge{\B\B bar}
\expandafter\A\hoge % -> \A\B\Bbar -> \B is \B bar -> \BB\BB is \BB\BB bar -> foofoo is foofoobar

出力:foofoo is bar

\edef\hoge{\noexpand{\B\B}\C} % -> \def\hoge{\B\B bar}?
\expandafter\A\hoge % -> \A\B\Bbar -> \B is \B bar -> \BB\BB is \BB\BB bar -> foofoo is foofoobar ?

出力:foofoofoofoo is bar

最後の出力結果を見て、1行目は

\def\hoge{{\B\B} bar}

なのではないかという仮説を立てました。それを検証します。

追加実験

\edef\hoge{\noexpand{\A\B}\C} %仮説では\def\hoge{{\A\B}bar}
\expandafter\A\hoge%仮説では\A\B is bar -> foo is i s bar

これを実行すると、\edef文中でエラーを吐きました。メッセージを見ると、

! Argument of \A has an extra }.

とあります。ということは、{}の中身は展開しようとしており、展開を制御されているのは{だけであることがわかります。\noexpandは直後の1トークンの展開を制御しているものだが、{の展開を制御することは{}囲いを崩さないというだけにすぎず、{}の中身を展開しないという効果までは持たない、ということが判明しました。確かに\expandafterのときに{}によるカタマリという概念が一切存在しないということを思い出すべきでした。

振り返る

前回仮説を立てるために使った例は

\edef\hoge{\noexpand{\B\B}\C}%{\BB\BB\B}\C -> {foofoo\B}\C -> {foofoo\BB\BB}\C -> {foofoofoofoo}\C -> {foofoofoofoo}barという展開により
%\def\hoge{{foofoofoofoo}bar}と同義
\expandafter\A\hoge % -> \A{foofoofoofoo}bar -> foofoofoofoo is bar

となり、出力結果に合います。

他にも、

\edef\hoge{\noexpand{\expandafter\A\B}\C} % -> \edef\hoge{{\A\BB\BB}bar} -> \edef\hoge{{foo is foo}bar}
\expandafter\A\hoge % -> \A{foo is foo}bar -> {foo is foo} is bar -> foo is foo is bar

出力: foo is foo is bar

となり、正しそうです。


展開制御は闇が深いんだなぁ


ぺりをだねぇ

\noexpand何もわからん

マクロ設定

\def\A#1#2{#1 is #2}
\def\B{\BB\BB}
\def\BB{foo}
\def\C{bar}

noexpandの挙動実験

\edef\hoge{\noexpand\B\C} % -> \def\hoge{\Bbar}
\expandafter\A\hoge % -> \A\Bbar -> \B is bar -> \BB\BB is bar -> foofoo is bar

出力:foofoo is bar

\edef\hoge{\noexpand\B\noexpand\B\C} % -> \def\hoge{\B\B bar}
\expandafter\A\hoge % -> \A\B\Bbar -> \B is \B bar -> \BB\BB is \BB\BB bar -> foofoo is foofoobar

出力:foofoo is foofoobar

\edef\hoge{\noexpand{\B\B}\C} % -> \def\hoge{\B\B bar}のつもりで書いたが……
\expandafter\A\hoge 

出力:foofoofoofoo is bar

これが上の例と違うのヤバくないですか?
\edef\hoge{\noexpand{\B\B}\C} の展開結果が\B\B barではなく{\B\B}bar になることなんて許されるのか????

ググっても\noexpandの直後の1制御綴に対して適用するものばかりですし。

\noexpandは一つの引数を取る通常のマクロではなく、\noexpandの直後のトークンの展開を制御する制御綴で、上は{の展開を制御した結果として中の\B\Bが展開されずに済んだ、というそういうことなんですかね

これから仮説の検証に入ります……



ぺりをだねぇ