2016年10月5日水曜日

ポケモンGOのポッポマラソンに必要なアメ数を計算する。

最近ブームが去りつつあるような気もするポケモンGOですが、私はいまだに楽しく遊んでいます。
しかし、トレーナーレベルが20以降になるとレベルアップに必要な経験値が非常に大きく、なかなか伸び悩んできました。


というわけで、ポケモンGOでトレーナーレベルを上げるには、効率の良い経験値稼ぎが必要です。そこで行われる方法がポッポマラソンなわけですね。
ポッポ、ビートル、キャタピーは比較的入手性がよく、また進化に必要なアメ数がとても少ないです。進化で得られる経験値は500EXPと非常に高く、また、その経験値を入手するタイミングもコントロールできるため、しあわせタマゴで経験値を倍増させている30分間に集中して進化させ、経験値を荒稼ぎするのがいわゆる「ポッポマラソン」なわけです。

しかし、しあわせタマゴは貴重なアイテムであるばかりか30分という制限時間まであり、なおかつ進化にはアニメーションが付きまとうため、最大限手際よく30分間ポケモンを進化させ続ける必要が出てきます。


アニメーションの時間はおおよそ25秒程度、操作をするのに必要な時間を加味して、30分間ではおおよそ60~70匹程度のポケモンを進化させられることができます。すなわち、70匹程度ポケモンが進化させられる状態にしたうえでポッポマラソンを開始させなければなりません。しかし、進化やポケモンを博士に送る(以下、『D進』と呼ぶこととする)ことでさらにアメを得られるため、それに必要なアメの数はそう簡単に暗算できるほどのものでもありません。もちろん進化させるのはポッポだけでないでしょうから、今、自分の手持ちの中で進化できるポケモンの数を管理するのは頭の中だけでできるようなものでもありません。

というわけで、ポッポマラソンに必要なアメ数を計算し、管理する必要が出てきます。

アメは、ポケモンを進化させることで1つ、ポケモンをD進させることで1つ手に入れることができます。すなわち、ポッポマラソン中にもどんどんアメが増えていくわけです。そのことを、Excelを使って管理してみましょう。


まず立式ですが、現在持っているアメの数を$c$、進化に必要なアメの数を$c_e$とします。進化させながらだと進化させた直後に1個アメが手に入りますから、実質進化に必要なアメの数は$c_e-1$個になります。というわけで、進化可能数$e$は
\[e=\left\lfloor\frac{c}{c_e-1}\right\rfloor\]
となります。$\lfloor\rfloor$は床関数ですね。与えた数字以下の最大の整数を返す関数です。


しかし、この式では不十分です。
例えば11個アメを持っていた場合、この式では進化可能数が1になってしまいます。そうです。「実質進化に必要なアメ数」と言いましたが、進化で得るアメは、その進化では使えないですね。 その分の項が入っていませんでした。
これはどう入れるかというと、「最後の進化で得るアメによる進化可能数の上昇」を差し引いてやればいいわけです。最後の進化で得るアメによる進化可能数の上昇というとややこしいかもしれませんが、すなわち$1/c_e$です。すなわち、完成した式は
\[e=\left\lfloor\frac{c}{c_e-1}-\frac{1}{c_e}\right\rfloor\]
と書くことができます。

ただ~し、この式にはまだ1つ問題があります。$c=0$のときに、$e$がマイナスになってしまうんですね。例えば$c_e=12$とすると$e=\lfloor-0.08333...\rfloor=-1$となるわけです。ですので、まあせこいですけど絶対値を取ってから床関数にでも与えれば大丈夫でしょう。床関数の中身が負になる場合はあっても、それが-1以下になることは無いですから。
\[e=\left\lfloor\left|\frac{c}{c_e-1}-\frac{1}{c_e}\right|\right\rfloor\]
ちなみに、Windowsの多くの処理系の小数型から整数型への変換やExcelのROUNDDOWN関数では0方向への丸めとなります。すなわち、正の数に対しては床関数になりますが、負の数に対しては天井関数となるわけです。なので、そのようなもので処理する場合は、このように絶対値をわざわざとる必要はありません。


さて、この式の何がいいかというと、進化直後にD進するときの式にも簡単に転用できるからです。
進化直後にそのポケモンをD進させた場合は、進化とD進で2個アメが手に入りますから、実質進化に必要なアメの数は$c_e-2$個になります。しかし、それでは進化やD進で発生したアメをそのポケモンの進化に使えてしまうことになるので、その分を差し引いておく必要があります。まあ、ついでに負の数になっちゃう問題も込々で式を立てると
\[e_{D進}=\left\lfloor\left|\frac{c}{c_e-2}-\frac{2}{c_e}\right|\right\rfloor\]
となりますね。


「進化させたり、それをD進させたりして、それで生まれるアメをまた再び進化に使う」という考えは手続き的で、このように1つの数式で進化可能数を求めることはできません。どちらかと言えばプログラムでループを回すようなイメージになってしまいます。
ですが、このような「実質的に進化に必要なアメ数」という考え方と、その考えで起こる問題に対する修正項を加えるという考え方で1つの式にすることができ、簡単にExcel等で計算できるようになりました。


あと少しで70匹になるぞ…。

2016年9月22日木曜日

Google Driveのホスティングサービス終了によるSyntaxHighlighter周りの修正

3か月くらい前にSyntax Highlighterが正常に動作しなくなっていたため、その修正をした旨の記事を書きました。

そして、昨日久々に記事を書いて、いくつか自分の過去記事を巡回していたら

ま た 動 か な く な っ て る じ ゃ ね え か

ちきしょーめ、なんかウェブページを開くとダイアログが出てきてソースコードが正常に表示されねえぞ!
こういうことで時間を取られるのは本当に面倒ですが、正常に動作しないブログはいろいろと問題ですので原因の究明と対応が必要になってくるわけです。

いろいろ調べていると、どうもGoogle Drive側に問題があるようです。
Google ドライブでウェブページをホストする
ああ、なんということでしょう。3か月前にドヤ顔で「Bloggerがhttps以外を締め出したからSyntaxHighlighter作った人のホスティングサービスが使えなくなった!だからGoogle Driveに置くことで解決してやったぜ!」って言っていたのに、その2か月後にそのサービスが終了してしまうとは…。どうもホスティングサービスの終了は結構前から予告されていたようで、それに気づかなかった私が情弱だったようですね…。

代替サービスをいろいろ調べましたが、どうもDropboxやOneDriveなどの他のクラウドストレージサービスのホスティングサービスはイマイチのようですね。うーん…。

というわけで行きついたのがFirebaseでした。
ウェブ関係にはめっきり弱い私ですから、明らかに私の手に余りそうなサービスですが、とりあえずホスティングに関してまとまった記事がちらほら見当たりましたので、それに従って挑戦してみることにしました。

というわけで、10分くらいいろいろ戦いながらやってみましたが、比較的簡単にできました。
詳細はググっていただくとして、Node.jsをインストールしてからnpmでFirebaseのクライアントをインストールし、ローカルの適当なフォルダをFirebaseのフォルダに指定した後でpublicフォルダにSyntaxHighlighter周りのファイルを入れてデプロイすれば、そのフォルダがインターネット上に公開されるようになります。そして、FirebaseのサイトからホスティングのURLを確認し、BloggerのテンプレートのURLをGoogle Driveからこちらに変更してやれば完了です。めでたくコードがハイライト表示されるようになりましたとさ。言うまでもなく、Firebaseはhttpsに対応しております。


ふぅ。また3か月後に、今度はFirebaseのサービス終了みたいなお知らせ見たら私は泣くぞ…。

2016年9月21日水曜日

RailwayMap 0.1.0

さて、ついに公開にこぎつけました。

RailwayMap ver.0.1.0

まだ粗削りですが、一応バージョン0代として公開してもいいかなということで、公開します。

鉄道地図を描きたいことって無いですか?ありますよね?
ですが、なかなかいいソフトが無いです。白地図ソフトみたいなものや、Yahoo地図のように、「鉄道路線の線が引かれただけの地図」ならありますが、任意の区間を任意の色で塗って、などといったことはなかなかできません。
例えば、地元の路線地図を描きたい、各社の鉄道ネットワークがどのようになっているのか見てみたい、乗りつぶしマップを描きたい、鉄道路線の研究をしたのでカテゴリごとに塗ってみたいなど、いろいろな鉄道地図を描きたい理由があると思います。

そんな(主に自分の)ニーズに応えるのがこのRailwayMapです。


任意の区間を任意の色、太さ、大きさで描くことができます。
路線データは国土地理院の国土数値情報からユーザー自身でダウンロードしてきてください。

国土数値情報 鉄道データ

現状最新版が2015年版のようですが、2015年版は正常に読み込めません。2014年版とはデータ形式が変わったようです。気が向いたら対応します。2014年版を使ってください。
このデータは、日本の鉄道・軌道全路線が網羅されておりますので、JR在来線、新幹線から路面電車、モノレール、ケーブルカー、トロリーバス、ガイドウェイバスまで全部描画することができます。
メニューのファイルからデータを読み込めば、私の環境ならば15秒くらいで右側にツリー構造が現れます。そして、チェックボックスをいじったり、色や太さなどをいじれば好きなように地図が描けるというわけです。

細かな注意点はZIP内のReadMeを読んでもらうとして、とりあえずQ&Aだけこちらに並べておきます。

Q1.このソフト重いんじゃない?よく固まるぞ!
A1.何十万もある点をプロットするので仕方ありません。諦めてすごいCPUやつよいGPU、でかいメモリを買ってください。それでもだめなら諦めてください。(タスクマネージャー等でこのソフトのCPU使用率が1コア分より低いにも関わらず固まる場合はバグの可能性があります)
Q2.地図の形は変えられないの?
A2.線の太さや縮尺は変えられますが、形はメルカトル図法での出力になります。モルワイデ図法や正距方位図法、ボンヌ図法などでの出力はできません。
Q3.海岸線や緯線経線、縮尺などは描き出せないの?
A3.描き出せません。鉄道路線だけです。現状、実装の優先度はかなり低いです。
Q4.地図は画像ファイルに保存できないの?
A4.できません。できたら実装したいなって思っています。今のうちはPrintScreenしてください。
Q5.一生懸命設定した路線の色や表示/非表示の設定は保存できないの?
A5.できません。できたら実装したいなって思っています。
Q6.Raiload Linesの一番末端のノードの意味がわからない。どのノードがどの区間なのよ。
A6.XMLファイルに何駅と何駅の間のデータか書いてないので、仕方なくデータのIDの順序で並べています。そのうち手は入れたいとは思っていますが、見込みは立っておりません。
Q7.駅名が辞書順で並んでるのはクソ。駅順に並び替えてくれ。
A7.僕もクソだと思いますが、駅順はXMLファイルに含まれていないのでとりあえず今のところこれで我慢してください。
Q8.同じ駅が同じ路線に重複して存在している。例えば東海道新幹線には三島駅と新大阪駅が2つある。バグでは?
A8.バグではありません。元のXMLにも2つあります。理由はよくわかりません。

てなわけで、よろしくお願いします。
現在開発段階の途中で公開したような形なので、そのうちいろいろなところを仕上げていくつもりです。モチベーションが保てるかどうかはわかりませんが。

2016年8月19日金曜日

WPFでPolyPolyline

GDIにはPolyPolylineという関数があります。Polylineは節点を指定して折れ線を描く関数ですが、PolyPolylineはその折れ線を複数同時に描くことができます。Polylineを複数回呼び出すより効率がよく、また、「Polylineを複数回呼び出す」という制御すら必要なくなるので非常に重宝した関数でした。

私みたいな老害は、とりあえずこういうクラシカルな関数が新しい描画システムに無かったら怒るわけです。「なんでWPFにPolyPolylineは無いんだ!」と。(Polylineはあります)

特に、WPFはGDIを使ったクラシカルなアプリとは違い、UIとロジックの分離が基本になります。あまり細かく言うと怖い人達にマサカリを投げられそうなのでやめておきますが、ロジック部が作りだした接点の配列を使って複数の線を動的に描画したいとき、XAMLでは動的にPolylineを複数回呼び出すことができず困ってしまいます。PointCollectionの配列をバインディングしたら複数の線を描画してくれるようなPolyPolylineが欲しい!!!!1!!!


しばらく悩んでいましたが、ふと思いつきました。

「ListViewでItemTemplateを使うと複数のアイテムの描画をかなり強力にカスタマイズできるよな…?ならば、そのシステムで複数の線を描画させればいいのでは?」

ListViewには、その大元となる親クラスとしてItemsControlというものがあります。
これでPointCollectionの配列を受け取って、ItemTemplateで配列の個々のPointCollectionについてPolylineで描画することで、PolyPolylineと同等の機能が実現できるはずです。
<ItemsControl ItemsSource="{Binding PointCollections}">
    <ItemsControl.ItemTemplate>
        <DataTemplate DataType="PointCollection">
            <Polyline Points="{Binding}" Stroke="Black" StrokeThickness="1" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>
ItemsControlのItemsSourceでPointCollectionの配列をバインディングし、ItemTemplateでPolylineを使って描画しています。また、ItemsPanelでCanvasを指定してあげることによって、Polylineを描画するエリア(ItemsControlコントロールが貼られている場所)をCanvasにすることができます。
至ってシンプルです。

あとはViewModel側でPointCollectionの配列を与えてあげます。


描画した結果がこちらです。


1万個を超えるアイテム数ですが、これでも難なく表示してくれちゃうWPFさんイケメン!(GDIだと(時代も時代ですが)リソース不足で落ちるのが関の山(ry

もちろん、ItemsControlを使っていますので、PointCollectionへの配列ではなく座標情報以外に別の情報も含んだクラスへの配列にすることで、例えばある一部分だけ色を変えたり線の太さを変えたり、はたまた点線にしたりすることもできるでしょう。そこまでやると、さすがにGDIのPolyPolylineにはできないことになりますね。強力なWPF+XAMLだからこそできることになるでしょう。

2016年7月10日日曜日

VLC media playerをLAN経由で操作する


VLC media playerというメディア再生ソフトがあります。クロスプラットフォームでオープンソース、そして数多くのコーデックを内蔵しているため、ほとんどのメディアファイルを再生できます。私も、動画ファイルの再生にはとても重宝しています。

さて、このVLC media playerですが、LAN経由で操作する機能を持っています。
例えば動画を再生中、パソコンから離れて見ていても、手元のスマホなどがLANにつながっていれば、再生を止めたり、シークしたり、プレイリストの次のファイルへ行ったりと、様々な操作ができるとても便利な機能です。

有効にするのも簡単です。詳細は他のページに譲りますが、VLCの設定画面でWebインターフェースを有効にし、
C:\Program Files\VideoLAN\VLC\lua\http\.hosts
でアクセスを受け入れるIPアドレスのマスクを設定してやれば良いだけです。



そして適当なウェブブラウザでhttp://localhost:8080を開くとウェブからVLCを操作する画面が出てきます。もちろんLAN他のPCからIPアドレスを指定してアクセスすればリモートで操作できるわけですし、これを使いやすいようにしたAndroid/iPhoneアプリなども出回っています。

というわけは、当然C#でVLCリモートコントロールライブラリが作れるのでは?という発想になるわけです。
まあ、パソコンの場合ウェブブラウザからアクセスできるのでそんなに必要性は無いわけですが…。いやいや、自分でアプリ作りたいでしょ…作りたくない?

いろいろ調べてみましたが、意外と操作するのは簡単みたいです。
VLC HTTP requests
ここに詳しく書かれていました。HTTPのGETリクエストでコマンドを送る仕様になっているようで、非常にシンプルです。ブラウザで試してみることすらできてしましますが、例えば
http://localhost:8080/requests/status.xml?command=pl_play
にアクセスするだけでVLCで一時停止/停止していたファイルを再生することができてしまいます。
また、一方でそのstatus.xmlの本文にXMLで現在再生中のファイルのファイル名やメタ情報や再生長、再生位置やボリュームから現在フルスクリーンで表示しているかどうかまで非常に多種多様な情報が入っています。これをパースすることでシークバーなどを作れそうですね。


というわけで、ライブラリを作ってみました。

VLC Controller - nuget

やっていることはstatus.xmlとplaylist.xmlのパースと、コマンドの送信機能です。

VLCを操作する

まずは、上記のNugetからライブラリをインストールし、インスタンスを作ります。
VlcController vlc = new VlcController() {
    HostUrl = @"http://localhost:8080/",
    Password = "****",
};
VLCが動いているマシンのURLとBasic認証用のパスワードを指定し、インスタンスを形成します。
public async void PlayButtonClicked()
{
    await vlc.Play();
}
あとは超簡単で、このように必要に応じてメソッドを呼び出せば良いです。どのようなメソッドがあるかはVisualStudio等で確認してください。引数も特に困ることは無いかと思います。

あ、再生メソッドやプレイリスト編集系メソッド等に「id」っていう引数が出てくるものがあると思いますが、それに関しては、下記のプレイリストについてのところでidが説明されますので、それを入れてください。

VLCの状態を取得する

再生中のファイルの情報や、再生位置、ボリューム、ランダム再生のON/OFFなどといった、再生状態にかかわる情報はStatusプロパティ、プレイリストについてはPlaylistプロパティに保持されています。このデータは一定間隔でポーリングして取得しており、デフォルトではポーリングはON、 ポーリング間隔は1秒にしてあります。これもVlcControllerクラスのプロパティから変更可能です。
もしもVLCが起動されていないなどで接続できていない場合はIsConnectedプロパティがfalseになります。また、それと同時にStatusプロパティとPlaylistプロパティがnullになります。注意してください。

Status

Statusクラスには、上記の通り再生状態にかかわる情報が入っています。プロパティ一覧を見れば、どのような情報が返ってくるのかがわかるでしょう。

Playlist

Playlistクラスはツリー構造が取られています。VLCのプレイリストはツリー構造になっており、通常のプレイリストの他、マイビデオなどにあるメディアもプレイリストから参照できるようになっています。


左上に「マイビデオ」等が書かれていますね。 これを1度クリックすると読み込まれ、Webインターフェースを介して触ることができるようになります。

PlaylistクラスにはRootNodeプロパティがあり、ここにはNodeクラスのインスタンスが入っています。このルートノードの下に、プレイリスト、メディアライブラリ、マイビデオなどといった各項目のノードが入っております。
各階層の中に入っている再生項目はLeafクラスのインスタンスになっており、これには長さ、URI、現在再生中の項目かどうかを示すフラグなどといった情報が入っています。LeafとNodeはともにElementBase抽象クラスを親クラスとして持っており、NodeクラスのChildrenプロパティはElementBaseの配列になっていることに注意してください。

ElementBaseクラスにはIdプロパティがあり、各ノード、LeafにユニークなIdが振られています。これを一部のコントロール系メソッドの引数に渡してやることによって、任意の項目を再生したり、プレイリストから削除したりすることができるようになります。


このライブラリを使って、ガジェットのようなものを作ったり、ツールバーに常駐するようなツールを作ることで、単なるWebインターフェースよりも使い勝手のいいリモコンツールを作ることができるかもしれませんね。
もしくは、このライブラリが使えるかどうかはわかりませんが、Wi-Fi経由でVLCを操作するリモコンなどを最近流行りのラズベリーパイみたいなコンピューターで作ってみるのも手かもしれません。

夢は広がりますね!

2016年6月28日火曜日

Bloggerのhttps強化によるSyntaxHighlighter周りの修正

(2016/9/22追記)Google Driveのホスティングサービス終了により、この記事の方法では正常に動作しなくなりました。対策として、Firebaseのホスティングサービスを使用する方法があります。詳しく?はこちらへ。


どうも久しぶりです。

最近、自分のブログにアクセスしようとするとやたら重たいなということに気づき、いろいろ探ってみるとSyntaxHighlighterが怪しいということになりました。

SyntaxHighlighterはソースコードの予約語などをハイライトするツールで、多くの言語にも対応していたためこのブログでも使用していたのですが、どうやら最近そのソースコードのハイライトがされなくなっているようでした。

なぜだかわからないまま試行錯誤していたのですが、ようやく原因がわかりました。
Bringing HTTPS to all blogspot domain blogs 
どうも今年の5月の頭にblogspotドメインからありとあらゆるドメインへのhttpsリダイレクトがデフォルトでONになったようで、当ブログも例外ではありませんでした。
このブログではSyntaxHighlighter公式のホスティングサービスを用いて必要なjsファイルなどを読み込んでいましたが、そのホスティング サービスはhttpsに対応しておらず、そのリダイレクトで読み込みに時間がかかった挙句失敗するという非常に残念な状態になっていたようです。

上記のブログにはこのようなセンテンスもありました。
Please be aware that mixed content may cause some of your blog's functionality not to work in the HTTPS version. Mixed content is often caused by incompatible templates, gadgets, or post content. While we're proactively fixing most of these errors, some of them can only be fixed by you, the blog authors.
(筆者拙訳)混在コンテンツ(HTTPSとHTTPが混在したコンテンツ)はブログの一部機能が正常に動作しなくなる原因となりうる点に注意して下さい。テンプレートやガジェット、投稿コンテンツがHTTPSに対応していない場合、混在コンテンツになりえます。我々はそれらの問題の多くを積極的に修正しているところですが、一部はブログ著者のあなたでなければ修正できないものもあります。
ということで、今回の問題はその「ブログ著者じゃないと修正できない問題」に該当するようです。


問題がわかれば対処はどうにかなります。

まずは、SyntaxHighlighterをDLし、自分のGoogle Driveに保存して公開します。
そして、下記のコードをテンプレートのHTMLのヘッダー要素の一番最後にコピペします。

<!-- Begin SyntaxHighlighter-->
<link href='https://googledrive.com/host/[ID]/styles/shCore.css' rel='stylesheet' type='text/css'/> 
<link href='https://googledrive.com/host/[ID]/styles/shThemeDefault.css' rel='stylesheet' type='text/css'/> 
<script src='https://googledrive.com/host/[ID]/scripts/shCore.js' type='text/javascript'/> 
<script src='https://googledrive.com/host/[ID]/scripts/shBrushCpp.js' type='text/javascript'/> 
<script src='https://googledrive.com/host/[ID]/scripts/shBrushCSharp.js' type='text/javascript'/> 
<script src='https://googledrive.com/host/[ID]/scripts/shBrushCss.js' type='text/javascript'/> 
<script src='https://googledrive.com/host/[ID]/scripts/shBrushJava.js' type='text/javascript'/> 
<script src='https://googledrive.com/host/[ID]/scripts/shBrushJScript.js' type='text/javascript'/> 
<script src='https://googledrive.com/host/[ID]/scripts/shBrushPowerShell.js' type='text/javascript'/> 
<script src='https://googledrive.com/host/[ID]/scripts/shBrushPython.js' type='text/javascript'/> 
<script src='https://googledrive.com/host/[ID]/scripts/shBrushXml.js' type='text/javascript'/> 
<script type='text/javascript'>
window.setTimeout(function() {
    SyntaxHighlighter.config.bloggerMode = true;
    SyntaxHighlighter.defaults['toolbar'] = false;
    SyntaxHighlighter.all();
}, 20);
</script>

[ID]の部分にはGoogle Driveの共有フォルダのアクセスIDを入力します。
IDとはGoogle Driveで共有設定したときに出てくる画面のid=の文字列です。上の画像のモザイクを掛けた部分です。
もちろん、テーマや読み込むスクリプトなどは必要に応じて変更すると良いでしょう。

また、最後に
SyntaxHighlighter.defaults['toolbar'] = false;
の一文を設けていますが、これを置くことでソースコード画面の右上の「?」マークが消えます。最初は消さなくてもいいかな~って思っていましたが、1行目に横長のコードを書くと鬱陶しくて仕方ないんですよね、これ。


というわけで、無事SyntaxHighlighterの問題を解決し、ブログの読み込みも早くなりましたとさ。めでたしめでたし。

P.S. ついでにブログのデザインも変えてみました。自分で目がチカチカするな~とか思ったらまた別のデザインにするかもしれません。

2016年2月9日火曜日

expressionとstatement

たまにはプログラミング言語についての話をしてみようかと思います。

最近、Twitterで中括弧戦争みたいなのが起きているらしいです。ぶっちゃけ、プログラミング言語なんてコンパイルできれば正義ですし、なおかつ保守性が高いコードを書けるのならば二重丸です。中括弧を付ける場合も付けない場合も、どこかに改行を入れる場合も、それが見やすいと思えるのならばそれで良いでしょう。実際、その辺はコーディング規約とかコーディングスタイルと言った言葉で、各宗派がある程度出来上がっていますし、たいてい、この手の宗派は入門に使った書籍に準じてしまうでしょうから、一緒のプロジェクトをやっているのでもなければ過干渉する必要はないと私は思っています。

しかし、これを機にググってみると中括弧に関する理解の甘い記述が見受けられます。プログラミング言語は自然言語ではないので、「言語ありきで、それについて文法などの説明を後付けする言語」ではなく、「文法ありきでそれをもとに記述する言語」です。
と言うわけで、私のC言語のこのあたりに関する理解について、ここにメモしておこうと思います。

中括弧の省略記法ではない

まず、if文などの制御文についてです。
if(hoge) {
    foo();
}

C言語ではおよそこのような書き方をします。ちなみに、中身が一文のみの場合、中括弧は省略できるとの記述があるものがしばしば見られます。
if(hoge)
    foo();

結果論としては正しいというか、そう見えます。しかし、中括弧が存在するのが原則で、省略記法としてその中身が一文だけなら中括弧が省略できるという理解なのならば、それは不十分です。


この手のプログラミング言語には「式(expression)」と「文(statement)」の概念を持っています。

まず、式とは
  • 値の計算を指定する
  • オブジェクトもしくは関数を指し示す
  • 副作用を引き起こす
の3種類の動作、またはその組み合わせを行うもののことを言います。副作用(side effect)という言葉は関数型言語マン以外には馴染みの薄い表現で、かつマイナスのイメージを持つ言葉かもしれませんが、ここでは「実行環境の状態を変化させる」程度の意味しか持ちません。例えば、変数の値を変更する(ことによって実行環境のメモリの値を一部書き換える)などといったことです。
式は評価した値を持ちます。

一方で、文とは実行すべき動作を規定したものです。JIS規格では
  • ラベル付き文
  • 複合文
  • 式文
  • 選択文
  • 繰返し文
  • 分岐文
の6種類が文として定義されています。詳しくは割愛しますが、ラベル付き文、式文、分岐文はセミコロンで終わる文法になっていて、選択文や繰返し文はいわゆるif文やwhile文など(分岐や繰り返しの終了地点まで含んだもの)で、複合文は中括弧で囲まれたブロックとなっています。式文はその名の通り式が入る文で、ここに空文も含まれています。分岐文はbreakとかcontinueとかreturnとかですね。


これらを踏まえた上で、if文の構文がどう定義されているかと言うと、
if ( 式 ) 文
です。 いたってシンプルです。

ここから導き出されるのは、中括弧無しは一文の場合の省略記法ではなく、式文で書くか複合文で書くかの違いに過ぎないということです。並列していくつもある「文」のうち1つを書けるという話なので、「原則が中括弧」みたいな発想はその人の固定観念ということになってしまいますね。


カンマ演算子

複合文は0個以上のをまとめて1つのとして扱うものですが、C言語には複数のを1つのとしてまとめる「カンマ演算子」と言うものがあったりします。ほぼほぼ出番が無い演算子ですので、知らない人も多いかもしれません。

カンマ演算子は次のような構文を持ちます。
式, 式, 式, ... , 式
2個以上の式をカンマでつなげるだけです。なお、この式の評価した値は最後の式(一番右側の式)となります。
int i, j, k;

k = (i = 0, j = 1); 
printf("i = %d, j = %d, k = %d", i, j, k);

このコードはコンパイルでき、実行すると
i = 0, j = 1, k = 1
という出力が得られます。 これを上手く使うことで、複合文のような記述を式文を使って作ることができます。
if(hoge)
    puts("hoge"),
    puts("foo");
else
    puts("bar");

ifの1つ下の行のputsは、末尾がセミコロンではなくカンマになっています。すなわち、2行目と3行目の2つのputsを合わせて1つの式にしているわけです。そのため、ifの直下の文は1つの式文になるため、これはコンパイルが通ります。もしもifに与えた条件式hogeが真ならば、hogeとfooが両方とも画面に出力されます。

この記法は中括弧で複数文をくくるより果てしなく見にくいです。と言うより、カンマかセミコロンかなんてパッと見で区別できる人なんてなかなかいません。そのような観点から、使われることはまずないと思われますが、たまに関数マクロなんかで上手く使えば問題の起きにくいマクロが作れたりしてそのような場に出番があると言えるでしょう。

else if文は存在しない

さて、C言語の選択文の項目には、実際には3種類の構文が規定されています。
  • if ( 式 ) 文
  • if ( 式 ) 文 else 文
  • switch ( 式 ) 文
見出しでネタバレしちゃっていますが、そうです、C言語に「else if文」と言うのは存在しません。しかしよく「else if」と記述する人はいますし、実際それでもコンパイルは通ります。
実はこれ、if文全体が1つの文と見なされることを利用し、else以下の文としてif文を置いただけということになります。すなわち、else ifはこのように書くのと同値です。
if(hoge) {
    foo();
} else {
    if(hogehoge) {
        foobar();
    } else {
        fooooo();
    }
}

一般的にこのような記法は無駄にインデントが増えるだけで好まれません。「if文の条件が成り立たなかったときに次の条件が成り立つかを判定する」という概念を1つ頭の中に入れるだけでelse ifという記述は読みやすくなりますから、その記法に反対する人はいないでしょう。しかし、言語仕様ではあくまでも「else以下の文をif文とした記法」となっているわけです。

それを踏まえると、else whileやelse switchなどの記法も同様に文法上書けることになってしまいますし、
if(hoge)
    while(hogehoge) {
        foobar();
        fooooo();
    }

といった記法も許されます。
読みやすいかどうかは別として、どこまでが文で、どこまでが式かと言うことをしっかりと理解しておけば、プログラムを見たときの脳内コンパイルは捗るでしょう。

C#のusing文

C#におけるusing文は、上記のif文同様に
using ( リソース取得式 ) 文
という構文になっています。もちろんusing文自体も文の1つにカウントされるため、using以下にusingを書くことができます。
using(StreamWriter sw = new StreamWriter("dst.txt"))
using(StreamReader sr = new StreamReader("src.txt"))
{
    while((line = sr.ReadLine()) != null)
        sw.WriteLine(line);
}

このように書くことによって、複数のIDisposableインスタンスを作って最後にすべて破棄するという動作を見た目上やっているようになります。本来ならば2つ目のStreamReaderのところはインデントを1段下げるべきなのでしょうが、特に並列的に書いて問題ないところですし、逆に下げると見にくくなるので敢えて下げていません。
下手したら、「usingは並列にいくつも書ける」みたいな説明をしだすところもあるかもしれませんが、特段文法上そういった定めをしているわけではなく、これはC言語系統の文法の体裁として、むしろ一般的な記述であるわけです。

まとめと私見

ここまで読んで、C言語系の言語のソースコードを見る目が変わった方もいるかもしれません。今まではなんとなく書いていたけど、今となればどれが文でどれが式かを意識しながら書けるようになっているかもしれません。

そもそも、プログラミング言語は、0と1のみで表されるコンピューターへの命令を人間にわかりやすい方法で書けるようにしようとして発展してきたものです。今は高度に抽象化され、さらに様々な記法やパラダイムで多種多様な形態を持っていますが、最終的に動作するコンピューターの仕組みは原則として同じです。そして、モダンなプログラミング言語はそのコンピューターの仕組みを知らなくてもできるだけ簡単に書けるように敷居を下げた一方で、やはりハイレベルなプログラミングにはどうしてもそういった知識は付いて回ります。
おそらくプログラミング言語に関してもそれは同じで、一般的な書き方、一般に可読性が良く保守性が高いと言われる記法はありますが、それより一歩進んでコンパイラーがどのように構文を解析し理解するかを頭に置いてプログラムを書くのはとても大事なことだと言えるでしょう。

私は、たまたまC言語を始めるにあたって巡り合った入門書に比較的そういった記述が多く、このような考え方を非常に強く植えつけられながらC言語を理解していきました。そのため、if以下の文などに中括弧を付けるかどうかの議論で「省略したほうがスマート」とか「省略したら可読性が下がる」みたいな意見を見るたびに「そもそも省略じゃないよねそれ」という気持ちになってしまいます。


私は、ifの下を必ず複合文にするかと言うと、そうではありません。むしろ、1つしか式を書かない場合は積極的に式文を使います。
「if文の中身を複数文にしたくなった時に中括弧を付け忘れたらバグになるじゃないか」という意見をたまに見ますが、私はそう思いません。インデントはIDEがちゃんと整理してくれますから、インデントだけ付けて中括弧を付け忘れる状態に気づかないということもまず無いですし、そもそも変更箇所の前後の中括弧が目に留まらないということもまずありません。むしろ、中括弧で1行を使ってしまうとプログラムの縦方向の密度が無駄に下がり、可読性が下がると考えています。
ただし、ifの直下にwhile文だけが入る場合など、一文でも複数行にまたがる場合は基本的に中括弧を入れています。すなわち、ほぼほぼ行数で決めているといったところでしょうか。

一方で「if(式)」とその「中身の文」の間に改行を入れない記法はまずしません。
if(input < 0)  return ERROR;

このような記法は、まあこれくらいなら良いのかもしれませんが、条件式が少しでも長くなると可読性が一気に下がるので私は好みません。たとえ空文でも次の行を1行使います。


結局、この辺りは好みだと思います。自分がC言語を習得したときの入門書、もしくはこれまでに使っていたプログラミング言語の風習、自分の感性、そういったものが合わさって「自分はこう書く」みたいな話が出てくるものでしょう。

大事なのは、言語の仕様を理解し、それに即し、また可読性と保守性が良くなるように自分で工夫を重ねることだと思います。そういったところから、技術力の高さをアピールしていきたいですね。