2011/08/31

Parsec3 におけるパーサーの型

きっかけは try

いつものように Parsec2 で安穏とパーサーを書き始めたのですが、 try が増えそうな気配だったこともあり、これを期に「Applicative! Parsec3!」と思い立ちました。とっかかりとして、『Real World Haskell』 16章の例を Parsec3 で試してみることにします。
import Control.Applicative hiding ((<|>))
import Text.Parsec

parser = (++) <$> string "HT" <*> (string "TP" <|> string "ML")
Parsec2 だと、 RWH に堂々と「Parsec プログラマは型宣言を省略するのが通常になってます」と書いてあったりして、パーサーの型シグネチャをはしょって生きてきました。その感覚でこのコードをコンパイルしたところ、こんなクレームが。
No instance for (Stream s m Char)
arising from a use of `string'
はて、(Stream s m Char)ってなんだろう。調べてみると、 NoMonomorphismRestriction なる言語拡張を導入すれば解決するらしい(参照)。いわれるままソースの冒頭に {-# LANGUAGE NoMonomorphismRestriction #-} という行を追加すると、確かにコンパイルに通ります。なにこのおまじない。。

単相性制限

憤慨していたら @bonotake さんからヒントをもらいました(ありがとうございます)。
型定義が与えられていない関数の型を推論する際に、多相的な型が想定できる場合でもデフォルトの型(Integerなど)に決めうちする、という規則らしいです。そのオプションを指定するか、型定義を忘れず自分で書けw ということらしい
http://twitter.com/#!/bonotake/status/108445024378818562
つまり型推論のときの付加的なルールとして単相性制限というのがあり、それを無効にするためのオプションがこのおまじないみたい。それにしても、単相性制限と (Stream s m Char) のインスタンスがないというエラーの関係はさっぱりわかりません。

単相性制限のことはいったん忘れて、パーサーの型を明示することに挑戦します。Parsec2 であれば GenParser Char st [String] なのですが、Parsec3 のパーサーの型は RWH には説明がありません。 NoMonomorphismRestriction を指定すればコンパイルに通ることはわかっているので、ずるをして GHC に型を教えてもらいます。
*Main GOA> :t parser
parser :: (Stream s m Char) => ParsecT s u m [Char]
おや、こんなところに Stream が。確かに、この型クラスのインスタンスが決まらないとパーサーの型が決まらなそうだけど、どうして単相性制限を使わないことにすると型推論が通ってしまうのだろう?

単相性制限のことはもう一度忘れて、GHC が教えてくれた型を明示してコンパイルしなおしてみます。
parser :: (Stream s m Char) => ParsecT s u m [Char]
parser = (++) <$> string "HT" <*> (string "TP" <|> string "ML")
結果
Non type-variable argument in the constraint: Stream s m Char
(Use -XFlexibleContexts to permit this)
In the type signature for `parser':
parser :: (Stream s m Char) => ParsecT s () m [Char]
制約に型変数じゃないものがあるとな。しかも、回避したければやっぱりおまじないを唱えろと。

Parsec3 におけるパーサーの型

おまじないはいやなので、 ParsecT が何ものかを調べてきっちりナシを通すことにします。まずは Hackage のドキュメントを眺めます。すると次のことがわかりました。
  • パーサーの型は ParsecT で、これはモナド変換子である。
  • 厳密な型シグネチャは ParsecT s u m a
    • s は抽象化された入力の型。この抽象化された入力をストリームという。
    • u はパース時に好きな状態を格納しておく容器の型。
    • m はモナド変換子にとって基盤となるモナド。
    • a は出力の型
さらに、Stream というストリームを用立てるための型クラスがあって、そのインスタンスを作ることでパーサーへの入力の型が決まるようです。Stream のインスタンスになるには、入力の型(String とか)と、それを読み込んでいくときの単位の型(Char とか)、入力から 1単位だけ読み出して残りと組にして返す関数が必要です(実際には得られる組を Maybe でくるんだものをさらに基盤となるモナドでくるむので、そのモナドも与えます)。

ようやく見えてきました。parserの型、つまり文字列から文字列を探して返すパーサーの型は、 Monad m => ParsecT String u m String です。
import Control.Applicative hiding ((<|>))
import Text.Parsec

parser :: Monad m => ParsecT String u m String
parser = (++) <$> string "HT" <*> (string "TP" <|> string "ML")
結果
*Main GOA> parseTest parser "HTTP"
"HTTP"
もちろん必要なら um に具体的な何かを指定できます。何も例が思いつきませんが、いろいろ検索してみたら、ユーザーステートを配列代わりにして brainfuck を作り、処理結果を IO に直接吐き出すという使い方が……。

Haskell: ParsecのParsecによるBrainfuck(SnowClust)
http://inviernostring.blog106.fc2.com/blog-entry-32.html

ところで Parsec3 のパーサーの型は、brainfuck を作ったりするのでなければ、Parser String という具合にお手軽に指定できるようです( Text.Parsec.String が必要)。
import Control.Applicative hiding ((<|>))
import Text.Parsec
import Text.Parsec.String

parser :: Parser String
parser = (++) <$> string "HT" <*> (string "TP" <|> string "ML")
これは、こういう定義が Parsec に用意されているからです。
type Parsec s u = ParsecT s u Identity
type Parser = Parsec String ()
つまり Parser という型変数を使うということは、入力としては String が、ユーザーステートとしては () が、基盤となるモナドとしては Identity が使われているということですね。

結局、単相性制限と最初のエラーの関係は?


もやもや中。

2011/08/25

技術書の索引にかける3つの覚悟

技術書の索引を作るのは、一筋縄ではいかない仕事です。
「IT書籍の索引について考える」(KeN's GNU/Linux Diary)
矢吹さんのtweetから始まって、まとまりはないけど索引について思考実験。オチはないよ。
まず索引というのは、そもそも正しいページを参照するものでなければなりません。42ページを見よとなっているのに、そのキーワードも関連する概念の説明も42ページに出現しないのはまずい。また、キーワードAに対して「キーワードBを見よ」などとなっている場合もありますが、これが循環しているのもまずいです。Aの項にもBの項にも具体的なページ番号がないのは論外ですが、「…見よ」が循環しているのもまずいです。こうした参照先について要求されることがらを「索引の健全性」と呼びます。(いま作った用語。このほか、キーワードが正しく五十音順に並んでいることなんかも健全性に含まれますね。)

一方、索引には、キーワードがあますところなく網羅されている必要もあります。こちらは、いわば「索引の完全性」です。索引の完全性は、「このキーワードの説明ページに飛びたい」に応えることだけではありません。「本に出現するキーワードはわからないけど、こんな話ってどこに書いてあるんだろう」とか「この話、あのキーワードの近くに説明があったような……」といった漠然とした要求にも応えられるよう、キーワードが選ばれている必要があります。

読者にとって索引の悲劇というやつは、たいてい、完全性が満たされていない事態です。健全性がまるでないことが発覚した本は、発行後に回収される場合さえあるので、作ってるほうはあまり手を抜いてません。健全性は機械的に(必ずしも自動的にという意味ではない)チェックができる性質でもあります。時間や人手が足りないなら、項目を減らせばいい。かくして完全性からは程遠い索引(けど健全)ができあがります。@kmutoさんが指摘するように、索引はページ割りが確定しないと着手できない場合も多く、それはスケジュール上は時間や人手が最高に足りない時間です。つまり索引の完全性は、なりゆきで本を作っていると、おのずから低下します。少しでも完全性の高い索引を作るには、著者や編集者にそれなりの心構えが必要ということです。心構えというより、むしろ覚悟がいります。
  1. キーワードの選択と参照先ページの決定(この両方の作業をインデキシングといいます)には時間がかかるという覚悟
  2. インデキシングに著者を巻き込む覚悟
  3. 著者にはできないインデキシングもあるという覚悟
キーワードを選び出す仕事は索引作りの端緒にすぎません。そのキーワードを参照すべきベストなページ番号を選び出す仕事、あるいは、そのキーワードの文脈を判断して必要があれば複数の索引項目や階層的な索引項目にする仕事が不可欠です。もちろん、頭から眺めてそれっぽいキーワードをピックアップし、PDF で検索して出現ページを調べ、それを組にして五十音順でソートすれば、健全性の保たれた索引はできあがります。でも完全性を目指した索引は、全文検索とは違う、ひとつのコンテンツです。だから、索引作りには時間がかかります。少なくとも書籍全体をメタに読み直すだけの時間がかかります。(というわけで電子書籍にもいかした索引は必要なんですよ。)

で、その時間を誰に割いてもらうか、というのが上記の2.と3.です。@kmuto さんがいうように、本の中身をいちばんよく知っているのは著者であり、著者が主体的にインデキシングした本はよい索引になることが多いです。が、ページ割が決まった後でないとインデキシングできないのでは、著者に作業をお願いしにくい。この点、LaTeXとかXMLからの自動組版は秀でています。なにせ、著者が自分の手で原稿に入れたメタ情報をそのまま情報を落とさずに利用できるのだから。つまり、こういう要求にも応えられる。
自分のDebian徹底本では索引用に5種類くらいのタグを使い分けて後処理していた。当時は紙に組んだら情報がかなり落ちてしまったのでいろいろもったいなかったんだけど、今ならもうちょっとうまい方法を思いつけそう
http://twitter.com/#!/kmuto/status/106236697867599872
実際、『RailsによるアジャイルWebアプリケーション開発』という本の第2版以降は、原著のXMLデータ(著者本人が作ってるもの)からあらゆるインデキシングに関する情報を抜き出しているつもり。(第4版はもうちょっとお待ちください。)

とはいえ、著者が作ればうまい索引になるというわけでもないのだよなあ。そこで、だからこそ編集者がいるんだ、というポジショントークです。最近の自分がかかわった例だと、『Coders at Work』という読み物があるのですが、これ、ちゃんと索引つけています。なんで読み物に索引ついてるんだという話ですが、この記事で最初のほうに言及した「この話、あのキーワードの近くに説明があったような……」に応えるためというのと、複数の話者のインタビュー集なので、同一の話題をパラレルに参照可能な手段があるほうがよかろうなと考えたからです。たとえば「テスト」のような項目を引いてもらうとわかりますが、別々の話者が主にテストについて語っている部分に飛べるようになっています。「テスト」というキーワードの直接出現がない場合もあるので健全性を一部犠牲にしていますが、後悔はしていない。なお、本書は原書にも索引はあるのですが、これが典型的な「それっぽいキーワードの出現箇所を適当に参照してみました」な出来だっていたので、結果的には参考にしませんでした(どうやら英語圏の大手出版社には「indexer」という専門職がいるようなので、その仕事っぷりによって索引の出来が大きく左右されてるような気もします)。

とはいえ、編集者が不要なインデキシングができるすごい著者はたくさんいて、たとえば『プログラミングのための確率統計』の著者の方々とか。それでも、編集者としては、たとえば「Γ関数」を「G」からも「記号・ギリシャ語」からも「カ行」からもひけるようにしました。いや、もしかしたらこれも平岡さんがやったのだったっけ? そこはかとなく気もしてきましたが、とにかくこれはすごい本です。

2011/08/16

TeXでナベアツ

以下の動作を行うマクロ \NabeAzz を作れ。
http://d.hatena.ne.jp/zrbabbler/20110815/1313398638


一晩すぎたっぽいし、回答例アップしてもいいよね。
\newcount\n \newcount\i
\newcount\r \newcount\q
\newcount\a \newcount\b
\newcount\d
\font\cmfi=cmfi10 at 12pt

\newif\ifhasdigit
\def\hasdigit#1#2{\d=#1 \r=10 \q=#2 \a=\q
\divide\q by \r
\b=\q \multiply\b by -\r
\advance\a by\b
\ifnum\a=\d \hasdigittrue
\else \ifnum\q=0 \hasdigitfalse
\else \hasdigit{\d}{\q}\fi\fi}

\newif\ifdividep
\def\dividep#1#2{\r=#1 \q=#2 \a=\q
\divide\q by \r
\b=\q \multiply\b by -\r
\advance\a by\b
\ifnum0=\a \divideptrue
\else \dividepfalse \fi}

\def\NabeAzz#1{\n=#1 \i=1
\loop \ifnum\i<\n
\hasdigit{3}{\i}\dividep{3}{\i}
\ifhasdigit{\cmfi \number\i}\else
\ifdividep{\cmfi\number\i}\else
{\number\i}\fi\fi
\advance \i by 1
\repeat}

\noindent\NabeAzz{400}

\vfill\eject\end
とくに奇妙なことはしていません。10進表記でない整数(16進とか8進とか文字とか)も引数にとれるところがちょっとポイント。アプローチは FizzBuzz とまったく同じ。FizzBuzz もナベアツも、整数の剰余をどう扱うかに TeX ならではの難しさがあるので、TeX マクロを使えるかどうかよりも、初等代数的なことを考える力を試されている気分。

参考文献はいうまでもなく "TeX Book" 一択。日本語版だと 296ページに出てくる \hex マクロを参考にしています。

はじめてでも安心 SXML入門

HTMLやXMLの文章を扱っていると、気楽にこんな操作をしたいケースが間々あります。

  1. 親に応じて処理を分けたい(例: <title> 要素に対して、その親が <chapter> なら処理 A 、 <section> なら処理 B をしたい)
  2. ある属性を持つ要素だけ処理したい(例: lang="en"<p> 要素だけ抜き出したい)
  3. 同じレベルの次の要素に応じて処理をしたい(例:表の各行の色を縞々にするため、 <table> 直下の <tr> に交互に属性 bgcolor="#cccccc"bgcolor="#ffffff" をつけたい)
(いずれもブラウザへの表示であれば CSS で実現できますが、いまは出力も XML データとして欲しい場合の話です。)

XML データの本質が木構造であることを思い出すと、この山括弧が S 式にさえなっていれば、 Scheme の関数でどうにでも操作できる気がしてきます。SXML は、まさにそのような夢をかなえてくれるものです。詳しくは Wikipedia で。ここでは、Gauche に入っている XML から SXML へのパーザーと SXML の操作関数、山括弧表記へのシリアライザを使って、SXML データを操る方法をまとめます。

XML データを SXML へ

何も考えずに ssax:xml->sxml というユーティリティを使います。自分の目的に応じた XML パーザを作る仕組みも提供されているのですが、私は使ったことがありません。

(use sxml.ssax)

(call-with-input-file "test.xml"
  (lambda (port)
    (with-module sxml.ssax
      (ssax:xml->sxml port '()))))

ただ一点だけ、このユーティリティを黙って使う際にぶつかる壁があって、それは文字実体参照です。定義済み実体しか解釈してくれません。そのため、たとえば &nbsp; が混ざっているとはじかれます。 &nbsp; は HTML でよく使われるので、これはいささか不便です。

ssax:xml->sxml には外部の文字実体参照を指定できる仕組みがないので戸惑うところですが、 sxml.ssax のソースを見ると ssax:predefined-parsed-entities というのがあって、これをつつけばよさそう。幸い、 Gauche には with-module という仕組みがあるので、これを使って文字実体参照のデフォルトを「上書き」してしまいます。

(use sxml.ssax)

(call-with-input-file "test.xml"
  (lambda (port)
    (with-module sxml.ssax
      (fluid-let ((ssax:predefined-parsed-entities
                  (list '(nbsp . " ") '(amp . "&") '(lt . "<") '(gt . ">")
                        '(quot . "\") '(apos . "'")))
        (display (ssax:xml->sxml port '()))))))

これで次のようなファイル "test.xml" が、

<body>
  <p>ab&nbsp;c</p>
</body>
次のような S 式になります。
(*TOP* (body (p "ab c")))

肩慣らし:要素の取得

とりあえず、これから使うサンプルの XML データを用意します。

(define newbooks "
<table>
  <thead>話題の新刊</thead>
  <tbody>
    <tr><th>書名</th><th>価格</th></tr>
    <tr><td>抽象によるソフトウェア設計</td><td>4500円</td></tr>
    <tr><td>インターネットのカタチ</td><td>1900円</td></tr>
    <tr><td>Scheme修行</td><td>2800円</td></tr>
    <tr><td>Coders at Work</td><td>2800円</td></tr>
  </tbody>
</table>
")

また、 XML の山括弧で表現されたテキストを SXML のデータに変換する関数をでちあげます(先に使ったコードと本質的に同じもの)。

(define (string->sxml str)
  (call-with-input-string str
    (lambda (port)
      (ssax:xml->sxml port '()))))

これから何度か利用するので、テーブル newbooks を SXML に変換したものに名前をつけておきます。これは恣意的に root という名前にします。

(define root (string->sxml newbooks))

準備はここまで。肩慣らしにテーブルからヘッダ部分を取り出してみましょう。実は XPath のような指定が使えます。

gosh> ((sxpath "table/thead") root)
=> ((thead "話題の新刊"))

"table/thead"」の部分には、求めるノードへのパスを XPath に似た(ほとんどそのまんまの)構文で記述できます。その記述に関数 sxpath を適用すると、ノードの集合からノード(記述したパスを満たすもの)を取り出す関数(コンバーターと呼びます)が返ってくるので、それを root に適用すれば求めるノードが手に入るという仕掛けです。

よりScheme風に

関数 sxpath は便利で多機能なのですが、クエリを組み立てるのに XPath の文法を知らないといけないので、ここではもうちょっと Scheme ふうのやり方を紹介します。

gosh> ((node-closure (ntype-names?? '(thead))) root)
=> ((thead "話題の新刊"))

この方法も、「ノードの集合からノードを取り出すコンバーター関数を作って、それを root に適用する」という方針は sxpath バージョンと同じです。コンバーターの作り方がちょっと違います。 node-closure という関数は、引数として述語をとり、その述語を満たすようなノードを「再帰的に」とってくるコンバーターを作ります。「再帰的に」というのは、述語を満たすノードをSXML木の根っこを起点に探すだけでなく、あらゆる子孫(大本の根っこも含みます)を起点に探すという意味です。この例では、「thead という名前かどうか?」( (ntype-names?? '(thead)) )という述語を指定しているので、とにかく木全体からその名前のノードをとってきます。

ところで、得られるノードは1つとは限りません。

gosh> ((node-closure (ntype-names?? '(td))) root)
=>((td "抽象によるソフトウェア設計") (td "4500円") (td "インターネットのカタチ") (td "1900円")
   (td "Scheme修行") (td "2800円") (td "Coders at Work") (td "2800円"))

ここで気にかけておくべきなのは、こうして REPL から結果がリストとして返ってくるとまるで結果のノードからなる新しいリストが得られたように錯覚するけれど、これらはちゃんと root の部分木になっているということです。つまり、これらの結果から、例えば「親」を知ることができます。

gosh> (define tds ((node-closure (ntype-names?? '(td))) root))
gosh> (((sxml:parent (ntype?? '*any*)) root) (car tds))
=> ((tr (td "抽象によるソフトウェア設計") (td "4500円")))

(ntype?? '*any*) は「何でもいい」という述語です。この式は、親( sxml:parent )なら何でもいいからとってこい、という意味になります。

くどくいようですが、上記の (car tds) は表現としては (td "抽象によるソフトウェア設計") というリストだけれども、そういう表現のリストに対して上記の結果が得られるのではありません。(当たり前の話だけど、最初はやっぱり見た目に惑わされがち。)

gosh> (((sxml:parent (ntype?? '*any*)) root) '(td "抽象によるソフトウェア設計"))
=> ()

実践編:ノードを追加する

SXML は木にすぎないので、その一部を変更するような操作も、 Scheme のような言語であればわりと直感的に書けます。ここでは、先の書籍一覧のテーブルの各行に「税」という項目を追加してみます。もちろん税額は、すでにテーブルに含まれている価格から求めます。そこでまず、価格の文字列から税額の文字列を作る関数を定義します。ここでは安直に正規表現で。

;; string -> string
(define (tax-price str)
  (rxmatch-if (#/([\d]*)円/ str)
      (m price)
    #`",(* 5/100 (x->integer price))円"
    #f))

次に、この tax-price 関数を使って「 <tr> ノードから税額を要素に持つ <td> ノードを作る関数」を定義します。なお、 SXML の一部をいじる関数を作るときは、このようにノードからノード(あるいはノードの集合)への関数として定義しておきましょう。そのほうが SXPath 用の適用関数をいろいろ利用できて便利です。

;; node -> node
(define (tax-node node)
  (cond ((tax-price (sxml:string-value node)) => (pa$ list 'td))
        (((sxpath "th") tr) '(th "税"))
        (else '())))

あとは各行に tax-node で得られるノードを要素として追加するだけですが、要素を追加する関数は Gauche にはないようなので、既存の要素 (sxml:content-raw tr) と新しいノード (tax-node tr) とを連結したもので既存のノードを置き換えます。木の一部を破壊的に変更する必要があるので、きちんと避難しましょう(『 Scheme 修行』ね)。

(let1 root (string->sxml newbooks)
  (for-each (lambda (tr)
              (sxml:change-content! tr `(,@(sxml:content-raw tr) ,(tax-node tr))))
            ((node-closure (ntype-names?? '(tr))) root))
  root)

=> (*TOP* (table (thead "話題の新刊") (tbody
      (tr (th "書名") (th "価格") (th "税"))
      (tr (td "抽象によるソフトウェア設計") (td "4500円") (td "225円"))
      (tr (td "インターネットのカタチ") (td "1900円") (td "95円"))
      (tr (td "Scheme修行") (td "2800円") (td "140円"))
      (tr (td "Coders at Work") (td "2800円") (td "140円")))))

軸とか

テーブルのいちばん左の列をヘッダのように使いたいことがあります。各 <tr> ノードの最初の要素に bgcolor="#cccccc" とつけるにはどうしたらいいでしょうか。

ぱっと思いつくのは、先行する要素がなければ属性を追加する、という処理です。 SXML では、 XPath のように、親や子孫、先行や後続といった「軸」が利用できます。これを使って、各行の先頭の要素に背景色の属性を付けてみましょう。

(let1 root (string->sxml newbooks)
  (for-each (lambda (td)
              (if (null? (((sxml:preceding-sibling sxml:element?) root) td))
                  (sxml:set-attr! td '(bgcolor "#cccccc"))))
            ((node-closure (ntype-names?? '(td th))) root))
  root)

使っている軸は、先行する兄弟の軸 sxml:preceding-sibling です。 <td> 要素のそれぞれについて、先行する兄弟の要素がいるかどうかチェックし、いなかったら属性を設定しています。要素の軸は、その要素が含まれる木(もちろん部分木のことも)に対して決まるものなので、 ((sxml:preceding-sibling sxml:element?) root) のように木を明示する必要があります。

=> (*TOP* (table (thead "話題の新刊") (tbody
      (tr (th (|@| #0=(bgcolor "#cccccc")) "書名") (th "価格"))
      (tr (td (|@| #0#) "抽象によるソフトウェア設計") (td "4500円"))
      (tr (td (|@| #0#) "インターネットのカタチ") (td "1900円"))
      (tr (td (|@| #0#) "Scheme修行") (td "2800円"))
      (tr (td (|@| #0#) "Coders at Work") (td "2800円")))))

なお、ここではわざわざ軸を使っていちばん左の列を選択しましたが、 1つめの要素、のような指定の仕方で取得することももちろんできます。

(let1 root (string->sxml newbooks)
  (for-each (lambda (tr)
              (sxml:set-attr!
               (car ((node-pos 1) ((node-closure (ntype-names?? '(td th))) tr)))
               '(bgcolor "#cccccc")))
            ((node-closure (ntype-names?? '(tr))) root))
  root)

山括弧にするには

良し悪しは別にして、やはり山括弧に戻せないといろいろ不便です。とくに html をいじっている場合には。Gauche のマニュアルを見ていると sxml->html という名前が付いた関数が二種類(sxml:sxml->htmlsrl:sxml->html)ありますが、ここで使うべきは srl:sxml->html のほうです。

(use sxml.serializer)

(srl:sxml->html
 (let1 root (string->sxml newbooks)
   (for-each (lambda (tr)
               (sxml:set-attr!
                (car ((node-pos 1)
                      ((node-closure (ntype-names?? '(td th))) tr)))
                '(bgcolor "#cccccc")))
             ((node-closure (ntype-names?? '(tr))) root))
   root))

よろしくインデントされた結果が返されます。

<table>
  <thead>話題の新刊</thead>
  <tbody>
    <tr>
      <th bgcolor="#cccccc">書名</th>
      <th>価格</th>
    </tr>
    (略)
  </tbody>

</table>

最後に宣伝

『Scheme修行』

ちなみに、この記事のタイトルは『はじめてでも安心コスプレ入門』から

2011/08/13

3才、補助輪なし自転車への挑戦

3才半の息子のために自転車を買いました。補助輪もついていたのですが、取り付けずにさっそく公園にいきます。



気持ち下り勾配のある場所なのですが、はじめてペダルつきの自転車に挑戦して、いきなり一人で乗ることができました。我が子ながらよくやったと思います。ちなみに、この自転車の商品名は「いきなり自転車」です。先日『インターネットのカタチ』という本にからめたイベントの懇親会でこの自転車の販売会社の方と知り合い、旧モデルをゆずっていただきました。ありがとうございます。

で、まあ親バカ記事ではあるのですが、なんで3才半でいきなり自転車に乗れるのかを知りたい人はけっこういると思うので、種明かしをします。実は、彼は1才半ころからこれで遊んでました。

balance bike

ペダルなしの二輪車です。一般名称としてはバランスバイクとか呼ばれています。この「ストライダー」という商品を選んだのは、Webでいろいろ調べたところ、さまざまな配慮がなされた商品であるように見受けられたからです。当時は日本の代理店が皆無だったので個人輸入しました(自転車は車両扱いで関税がかからない!)。最近は日本でもふつうに手に入るようになり、街でも見かけるようになりました。

車体がそこそこ軽いので、1才も終わりになると自分で取り回せるようになりました。足で地面を蹴って進むので1才でもよちよちながら走行を楽しめます。しかし、まがりなりにも二輪車なので、ある程度のスピードで走るには前進速度とバランスに対する感覚が必要になります。また、公道を走るからには、交差点で止まるとか、他人がいるときは飛ばさないとか、そういう交通に対する理解も培っていく必要があります。

最近ではこれくらいには乗りこなせるようになっていました。



15秒付近から両足を離して滑走しています。(個人差はあるでしょうが、たいていの子はしばらく練習すればこれくらい走れるようです。)

というわけで、3才半でいきなり補助輪なし自転車で走れたのは、このバランスバイクで養った何かのおかげではないかと推測しています。もちろんサンプル数が高々1なので推測の域は出ませんが、自分はかつて苦労して補助輪はずしの練習をした覚えもあるし、バランスバイクの影響がまったくないということはないように思います。

ただ、補助輪なしで走れるには走れるのですが、3才半だと自転車の本体が重くて取り回しがうまくできないのと、ペダルをこぎ出すための筋力が足りないようで、自力でゼロからこぎ出せるわけではありません。最初の動画のように下り勾配がある場所ならいけるのですが、そうでなければ助走が必要です。「いきなり自転車」の後ろには大人が制御するための舵取り棒がついていたので、これはたいそう助かりました。

2011/08/10

TeXで「最後のコマンド」だけ挙動を変える(ワンパス版)

@munepixyzさんのツイートで、あるコマンドの挙動を最後の出現のときだけ別なものにしたい、というTeXフォーラムへの質問があったことを知りました。

最後のコマンド
http://oku.edu.mie-u.ac.jp/tex/mod/forum/discuss.php?d=691&parent=3733

すでに、LaTeXで.auxファイルを使うというお手本のような回答があがっていますが、くだんのコマンドが本文のトップレベル階層にしか出現しない場合には、texコマンドを一回起動するだけで求める動作を実現できます。TeXフォーラムに登録していないため、ここでネタにします。

いつもは文字列「hoge」を出力し、最後の出現だけは文字列「fuga」を出力するコマンド\hogeの定義はこれだけです。(最後の\fiと#2の位置が逆だったバグを直しました(2012/2/27)。utさんコメントでの指摘ありがとうございます。)
\long\def\hoge#1\hoge#2{%
  \ifx#2\end fuga#1#2%
    \else hoge#1\expandafter\hoge
  \fi#2}

こんなふうに使います。pdftexを起動してインタプリタに以下を貼り付ければ、最後の\hogeだけがfugaに置き換わったPDFができあがります。
\hoge \hoge $1+2=3$ \hoge

\hoge \hoge

oshimai
% 本文はここまで

\hoge\end % この行を本文の最後に追記する
\eject

\hogeの定義では、引数を指定する部分が#1\hoge#2というあまり見慣れない形をしています。これは大雑把に言うと「次に\hogeが出てくるまでを#1に、その\hogeの直後のトークンを#2に束縛して、定義の本体を実行せよ」という意味です。このように、TeXのマクロでは引数のパターンマッチができる!

実際には1つのパターンでしか定義できないので、これはパターンマッチでもなんでもないのですが、この「パターンでマクロの引数を取り出す」手法はTeXプログラミングの主要な武器のひとつです。少なくとも私はそうおもっているので、自分でもけっこう使う機会が多いです。これを知ってればlatex.ltxだってもりもり読めるよ。