檜山正幸のキマイラ飼育記 このページをアンテナに追加 RSSフィード

キマイラ・サイトは http://www.chimaira.org/です。
トラックバック/コメントは日付を気にせずにどうぞ。
連絡は hiyama{at}chimaira{dot}org へ。
蒸し返し歓迎!
ところで、アーカイブってけっこう便利ですよ。タクソノミーも作成中。今は疲れるので作っていません。

2016-05-23 (月)

猫と子供

| 11:40 | 猫と子供を含むブックマーク

これ、中国語だし、「もと動画から許可とってる?」とか思ったりもするのですが、「猫と赤ちゃん」の可愛さは無敵ですね。

D

この動画(↑)では、みんな(猫たち)赤ちゃんに気を使ってますね、爪を立てないようにしたり。でも、いつでも子供にやさしいとは限らないようです。

D

ところで、最初の動画の1分20秒辺りから、ミスタードーナツのポン・デ・ライオンだ。ミスタードーナツには何かと世話になりました、なつかしい。

トラックバック - http://d.hatena.ne.jp/m-hiyama/20160523

2016-05-19 (木)

水素水広告

| 11:36 | 水素水広告を含むブックマーク

いらすとやさんの「水素水のイラスト」

これは笑えます。けど、笑ってくれない人もいそうで、それがちょっと怖いです。

このイラストのページの広告には(たぶん、ほとんどの場合)水素水の広告が出ます。それがまた可笑しかったりします。「スポンサードリンク」が「スポンサー・ドリンク」に見えて、さらに笑える。

僕は、ケラケラ笑いながら、ドリンクならぬリンクをクリックしてしまったんですよ。すると、その後水素水の広告に追い回されるハメに。

Googleの広告はすぐに拒否できます。が、Yahooプロモーション広告のほうは、アンケートに「興味ない」と答えてもすぐには止まらないようです。しばらくして、水素水広告は出なくなりました。

正確に言うと「人間向けの水素水広告」は出なくなりました。今は、「犬用の水素水」の広告が出てます。

トラックバック - http://d.hatena.ne.jp/m-hiyama/20160519

2016-05-16 (月)

C/C++の思わぬ落とし穴(まーた、ハマっちまったぜ)

| 09:23 | C/C++の思わぬ落とし穴(まーた、ハマっちまったぜ)を含むブックマーク

上記の記事で、Shift_JIS(CP932)のコメントにまつわるトラブルを紹介しました。あまり時をおかずして、C/C++またハマりました。コメント事件ほどの意外性はないにしろ、事情を知らないと対処しにくいトラブルなので顛末を記しておきます。

この記事の内容は、僕が実際に体験した状況そのものではありませんが、それらしい例題を仕立てたので、ストーリーを追いかけてみてください。

内容:

  1. 例題:時刻付きのメッセージキュー
  2. 実装を変えてみると
  3. なんで、こんなことに
  4. 誰が悪いのか

例題:時刻付きのメッセージキュー

例題として、次のような機能を持つクラスを考えます。

  • 文字列メッセージを、時刻(タイムスタンプ)を添えてキューで管理する。

クラスのインターフェースを、次のようなヘッダーファイルに記述します。

// msgq.h
#ifndef MSGQ_H
#define MSGQ_H

#include <queue>
#include <string>

class MessageQueue {
public:
    void PostMessage(const std::string& msg);// 時刻を付加する
    const std::string TakeMessage(void);
    bool IsEmpty(void) const;
private:
    std::queue<std::string> queue_;
};

#endif

実装ファイルは次のようです。

// msgq.cpp Version 1
#include <time.h>    // time(), localtime()
#include <stdio.h>   // sprintf()

#include "msgq.h"

void MessageQueue::PostMessage(const std::string& msg) {
    time_t now;
    time(&now);
    struct tm *ptm = localtime(&now);
    char timeStamp[30]; // 多め
    sprintf(timeStamp, "[%4d-%02d-%02d %02d:%02d:%02d] ",
        ptm->tm_year + 1900,
        ptm->tm_mon + 1,
        ptm->tm_mday,
        ptm->tm_hour,
        ptm->tm_min,
        ptm->tm_sec
    );
    queue_.push(std::string(timeStamp) + msg);
}

const std::string MessageQueue::TakeMessage(void) {
    const std::string msg = queue_.front();
    queue_.pop();
    return msg;
}

bool MessageQueue::IsEmpty(void) const {
    return queue_.empty();
}

MessageQueueクラスをテストするためのプログラムも書きます。

// test-msgq.cpp
#include <iostream>
#include "msgq.h"

int main()
{
    MessageQueue msgq;
    msgq.PostMessage("hello");
    msgq.PostMessage("good morning");
    msgq.PostMessage("good afternoon");
    msgq.PostMessage("good evening");
    msgq.PostMessage("good night");
    while (!msgq.IsEmpty()) {
        std::cout << msgq.TakeMessage() << std::endl;
    }
    return 0;
}

このテストプログラムでは、メッセージに付加される時刻がほとんど同じになってしまいますが、それはいいとしましょう。

コンパイル&実行してみます。

$ g++ msgq.cpp test-msgq.cpp

$ ./a.exe
[2016-05-16 09:14:42] hello
[2016-05-16 09:14:42] good morning
[2016-05-16 09:14:42] good afternoon
[2016-05-16 09:14:42] good evening
[2016-05-16 09:14:42] good night

使っているコンパイラMinGWgccですが、今回の話では、コンパイラがなんであるかは影響しません。

実装を変えてみると

例題のプログラムはWindows上で動かすとしましょう。はじめに断っておくと、「まーた、Windows特有の話か」と言うと、そうでもなくて、ことの本質は、OS云々というよりはC/C++の仕掛けの問題です。

で、Windows上なので、time.hの関数を使う代わりに、Windows APIのGetLocalTime()関数を使うことにします。GetLocalTime()は次の点でtime.hの関数より便利です。

  1. time()とlocaltime()の2つを使う必要がなく、GetLocalTime()だけで済む。
  2. time_t型とstruct tm型(へのポインター)の2つを使う必要がなく、SYSTEMTIME型だけで済む。
  3. 年月に対して「+1900」「+1」のような補正をする必要がない。

GetLocalTime()版のMessageQueueクラスをVersion 2とします。

// msgq.cpp Version 2
#include <Windows.h> // GetLocalTime()
#include <stdio.h>   // sprintf()

#include "msgq.h"

void MessageQueue::PostMessage(const std::string& msg) {
    SYSTEMTIME now;
    GetLocalTime(&now);
    char timeStamp[30]; // 多め
    sprintf(timeStamp, "[%4d-%02d-%02d %02d:%02d:%02d] ",
        now.wYear,
        now.wMonth,
        now.wDay,
        now.wHour,
        now.wMinute,
        now.wSecond
    );
    queue_.push(std::string(timeStamp) + msg);
}

const std::string MessageQueue::TakeMessage(void) {
    const std::string msg = queue_.front();
    queue_.pop();
    return msg;
}

bool MessageQueue::IsEmpty(void) const {
    return queue_.empty();
}

さて、コンパイル&実行。

$ g++ msgq.cpp test-msgq.cpp
C:\Users\hiyama\AppData\Local\Temp\ccIGR1jP.o:test-msgq.cpp:(.text+0x55): undefined reference to `MessageQueue::PostMessage(std::string const&)'
C:\Users\hiyama\AppData\Local\Temp\ccIGR1jP.o:test-msgq.cpp:(.text+0xa2): undefined reference to `MessageQueue::PostMessage(std::string const&)'
C:\Users\hiyama\AppData\Local\Temp\ccIGR1jP.o:test-msgq.cpp:(.text+0xef): undefined reference to `MessageQueue::PostMessage(std::string const&)'
C:\Users\hiyama\AppData\Local\Temp\ccIGR1jP.o:test-msgq.cpp:(.text+0x13c): undefined reference to `MessageQueue::PostMessage(std::string const&)'
C:\Users\hiyama\AppData\Local\Temp\ccIGR1jP.o:test-msgq.cpp:(.text+0x189): undefined reference to `MessageQueue::PostMessage(std::string const&)'
collect2: ld returned 1 exit status

あんれー? エラーです。エラーの内容は、test-msgq.cppから参照されている`MessageQueue::PostMessage(std::string const&)'関数が見つからない、というものです。

なんで、こんなことに

現在時刻の取得に、time()とlocaltime()を使うか、それともGetLocalTime()を使うかは、実装者の判断であって、どっちを選ぶかは自由です。コンパイラに文句を言われる筋合いはないですよね。にもかかわらず、GetLocalTime()を使用するとダメなんですよ。なんで?

実は、(しばらく…が続く)

GetLocalTime()関数が直接の原因ではありません。問題なのは、MessageQueueクラスのメソッド(メンバー関数)であるPostMessage()です。「PostMessage」という名前そのもの、名前だけが問題なのです。

Windows APIにPostMessage()という関数があり、それと名前がかぶっているのです。しかし、Windows APIのPostMessage()は大域的関数、かたやクラスのメソッドなのだから、同じ名前でも問題がないだろう、と、そう思うでしょ。でも、ダメなんです。影響されちゃうんです。

Windows API関数の名前は、Windows.hのなかでマクロ定義された名前なんです。今回のケースでは、次のマクロ定義が作用します。

  • #define PostMessage PostMessageA

.cppソースコード内では、このマクロ定義が働いて「PostMessage → PostMessageA」という名前の置換が起きていたのです。定義された名前が MessageQueue::PostMessageA で、呼び出しに使った名前が MessageQueue::PostMessage ですから、呼び出せるわけがありません。

誰が悪いのか

やっぱりWindowsが悪いんでしょ -- とは言えないです。Windows.hに限らず、意図せぬマクロ置換によってコードを壊されることは、それほど珍しく無いと思います。コンパイルエラー/リンクエラーになってくれればいいですが、たまたま構文的に正しかったりすると相当分かりにくいでしょう。

C/C++では、プリプロセッサが言語処理系全体のなかでけっこう重要な役割を担っています。そのプリプロセッサは、C/C++の構文に関して何も知りません。名前のスコープのことなんか分からないので、闇雲なテキスト置換をやらかします。無知ゆえに間違いを犯しやすいのです。

プリプロセッサって、処理の入り口担当なんですが、処理対象に対する知識を全く持たないヤツに最初の処理を任せるってヒドイと思いませんか。栃木県生まれ東京在住で琵琶湖を見たことがない僕に、滋賀県の観光大使を任せるようなもんです(例えが意味不明? そうですね)。

昔に比べれば、プリプロセッサへの依存度は減ってはいる(inline関数とかconstによる定数とか)でしょうが、危なっかしいプリプロセッサ処理をナシにできないのかなー、と思ったります。

shiroshiro 2016/05/16 20:41 X11のヘッダだったかな。Statusという名前をマクロ定義していて、自分のコードの何かの名前とかぶってて悩んだことがあります。以来、自分が書く時はパブリックヘッダに含めるマクロ名には必ずプレフィクスをつけるようにはしています。

m-hiyamam-hiyama 2016/05/17 08:53 shiroさん、
マクロで意図せぬ置換は昔からありましたよね。他人様のものを上書きしないように用心してたこともありました。

#ifdef MY_MACRO
# error MY_MACRO is already defined.
#else
# define MY_MACRO
#endif

あと、一時的なマクロは使い終わるとすぐに #undef したり。でも、面倒さ(と、どことない虚しさ)でやらなくなってしまいましたね(苦笑)。

トラックバック - http://d.hatena.ne.jp/m-hiyama/20160516

2016-05-10 (火)

関手データモデル/圏論データベース: その後の発展と現状 (2016)

| 09:49 | 関手データモデル/圏論データベース: その後の発展と現状 (2016)を含むブックマーク

2013年の初頭に、デイヴィッド・スピヴァックの関手データモデル(functorial data model)について紹介しました。

あれから3年3ヶ月が経過して、今、関手データモデルや圏論データベース(categorical database)の状況はどうなっているでしょうか。

一言でいえば、

  • 派手に喧伝はされてないが、着実に発展している

となるでしょう。その進展の様子を次の3つの側面から概観してみます。

  1. ビジネス
  2. ソフトウェア
  3. 理論

内容:

  1. ビジネス: Categorical Informatics, Inc
  2. ソフトウェア: FQL IDE
  3. 理論: 等式論理と代数データベース

※ リンクと注釈がたくさんあるのは、この記事が、この話題に関する説明付きブックマークになるように、と意識したからです。

ビジネス: Categorical Informatics, Inc

関手データモデルに基づく製品やサービスを提供するIT企業としてCategorical Informatics, Inc が設立されています。

この企業が、急速な成長とイグジットを狙うスタートアップなのか、研究の合間にビジネス活動もボチボチやる程度のゆるーいチームなのか、ハッキリとは分かりません。いずれにしても、Categorical Informaticsが関手データモデル/圏論データベースのテクノロジーを扱う企業である事は間違いありません。

関手データモデルの創始者*1であるデイヴィッド・スピヴァック(David Spivak)はCategorical Informaticsのメンバーですが、彼のコミットメントの度合いはそれ程高くないように思います。スピヴァックはデータベース以外の研究もしてますし*2、学生の指導もしてます*3。ベンチャー起業家になる気はたぶんないでしょう。

Categorical Informaticsの中心人物となるのはライアン・ウィズネスキー(Ryan Wisnesky)*4です。

ウィズネスキーは、2012年頃*5からCategoricalData.netにおいてソフトウェアを開発していたようです。僕が2013年にCategoricalData.netを見た印象では、ソフトウェアも文書も断片的で何をやってるのか/やりたいのか? 分かりませんでした。その後たまに覗いても、あまり進捗した様子もないので忘れてしまったのですが、開発はちゃんと継続されていたのですね。

CategoricalData.netで開発されていたFQL IDEは、ちゃんと動くものとなり、マニュアルもだいたい揃っています*6。これがCategorical Informaticsの主力製品(今はこれしかない)というわけです。Categorical Informaticsは、ソフトウェア以外に、企業のデータ統合(data integration)案件などに対する分析・コンサルティングもやるようです。

Categorical Informaticsのもう一人のメンバーであるジョシュア・タン(Joshua Tan)も研究者で、ウィズネスキーとスピヴァックの学生です。が、スタートアップの経験があるということで彼がビジネスサイドを担うとのこと。

スピヴァック、ウィズネスキー、タンの三人とも、とてもアカデミックな感じで、資金や人を集められるのかなー? と余計な心配をしてしまいます。アカデミズム出身で辣腕を振るう起業家もいるので先入観はいけませんけどね。とりあえず大学(MIT)やNIST(National Institute for Standards and Technology)、NSF(National Science Foundation)からの支援はあるようです。スピヴァックのプロジェクト提案書が承認された、ってことでしょう。

ウィズネスキーはIBMと関係があり、スピヴァックはオラクルマイクロソフトに顔を出したりしています。パワーのある企業が関手データモデルに興味を示せば劇的な展開もあるかも知れません。もっとも、そんな展開が望ましいかどうかはまた別問題ですけど。

ソフトウェア: FQL IDE

FQL IDEは、関手データモデルやオペラディクス(Operadics)理論などを実装するソフトウェアです。Javaで書かれています。名前にFQL(Functorial Query Language)が冠されていますが、実際には複数の言語(後述)を扱います。基本的にはコードエディタで、言語処理系に対するフロントエンド機能を兼ね備えたものです。

ちゃんと動きますが、プロトタイプの域を抜けてない感じで、実用に使えるソフトウェアになるにはまだ時間がかかりそうです。実用性を脇においても、個人的には不満が色々あります。が、今回は不満を言うのはヤメときます。

現在、FQL IDEは次の4つの言語をサポートしています。

  1. FQL(Functorial Query Language)
  2. FQL++
  3. FPQL(Functorial Programming and Query Language)
  4. OPL(Operadic Programming Language)

なんで4つも言語があるの? たぶん、試行錯誤の結果がそのまま残ってしまった、という事情でしょう*7。構文と用途は少しずつ違う、言い換えると、4つとも似たような構文で用途も相当かぶっています。これ、あまりよろしくない状況(個人的不満じゃなくて、誰でも思うことだよね)。

いずれは単一言語に統合する予定なんでしょうが、現状では:

  1. FQL : データベース言語としては一番分かりやすく使いやすい。
  2. FQL++ : FQLの機能拡張・後継のはずだ(った)が、データベース言語としての使い勝手は落ちている。見捨てられたらしい。ただし、圏論の学習にはとても有用。
  3. FPQL : FQLのプログラム機能を強化したものらしいが、マニュアルがないのでよく分からない。OPLに吸収されて開発は止まりそう。
  4. OPL : 現状では、機能的に最上位の言語。プログラミング機能もデータベース機能も持っている。しかし、FQLに比べて記述が面倒になっているし、ビジュアライズもSQL生成機能もないので、OPLでFQLを置き換えられるとは言いがたい。

とりあえずFQLを使ってみることをおすすめします。SQLデータベースと関手データモデルに関する基本的な知識があれば使えます。スキーマスキーマ間の対応(マッピング)、データベースインスタンス(実際のデータのこと)、データベースインスタンス間の変換などを、比較的簡単な構文で定義できます。そして、スキーマの対応を使った問い合わせを実行できます。

要するに、データベースに関わる一通りの作業がFQLにより出来るのです。FQLコードに対応するSQLスクリプトも(可能ならば)生成してくれます。ある程度の規模の実例は、"Functorial Data Migration: From Theory to Practice"(9ページ) にレポートされています。

新しい言語OPLは、データベースに関わる概念以外に、型システムを導入し(まだ不十分)、汎用プログラミング言語との連携を意識しています。また、等式的推論機能を入れて、かすかに証明支援系の香りを醸し出しています。

型システムと等式的推論を入れた結果、CafeOBJなどのOBJファミリーやAlloyなどに近いものになっています。これは当然のことで、OBJファミリーのベースとなっているインスティチューションと、FQLのベースとなっている関手データモデルは、事実上同じものです。そのことは次の記事に書きました。

Alloyとの類似性に関して言えば、「Alloyを理屈っぽく考えてみようと思う」で次のように書いたことがあります。

関手データモデルとAlloyは、かなり親和性が高いんじゃないか、と感じるのです。
[...snip...]
また、関手データモデルを理解する道具としてAlloyが役に立つとも言えます。

これはつまり、AlloyのモデルとFQL/OPLのモデルは似てるってことです。今ではFQL/OPLが使えるので、関手データモデルにAlloyを使う理由はなくなった、とも言えますが。

形式的仕様記述言語、モデル発見器、証明支援系などを使った経験があれば、FQL/OPLはとっつきやすいと思います。また、OPLと一緒に使える汎用プログラミング言語はJavaScripなので、大変にポピュラーな言語と連携できるわけです(Java8にJavaScriptエンジンNashorn(ナスホーン)が付いているので、それを使ってJavaScriptコードを走らせています)。でも、JavaScriptとの連携方法がすげーカッコ悪い -- 将来の改善に期待。

FQL IDEがサポートする4つの言語のなかで、位置付けがハッキリしないカワイソウなFQL++ですが、圏論の学習にはとても有用」という特徴があります。これだけを切り出して圏論学習ツールにして欲しいくらいです。FQL++に関しては、いずれ別な記事で語りたいと思っています。

理論: 等式論理と代数データベース

今の関手データモデルを初期(2010年から2012年くらい)のものと比べると、理論面でも長足の進歩を遂げています。圏論特有のアブストラクト・ナンセンスを突き詰めているようにも見えますが、ソフトウェア実装のための要求が相当な刺激・圧力になっているはずです。

当初の関手データモデルでは、テーブル(レコードの集合)とドメイン(値の集合)の区別をしていませんでした。テーブルのカラム(属性)、外部キーによる関連性、レコードの挿入・削除などは、集合圏モデルではすべて写像で表現されてしまうので、テーブルとドメインの区別は不要、あるいは暗黙に決まるという方針でした。また、データベースの制約は単純な等式(パス同値関係=可換図式)だけしか書けない*8ので、論理式としての記述能力は貧弱でした。

このような状況だと、通常のプログラミング言語が持つキチンとした型システムや論理式による複雑な条件記述との接点がうまく見いだせません。Categorical Informaticsチームの最近(2015年, 2016年)の目標は「データベースと汎用プログラミング言語との統合」らしく*9、型システム(type system)と等式論理(equational logic)を関手データモデルと上手に接合する、という課題を解決しつつあります。その主要な成果は次の2つの論文に述べられています。

  • Title: An Equational Formalism and Design Pattern for Functorial Data Integration (v3: 28 Apr 2016)
  • Authors: Patrick Schultz, Ryan Wisnesky
  • URL: http://arxiv.org/abs/1503.03571
  • Pages: 40 pages
  • Title: Algebraic Databases (10 Feb 2016)
  • Authors: Patrick Schultz, David I. Spivak, Christina Vasilakopoulou, Ryan Wisnesky
  • URL: http://arxiv.org/abs/1602.03501
  • Pages: 80 pages

実装と運用に近い事項がEquational Formalismに述べられていて、その背景となる一般的・抽象的な枠組みのほうはAlgebraic Databasesで展開されています。

プロ関手(profunctor)、フレーム付き双圏(framed bicategory)、二重圏(double category)、カン拡張(Kan extension)などの道具が使われているので理解するのは骨が折れます。とは言え、これらの道具はデータベースやプログラミングと関係しそうだ、と感じていたものではあります。道具を使うのにためらいは不要です。「関手データモデル入門 4:ためらってしまう心を取り除く」より:

道具を使うにあたっての制限や障害はありません。ためらう必要はない、リミッターなしフルスロットルで使ってみればいいのです。

これらの道具立てに関連する過去記事を挙げておきます:

素朴でシンプルだった当初の理論と比べるとだいぶ難しくなってしまいましたが、本質が変わったわけではありません。より精密化されて柔軟な定式化になった、ということです。

FQL/OPLも、これらの理論を背景に設計・実装が進むのでしょう。どんなソフトウェアになっていくのか楽しみです。

*1:関手データモデルのアイデアは20世紀から存在はしていました。しかし、その面目を一新したのはスピヴァックなので、モダンな関手データモデルの創始者はスピヴァックである、と言っていいと思います。

*2:数学 http://arxiv.org/find/math/1/au:+Spivak_D/0/1/0/all/0/1 計算科学 http://arxiv.org/find/cs/1/au:+Spivak_D/0/1/0/all/0/1 応用 http://math.mit.edu/~dspivak/informatics/applied/ などの一覧を眺めれば、スピヴァックの興味の範囲が随分広いことが分かるでしょう。

*3:例えば、http://arxiv.org/abs/1506.03119 はスピヴァックの学生の論文。

*4:Wisneskyの発音を http://ja.forvo.com/ で探してもありませんでした。似た綴の"Wisniewski"が「ウィズネスキー」と書かれるので、このカタカナ表記を採用します。

*5:"Functorial Data Migration" http://arxiv.org/abs/1009.1166 のログを見ると、スピヴァックの関手データモデルに関する最初の投稿は2010年の9月です。しばらく間を置いてソフトウェア開発がスタートしたようです。

*6:チュートリアルはあるのですが、網羅的なリファレンスがないのが困りますね。説明が見つからないキーワードが幾つかあります。

*7:これら複数の言語を比べると、関手データモデルの実装に対する考え方・方針がどのように変遷したかをトレースすることができます。現時点でのマニュアルは、歴史的資料として意味がありそうです。(1)FQL http://categoricaldata.net/fql/tutorial.pdf (2)FQL++ http://categoricaldata.net/fql/tutorial2.pdf (3)OPL http://categoricaldata.net/fql/oplManual.pdf

*8:単純な制約の使い方は「関手的データモデル入門 2:統一的に制約を書く方法」にあります。

*92010年のスライドなどを見ると、2010年の時点で既に、スピヴァックはプログラミング言語の型システムとデータベースの統合を考えていた様子が伺えます。2015, 2016年は「そろそろ本気出そうぜ」の年といえるでしょう。

トラックバック - http://d.hatena.ne.jp/m-hiyama/20160510

2016-04-28 (木)

律子からカタストロフへ

| 10:20 | 律子からカタストロフへを含むブックマーク

最近、「律子」という言葉を使っています。「りつこ」じゃなくて「りつし」です。英語の接尾辞「-or」の訳語のつもり。

では、その「-or」とは何でしょうか。


M = (M, ×, i) をモノイドとすると、次の法則が成立します。

  • (a×b)×c = a×(b×c) ― 結合律(associative law, associativity)
  • i×a = a ― 左単位律(left unit law, left uniticity)
  • a×i = a ― 右単位律(right unit law, right uniticity)

モノイド圏になると、法則はイコールではなくて同型になります。

  • (A¥otimesB)¥otimesC ¥stackrel{¥sim}{=} A¥otimes(B¥otimesC)
  • I¥otimesA ¥stackrel{¥sim}{=} A
  • A¥otimesI ¥stackrel{¥sim}{=} A

これらの同型を与える射(可逆射)を、それぞれ、associator, left unitor, right unitor と呼びます。

モノイド圏の交替律(interchange law)は次のような等式です(下に図があります、図の方向は下から上、左から右)。

  • (A¥otimesg);(f¥otimesB') = (f¥otimesB);(A'¥otimesg)

これを同型にすると:

  • (A¥otimesg);(f¥otimesB') ¥stackrel{¥sim}{=} (f¥otimesB);(A'¥otimesg)

この同型を与える射は interchangor と呼ばれることがあります。interchanger じゃなくて、語尾は「-or」です。バートレット(Bruce Bartlett)の論文から引用しましょう。語尾の「-or」に注目。

「-or」接尾辞の使い方はもう分かったと思います。

  • 結合律を与える同型射 associator
  • 単位律を与える同型射 unitor
  • 交替律を与える同型射 interchangor

ナントカ律を与える同型射を“ナントカ-or”と呼ぶわけです。これを日本語で、ナントカ律子としたらいいだろう、と思うわけです。associator=結合律子、unitor=単位律子、interchangor=交替律子 ですね。


ところで、同型で与えられる法則の左辺・右辺の項をストリング図で表すと、同型は2次元図形の変形になるので、その軌跡は3次元の図形になります。結合律を表す結合律子(associator)の形状は次の動画で確認できます。

D

これ、いまいち分かりにくい気もします。手前味噌ですが、僕が昔描いた立体図がけっこう分かりやすいかと。

双対/随伴を表すニョロニョロ法則を表す図形の動画もあります。

D

動画タイトルは、zigzagirator。ニョロニョロ法則はジグザグ法則とも呼びますからね。もちろん、snakerator(ニョロニョロ律子)と呼ぶ人もいます。

次の図もニョロニョロ律子と同じ形ですよね。

この絵は、ルネ・トムのカタストロフ理論を象徴する図形のひとつです。高次圏の法則に対する律子は、典型的な特異点パターンに対応するようなので、ニョロニョロ律子がカスプ特異点を持ち上げた形状なのはその一例なんでしょう、よく知らんけど。

トラックバック - http://d.hatena.ne.jp/m-hiyama/20160428

2016-04-27 (水)

関手と自然変換の計算に出てくる演算子記号とか

| 15:53 | 関手と自然変換の計算に出てくる演算子記号とかを含むブックマーク

モノイド自然変換としての積分: 大雑把に」にて:

勘違いや抜けがあるかも知れませんが、修正をしても大筋はたぶん維持できるでしょう。もう少し確認して、“時間と気力があるときに”具体的な構成法を書くつもりです。

この件は書くつもりでいますが、一度には無理ですね。何度かに分けて書きます。

積分がモノイド自然変換である」ことを示すには、積分の計算と圏論の計算をします。圏論側では、主に関手と自然変換の計算をします。この計算過程を書き記すのがけっこう負担。これといって標準的な書き方が決まってないし、メジャーな記法にも問題があります。ほんとに酷い記法も横行してます。

てな事情で愚痴が入りますが、後で参照したいこともある(主にココ)ので、関手と自然変換の記法に関してまとめておきます。

内容:

  1. 酷い話
  2. 演算子記号
  3. 演算子記号選択の事例
  4. DOTN二号とストリング図
  5. 今後使う予定の演算子記号

酷い話

https://en.wikipedia.org/wiki/Natural_transformation では、自然変換の縦結合と横結合について、次のような記法を採用しています。

  • 縦結合: η : F → G と ε : G → H に対して、その縦結合は εη : F → H
  • 横結合: η : F → G : C → D と ε : J → K : D → E に対して、その横結合は ηε : JF → KG

関手も自然変換も同じ形の矢印、縦結合も横結合も単なる併置(並べるだけ)なんですよ。しかも、縦結合は反図式順(右から左)で、横結合は図式順(左から右)です。単なる間違い(誤記)なのか、それとも意図的なのか判断できません。同じ定義をPDFにしたものがあったので、下に引用します。こっちでは、横結合も反図式順です。

縦結合:

横結合:

いったいナニ考えてんでしょうか? 縦結合も横結合も併置で書いて、計算出来るわきゃないでしょ! 計算の都合をまったく、あるいはほとんど考えてない定義や説明が多すぎますよ。

酷い話だなー。

演算子記号

僕が自分で計算するときは、DOTN二号記法とストリング図ストライプ図を使っています。DOTN二号記法はマイナー(つうか自己流)なので、コミュニケーションの都合からメジャー(比較的に多数派)の書き方に直すのですが、わざわざ分かりにくい書き方にするのがけっこうなストレス。歴史的経緯があるからしょうがない、とは思いますが、辛いなー。

ともかくも現実の多数派は反図式順(右から左)記法です。反図式順で使われる演算子記号は次のようなモノでしょう。他にも記法がありますが、目ぼしいところはこんなもんかと。

演算 演算子記号
射の結合 ¥circ, 併置 g¥circf, gf
関手の適用 丸括弧, 併置 F(A), FA
関手の結合 ¥circ, 併置 G¥circF, GF
自然変換の適用 下付き添字, 併置 αA, αA
自然変換の縦結合 ¥circ, ・, 併置 β¥circα, β・α, βα
自然変換の横結合 ¥circ, ・, *, 併置 β¥circα, β・α, β*α, βα

どういう組み合わせを採用するかは人により場合により様々です。何を併置(演算子記号なしで並べるだけ)にするかもバラバラ。何の断りもなしに併置を多用されると、ほんとに解読に苦労します。

徹底して反図式順なら、それはそれで一貫性がありますが、反図式順(右から左)と図式順(左から右)が混ざることも多く、バイダイレクショナルな表記になります。添字の上下も入れるとテトラダイレクショナルかな。

僕はマルチダイレクショナルな記述をほどいて読む能力がとても劣っているので、もうやんなっちゃいますよ(ため息)。目線の細かい移動によるスキャンと脳内での再構成が人並みには出来ないみたいです。

演算子記号選択の事例

これが標準だという記号セットはないのですが、マックレーンのThe Bookでは次のようです。

演算 演算子記号
射の結合 ¥circ
関手の適用 丸括弧, 併置
関手の結合 ¥circ, 併置
自然変換の適用 下付き添字, 併置
自然変換の縦結合
関手と自然変換のヒゲ結合 ¥circ, 併置
自然変換の横結合 ¥circ

前節の表と比べると「関手と自然変換のヒゲ結合」が増えています。これは、関手と自然変換との横結合のことで、whiskering と言います。関手Fの恒等自然変換をιFとすると、α¥circF = α¥circιF のように、ヒゲ結合は自然変換の横結合で表せます。逆に、ヒゲ結合を使って自然変換の横結合を定義することが出来ます。

ヒゲ結合と(自然変換どうしの)横結合は、たいてい区別しないようです。しかし、Globularでは、ヒゲ結合は出来ても横結合が出来ない*1ので、両者の違いをシッカリ把握してないと混乱します。

マックレーンの記号セットを使って、関手/自然変換の計算でよく使う公式を書いてみます。最後の公式で、α::F⇒F':CD、β::G⇒G':DE です。

  1. (G ¥circF)(A) = G(F(A)) ― 関手の結合と適用
  2. (F¥circα)A = F(αA) ― ヒゲ結合と適用 1
  3. ¥circF)A = αF(A) ― ヒゲ結合と適用 2
  4. (β・α)A = αA¥circβA ― 縦結合と適用と射の結合
  5. β¥circα = (β¥circF')・(G¥circα) = (G'¥circα)・(β¥circF) ― 横結合とヒゲ結合と縦結合

DOTN二号とストリング図

前節の公式をDOTN二号記法で書くと:

  1. A.(F*G) = A.F.G
  2. A.(F*α) = A.F.α
  3. A.(α*F) = A.α.F
  4. A.(α;β) = (A.α);(A.β)
  5. α*β = (α*G);(F'*β) = (F*β);(α*G')

これは図式順であり、次のような演算子記号を使っています。

演算 図式順演算子記号
射の結合 ;
関手の適用 .
関手の結合 *
自然変換の適用 .
自然変換の縦結合 ;
関手と自然変換のヒゲ結合 *
自然変換の横結合 *

格上げ(すぐ下で説明)を使えば、「.」と「*」を区別する必要もなくなるので、同じことを「*」だけ使って書けます。ストリング図との対応も直接的で自然です。

  1. A*(F*G) = (A*F)*G
  2. A*(F*α) = (A*F)*α
  3. A*(α*F) = (A*α)*F
  4. A*(α;β) = (A*α);(A*β)
  5. α*β = (α*G);(F'*β) = (F*β);(α*G')

格上げとは、「対象→関手」「射→自然変換」とみなす方法で、絵算では非常に重要なテクニックです。次の記事で説明しています。

横結合の「*」を省略して併置にすれば、次のように簡略に書けます。

  1. A(FG) = (AF)G
  2. A(Fα) = (AF)α
  3. A(αF) = (Aα)F
  4. A(α;β) = (Aα);(Aβ)
  5. αβ = (αG);(F'β) = (Fβ);(αG')

こうすると、必要な演算子記号は「;」だけになります。ストリング図を併用すれば、混乱もほとんどありません。

今後使う予定の演算子記号

単に趣味の問題だけではなくて、例えば反対圏の計算をするときには二種類の記号セットがあったほうが便利なので、反図式順記法を併用することに吝〈やぶさ〉かではありません。

しかし、多数派の記法で困るのは、「似ている概念に違う記号、異なる概念に同じ記号」になっている点です。計算で使う演算は、縦方向の演算と横方向の演算に大別されます。

  • 縦方向の演算:射の結合、自然変換の縦結合
  • 横方向の演算:関手の結合、関手と自然変換のヒゲ結合、自然変換の横結合

マックレーンの記法だと:

  • 縦方向の演算:射の結合 ¥circ、自然変換の縦結合 ・
  • 横方向の演算:関手の結合 ¥circ、関手と自然変換のヒゲ結合 ¥circ、自然変換の横結合¥circ

と、縦方向の演算に白丸と黒丸が混じっています。このため、「白丸は省略可能だが黒丸は省略しない」という単純なルールを採用できません。ストリング図との併用では、これはけっこうな負担になります。

縦方向と横方向の演算子記号が揃ってないのは辛いので、射の結合も黒丸に変更します。その他はマックレーンと同じです。こうすると、黒丸は省略不可能だが、白丸や括弧は適宜省略してよい、というルールが使えます。自然変換の成分をとる下付き添字も、書きにくいときは併置でも大丈夫です。

演算 反図式順演算子記号 図式順演算子記号
射の結合 ;
関手の適用 丸括弧 .
関手の結合 ¥circ *
自然変換の適用 下付き添字 .
自然変換の縦結合 ;
関手と自然変換のヒゲ結合 ¥circ *
自然変換の横結合 ¥circ *

世間に逆らっているようですが、現状のメジャーな記法はホント計算に向いてません。また残念ながら、反図式順と図式順を混ぜざるを得ないことはあります。例えば、左加群圏では反図式順、右加群圏では図式順を使うと計算が楽です。射が集合圏の写像だったりすると、g・f という書き方をゴリ押しするのは困難で、g¥circf と書くしかないこともあるでしょう。さらには、特定の状況下では、これ以外の記法を使うこともあります。

*1:Globularの計算モデルは、関手と自然変換からなる2-圏ではなくて、3次元の圏なので、ストリクトな横結合は定義できないのです。

トラックバック - http://d.hatena.ne.jp/m-hiyama/20160427

2016-04-25 (月)

モノイド自然変換としての積分: 大雑把に

| 09:59 | モノイド自然変換としての積分: 大雑把にを含むブックマーク

モノイド圏上の加群圏の実例」にて:

僕が加群圏にちょっと興味をいだいたのは、変則的なラムダ計算のモデルとして加群圏が使えないかな? と思ったからです。思っただけで、よく分かってません。

これ、分かりました。

何が分かったのか? を大雑把に記します。どうしてそうなるのか? を説明する余裕が今日はないので、それは時間と気力があるときに書くつもりです。

内容:

  1. 積分の計算がモナドみたいだった理由
  2. 積分計算
  3. コンパクト測度空間
  4. 割とうまく出来てる

積分の計算がモナドみたいだった理由

ことの発端は、次の記事に書いてあります。

ラムダ計算に積分を入れてみた、あるいは積分計算にラムダ抽象を入れてみたわけです。ちょっと計算してみると、状況がなんかモナドやモナド変換と似ているんですよ。モナドの香りがするぞ! けど、モナドそのものではないのです。モナド/モノイドに似たナニカがありそうです。なんだろう? モヤモヤ、ヤキモキ。

それで、加群圏(module category)が何か関係するのではないか、と当たりを付けました。加群圏に関しては、次の記事にかなり詳しく書きました。

結果的に、山勘が当たったようです。つまり、積分を入れたラムダ計算、あるいはラムダ抽象を持つ積分計算の構造のなかに加群圏が現れます。上記の記事で述べたように、「加群圏=自己関手圏へのモノイド関手」なので、自己関手圏へのモノイド関手が現れる、と言っても同じことです。

さらに、積分という操作がモノイド関手のあいだのモノイド自然変換(monoidal natural transformation)になります。モノイド関手やモノイド自然変換は、モナド/モノイドと類似した構造を持ちます。積分がモノイド自然変換なら、モナドの香りがするのは納得がいくことです。これで、僕のモヤモヤ感は晴れたのでした。

積分計算

積分はモノイド自然変換である」という主張をちゃんと示すには、圏論の道具立てが必要です。それだけではなくて、要所要所で具体的な構成が必要で、そのときは積分計算をします。この節では、圏論側ではなくて、積分側の議論で使う公式をひとつだけ紹介します。

僕が最初に気付いたことは、ラムダ抽象を含んだ次の等式が成立しそう(成立すべき)だ、ということです。

 ¥lambda x. ¥int_{y¥in Y} f(x, y) dy = ¥int_{y¥in Y} ¥lambda x. f(x, y)¥, dy

等号の左右は関数です。「関数の等しさ」は難しいので、on the noseで等しいか?と聞かれると自信がありません。もう一度積分して、積分値の等しさにすると、よりハッキリします。

 ¥int_{x¥in X}¥biggl( ¥lambda x. ¥int_{y¥in Y} f(x, y) dy¥biggr)(x)¥, dx = ¥int_{x¥in X}¥biggl(¥int_{y¥in Y} ¥lambda x. f(x, y)¥, dy¥biggr)(x)¥, dx

等号の左右の表現(字面)は似てますが、その意味はまったく違います。特に右辺の解釈はけっこう難しいですよ。

右辺の被積分関数は y |→ λx.f(x, y) ですが、ラムダ記号は2変数関数のカリー化を表すので、yに対する値は、1変数(xの)関数です。つまり、y |→ λx.f(x, y) は関数空間に値を取る関数なのです。適当な仮定のもとで、関数空間をバナッハ空間に仕立てられるので、y |→ λx.f(x, y) はY上のバナッハ空間値関数となります。

そうなると、右辺の内側にあるyに沿った積分ボッホナー積分(Bochner integral)を使うことになり、その積分結果はバナッハ空間のベクトルです。そのベクトルを、もう一度X上の関数として再解釈した上で外側の積分を実行します。

左辺はそんなには難しくありません。2変数関数のy方向への積分値達をxの関数とみなして、それをxに沿って積分した値です。これは、通常、λx. なしで簡略に書かれる積分の繰り返し(反復積分、逐次積分)です。

式の見た目では、ラムダ記号の位置がズレているだけなので簡単そうに見えますが、決して自明ではありません。ボッホナー積分なしで等式を示すのは難しそうです。ボッホナー積分については、Wikipedia項目を読んでもおおよそ分かりますが、より詳しい(でも長くない)解説は:

コンパクト測度空間

積分を入れたラムダ計算 状況編」では、関数の定義域としてコンパクト領域を使っています。コンパクト領域とは、標準ユークリッド空間Rnのコンパクト部分集合に多少の条件を付けたものです。

コンパクト領域は具体性があって良いだろうと思ったのですが、どうも具合が悪い。Rnに入っていることが邪魔になるので、コンパクト領域(Rnの部分集合)を使うのはやめます。代わりにコンパクト測度空間を関数の定義域にします。

コンパクト測度空間とは次のようなものです; Xは位相空間で、位相空間としてコンパクト・ハウスドルフとします。Xには、位相から定まるボレル集合族(σ代数)が付いています。なので、Xには唯で可測構造が入ります。この可測構造に対する有界測度をひとつ決めてμとします。(X, μ) がコンパクト測度空間です。

コンパクト測度空間 (X, μ) は、単なる測度空間ではなくて、下部構造として位相を持っているので、Xから/Xへの連続写像/連続関数を考えることができます。Rnに含まれるという制約もないので、使い勝手がいいように思います。

(X, μ), (Y, ν) がコンパクト測度空間だとして、φ:X→Y を連続写像とします。φは可測写像になります。φによりX上の測度μを前送りできるので、それを φ*(μ) と書きます(像測度)。φ*(μ) = ν であるようなφをコンパクト測度空間のあいだの準同型写像だと定義します。

すると、コンパクト測度空間と準同型写像の全体は圏となります。さらに、直積空間と直積測度によりモノイド積を定義すれば、モノイド圏を構成することができます。モノイド積の構成に直積を使ってますが、圏論的直積が出来上がるわけではありませんから注意してください。このモノイド圏を CompMS = (CompMS, ¥otimes, 1) とします。1 は単元集合上に構成した自明な測度空間です。

積分を入れたラムダ計算 状況編」と同じ議論で、モノイド圏CompMSはバナッハ空間の圏Banに作用します。つまり、バナッハ空間の圏BanCompMS上の加群の構造を持ちます。「モノイド圏と加群圏に関するフォークロアとマックレーン五角形・三角形」で述べた無名の定理により、加群圏は CompMS→End(Ban) というモノイド関手を定義します。

割とうまく出来てる

前節で概略を示したモノイド関手 CompMS→End(Ban) を構成し、その他いくつかの準備をすると、積分をモノイド自然変換として解釈できます。積分計算の公式(例えばフビニの定理)が、モノイド関手/モノイド自然変換の条件とうまいこと対応するので、「積分はモノイド自然変換である」は本当っぽい気がします。

勘違いや抜けがあるかも知れませんが、修正をしても大筋はたぶん維持できるでしょう。もう少し確認して、“時間と気力があるときに”具体的な構成法を書くつもりです。

2016-04-19 (火)

花? 葉?

| 10:33 | 花? 葉?を含むブックマーク

アレックスの花が今でも咲いています。しかし、花にしては寿命が長すぎる気がするし、緑色になってきてるんだけど。これって実は葉っぱ?

内海内海 2016/04/19 17:52 幾つかのサイトで確認したら、 どうもその葉っぱに見える部分は 「仏炎苞 (ぶつえんほう)」 という蕾を包んでた部分の様ですね (^_^;) ピースリリーの場合その真ん中にあるブツブツのある部分が本来の花らしいです。

m-hiyamam-hiyama 2016/04/19 19:19 内海さん、
ということは、花でもなく葉でもなく、なんだかありがたいお名前の部位なのですか。
これはサトイモ科の特徴なんですね、ありがとうございます。

トラックバック - http://d.hatena.ne.jp/m-hiyama/20160419

2016-04-18 (月)

間違っている? いやあってる

| 15:10 | 間違っている? いやあってるを含むブックマーク

試供品でもらったシャンプー&リンスだけど、間違っているよね、これ。

と思ったんですが、こういうデザインのようです(コメント欄参照)。

SaitoAtsushiSaitoAtsushi 2016/04/18 17:00 コンディショナーが逆向きなのは試供品でないやつでもそうなってますよ。 (ポンプタイプは例外)
合理的だと思います。 形状も表示の意匠も統一していながら区別しやすいです。 表示の向き通りに置いておけば、使うときには眼鏡を外していても、あるいは泡が目に入りそうで目を閉じてしまっていても間違った方を取り難いんじゃないでしょうか。
理想的には、形状をちょっと変えて欲しいですが。

m-hiyamam-hiyama 2016/04/18 17:23 SaitoAtsushiさん、
えっ、これでいいんですか。言われてみれば、なるほど、判別しやすいですね。
タイトル変更しておきます。ありがとうございます。

トラックバック - http://d.hatena.ne.jp/m-hiyama/20160418

2016-04-15 (金)

C/C++のとんだ落とし穴(ハマっちまったよ)

| 09:31 | C/C++のとんだ落とし穴(ハマっちまったよ)を含むブックマーク

とあるC++コードがコンパイルエラーするんですが、原因がまったく分からなかったんですよ。「そんなバカな?!」という感じ。しばらくハマってしまいましたよ。

結局、C++でもCでも同じことが起きることが分かりました。次は、僕が遭遇したのと同じ現象が起きるC言語ソースコードです。

// -*- coding: sjis -*-
// strange.c

struct ThreeNums {
    int x; // 負の数も指定可能
    int y;
    int z;
};

int total(struct ThreeNums nums)
{
    return nums.x + nums.y + nums.z;
}

コンパイルすると:

$ type tdm-gcc
tdm-gcc is aliased to `/c/Installed/TDM-GCC-64/bin/gcc.exe'

$ tdm-gcc --version
gcc.exe (tdm64-1) 5.1.0
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ tdm-gcc -c strange.c
strange.c: In function 'total':
strange.c:12:25: error: 'struct ThreeNums' has no member named 'y'
     return nums.x + nums.y + nums.z;
                         ^

ソースコードを目視している限り、いくら見てもバグは見当たりません。

実は、(しばらく…が続く)

コメントのせいです。コメントを取ると正常にコンパイルできます。

それにしても「なんでだよー?」と思うでしょ(僕は思いました)。僕の環境・状況が少し特殊だったわけですが、次の3つの事情が絡んでいます。

  1. ソースコードのエンコーディングがShift_JISであった。
  2. C/C++の単一行コメントでは行継続が可能である。
  3. GCCは、Shift_JISで書かれたソースコードを考慮してない。

コメントに書かれている文言の最後の文字「能」ですが、これはShift_JISのヤバイ文字のひとつです。つっても、“最近のわけーもん”は知らないかな? エンコーディングがShift_JISであることを考慮しないと、色々とトラブルを起こす文字です。次のページに危険な文字が一覧されています。

「能」は、バイト列としては 0x94, 0x5cで、2バイト目がバックスラッシュ(または半角円マーク)のグリフを持つ文字と同じです。GCCは、Shif_JISに対して特別な配慮はしないようで、コメントの末尾にバックスラッシュがあると認識します。

そして、なんとですね、C/C++の単一行コメントは、末尾のバックスラッシュによって行継続が出来るんですよ。って、知らねーよ!そんなこと。誰が使うんだよ、そんな機能。トライグラフ知ってたけど、知らんかったわ*1

この変な仕様により、次のソースコードは問題なくコンパイルできます。

// comment line 1 \
comment line 2 \
commant line 3
int main() {return 0;}

単一行コメントの行継続処理はプリプロセッサで行われるので、コンパイラの「-E オプション」で確認できます。

$ tdm-gcc -E strange.c
# 1 "strange.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "strange.c"



struct ThreeNums {
    int x;

    int z;
};

int total(struct ThreeNums nums)
{
    return nums.x + nums.y + nums.z;
}

$ tdm-gcc -E strange2.c
# 1 "strange2.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "strange2.c"



int main() {return 0;}

MicrosoftのVisual C++Shift_JISエンコーディングを考慮しているので(考慮してなかったら怒るよ)、この問題は起きません。また、単一行コメントの行継続には警告をしてくれます。

Windowsだと、次のように書くことはありそうだから、黙っているのはマズイもんね。

char *tmpFileName; // ディレクトリは C:\tmp\
tmpFileName = "001.txt";

GCCだとtmpFileNameは初期化されないままです(恐ろしい)。

今回のケースは不幸な事情が重なった感じですけど、「すごく珍しい」ってほどではない気がします。ご用心ください。


[追記 date="翌日"]

コンパイラShift_JISソースコードをそれと認識してないと、コメント以上に文字列リテラルが問題になりますよね*2GCCソースコードと実行時のエンコーディングを指定できる、という噂を聞きました。だとすると、次のソースコードは正常にコンパイル・実行できるはずです。

// -*- coding: sjis -*-
// a.c
#include <stdio.h>

int main() {
    printf("噂では表示可能。\n");
    return 0;
}

では、噂のおまじない付きでコンパイル・実行。

$ tdm-gcc --input-charset=cp932 --exec-charset=cp932 a.c

$ ./a.exe
噂では表示可能。

おおー、出来た。ちなみにオプションを付けないと:

$ tdm-gcc a.c
a.c: In function 'main':
a.c:6:12: warning: unknown escape sequence: '\202'
     printf("噂では表示可能。\n");
            ^
a.c:6:12: warning: unknown escape sequence: '\216'
a.c:6:12: warning: unknown escape sequence: '\201'

$ ./a.exe
奄ナは侮ヲ可煤B

0x5c(「\」と解釈される)の直後がエスケープシーケンスとして不正な文字になるんで警告は出ています。結果はご覧のとおりの文字化け。

Shift_JISソースコードを使わざるを得ないときは、--input-charset, --exec-charsetオプションを付ければいいですね。だけど、このオプション、helpに出ないんだよな: tdm-gcc --help | grep -i charset → 何もなし 。

[/追記]

*1[追記]#defineのときに使えるのは知っていたし、例えば http://d.hatena.ne.jp/m-hiyama/20160227/1456547986 のサンプルで実際に使っています。しかし、コメント内でも有効だとは思わなかった、ってことです。ほんとに「誰得?」[/追記]

*2:今僕がいじっているソースコードでは文字列リテラルを使ってないので、文字列リテラルの問題は生じません。