お昼無性にラーメンが食べたくなって、 「そういえば郷家の辛ねぎらーめん食べたことないな」と思ったので、 食べに行ってきた。
魚介豚骨の旨味と、辛ねぎのスパイシーさが絶妙にマッチしていて美味。 麺も細すぎずちょうどいい太さで、ツルシコで良かった。 さすが、一番人気なだけある。 支那そばと担々麺も気になるので、 近いうちにまた訪れることになりそうな予感。
お昼無性にラーメンが食べたくなって、 「そういえば郷家の辛ねぎらーめん食べたことないな」と思ったので、 食べに行ってきた。
魚介豚骨の旨味と、辛ねぎのスパイシーさが絶妙にマッチしていて美味。 麺も細すぎずちょうどいい太さで、ツルシコで良かった。 さすが、一番人気なだけある。 支那そばと担々麺も気になるので、 近いうちにまた訪れることになりそうな予感。
.NET でコンソールアプリケーションを開発するときの悩みの種が、コマンドライン引数の解析。 .NET Framework がコマンドラインパーサーを提供してくれないので、 仕方なくオレオレパーサーを書いたり、Mono にあるライブラリを使ったりしてきたけど、 .NET Core 時代になってようやく Microsoft がライブラリを提供してくれた。
cURL みたいな、サブコマンドを持たない CLI を書くならこんな感じになる。
using System; using Microsoft.Extensions.CommandLineUtils; namespace SampleCli { class Program { static void Main(string[] args) { var app = new CommandLineApplication(throwOnUnexpectedArg: false); app.Name = nameof(SampleCli); app.Description = "cURL みたいな単機能 CLI"; app.HelpOption("-h|--help"); var methodOption = app.Option( template: "--method", description: "HTTP メソッド", optionType: CommandOptionType.SingleValue); var urlArgument = app.Argument( name: "url", description: "URL", multipleValues: false); app.OnExecute(() => { if (urlArgument.Value == null) { app.ShowHelp(); return 1; } var method = "GET"; if (methodOption.HasValue()) { method = methodOption.Value(); } Console.WriteLine($"{method} {urlArgument.Value}"); return 0; }); app.Execute(args); } } }
Command メソッドを使えばサブコマンドも追加できるので、Git みたいな多機能な CLI も夢じゃない。
using System; using Microsoft.Extensions.CommandLineUtils; namespace SampleCli { class Program { static int Main(string[] args) { var app = new CommandLineApplication(throwOnUnexpectedArg: false); app.Name = nameof(SampleCli); app.Description = "Git みたいな多機能 CLI"; app.HelpOption("-h|--help"); app.Command("clone", command => { command.Description = "リポジトリをクローンします。"; command.HelpOption("-h|--help"); var urlArgument = command.Argument("url", "リポジトリの URL"); command.OnExecute(() => { if (urlArgument.Value == null) { command.ShowHelp(); return 1; } Console.WriteLine($"clone {urlArgument.Value}"); return 0; }); }); app.Command("remote", remoteCommand => { remoteCommand.Description = "リモート一覧を表示します。"; remoteCommand.HelpOption("-h|--help"); remoteCommand.Command("add", addCommand => { addCommand.Description = "リモートを追加します。"; addCommand.HelpOption("-h|--help"); var nameArgument = addCommand.Argument("name", "リモートの名前"); var urlArgument = addCommand.Argument("url", "リポジトリの URL"); addCommand.OnExecute(() => { if (nameArgument.Value == null || urlArgument.Value == null) { addCommand.ShowHelp(); return 1; } Console.WriteLine($"remote add {nameArgument.Value} {urlArgument.Value}"); return 0; }); }); remoteCommand.OnExecute(() => { Console.WriteLine("origin"); Console.WriteLine("staging"); Console.WriteLine("production"); return 0; }); }); return app.Execute(args); } } }
サブコマンドにも対応できるくらいに高機能。自分のユースケースは全て満たしているので、もうこればかり使っている。オレオレパーサーは窓から投げ捨てた。
デザインが苦手な自分みたいな開発者にとって、FontAwesome は非常に助かる存在。 少なくともアイコンに関しては悩むことがなくなる。 そんなステキなアイコンフォントセットを、Web だけでなくアプリの開発でも使いたい、と考えるのは自然なことだと思う。
Xamarin.iOS でも FontAwesome は使える。そう Iconize ならね。
NuGet でパッケージをインストールしたら、AppDelegate に初期化処理を追加する。
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions) { Plugin.Iconize.Iconize.With(new Plugin.Iconize.Fonts.FontAwesomeModule()); return true; }
パッケージに含まれているアイコンフォントをアプリから使えるようにするために、Info.plist に UIAppFont も追加しないといけない。 よく忘れがちなので注意。
<key>UIAppFonts</key> <array> <string>iconize-fontawesome.ttf</string> </array>
あとは使うだけ。FontAwesome のアイコンフォントを UIImage に変換してから、タブやツールバーに表示するのをよくやる。
using System; using UIKit; using Plugin.Iconize; using Plugin.Iconize.iOS; namespace FontAwesomeSample.ViewControllers { public class GitHubViewController : UITableViewController { public GitHubViewController() { Title = "GitHub"; // GitHub のアイコンフォントを UIImage 化してタブに表示 var icon = Iconize.FindIconForKey("fa-github"); var image = icon.ToUIImage(28); TabBarItem = new UITabBarItem() { Title = "GitHub", Image = image, }; } } }
実行すると、ちゃんとタブに GitHub アイコンが表示されている。
UILabel や UIButton を拡張したコントロールも提供されてはいるけど、UIImage に変換するのが一番使い勝手がいい。
メタプログラミングと聞くと、LISP や Ruby や JavaScript といったスクリプト言語を思い浮かべる人が多いと思う。 でも、メタプログラミングはスクリプト言語の専売特許というわけではない。C# のような静的型付けの言語でも可能だということを、本書は示している。 T4 を使えばソースコードを生成できるし、式や Reflection.Emit で動的にメソッドを作成して実行することも可能。 ビルド後にアセンブリを書き換えて、横断的な処理を追加することだってできる。そのためのツールは提供されている。Roslyn を使い、動的にソースコードの文字列をコンパイルして実行できるところまできていて、Ruby や JavaScript で行うメタプログラミングにかなり近づいた。
ただ、2013年の本なので、今となっては参考程度にとどめておいたほうがいい内容もまぁまぁある。 DLR で出てくる IronPython や IronRuby といった Iron 言語は、自分の観測範囲で名前を聞かなくなって久しい。Boo も。 Spring.NET は、本家である Java の Spring の進化についていけず、置きざりにされてしまった感が拭えない。
自分は社内向けにライブラリを書いたりする仕事が多いので、T4 でソースコードを生成したり、式で動的にメソッドを作成して実行したりするコードは書いてきた。 CodeDom は大昔に少し使っていたが、ソースコード生成どまりで、動的にアセンブリを作成して実行するまではやってない。 それに T4 使うようになって出番なし。 Reflection.Emit や Cecil を使って IL を書くのは、まだ本格的にやってない。 IL までいくと、いよいよ黒魔術だ。 ただ、黒魔術に手を染めたいと思い始めた。 C# でラムダ式を書いてデバッグ実行し、式木の構造を調べるのはよくやるが、 IL でも似たアプローチができるのは目から鱗だったので。 確かに、書いたコードがどんな IL に変換されるのか、ILSpy を使えば簡単に調べることができる。 この TIPS は本書で1番の収穫かもしれない。
本書は .NET の黒魔術書と言ってもよさそうだ。
先日、SQL Server で緯度経度から住所を検索する方法を試したが、 PostgreSQL でも拡張の PostGIS を使えば同じようなことが可能だった。
-- 住所を格納するテーブルを作成 CREATE TABLE addresses ( address_id BIGSERIAL PRIMARY KEY, address TEXT, location GEOMETRY(POINT, 4326) ); -- 空間インデックスを作成 CREATE INDEX ON addresses USING gist (ST_Transform(location, 32654)); -- テストデータを投入 INSERT INTO addresses ( address, location ) VALUES ( '福岡県福岡市中央区天神一丁目', ST_GeomFromText('POINT(130.401396 33.590878)', 4326) ); INSERT INTO addresses ( address, location ) VALUES ( '福岡県福岡市中央区天神二丁目', ST_GeomFromText('POINT(130.398043 33.589390)', 4326) ); INSERT INTO addresses ( address, location ) VALUES ( '福岡県福岡市中央区天神三丁目', ST_GeomFromText('POINT(130.396224 33.593515)', 4326) ); INSERT INTO addresses ( address, location ) VALUES ( '福岡県福岡市中央区天神四丁目', ST_GeomFromText('POINT(130.399850 33.594580)', 4326) ); INSERT INTO addresses ( address, location ) VALUES ( '福岡県福岡市中央区天神五丁目', ST_GeomFromText('POINT(130.398918 33.596781)', 4326) ); -- 天神二丁目の2の緯度経度に一番近い住所を取得 SELECT address_id, address, ST_Transform(location, 32654) <-> ST_Transform(ST_GeomFromText('POINT(130.398821 33.589085)', 4326), 32654) AS distance FROM addresses ORDER BY distance LIMIT 1;
Amazon RDS for PostgreSQL だと、最初から PostGIS が有効になっているのですぐ使えた。
13巻の主役は二海堂だった。 主人公の桐山はほとんど見せ場なし。 二海堂は登場するたびに、そのひたむきさとふくふくさに癒されてたけど、 今回は宗谷名人とのガチ対局。 二海堂のガチ対局が描かれるのっていつ以来だろう。かなり久しぶり。 一手損角換わりのワクチンを編み出すどころか、むしろ魅了されてしまっていたところは、ミイラ取りがミイラになったようでクスッときた。 そこ以外はあわや勝利かいう展開で、熱い一局だった。 この展開って普通、主人公がするんだけどね。
あと、あかりと島田八段、林田先生の3人も良かった。これはもう三角関係ってやつだよな。島田八段は予想通りだったが、まさか林田先生まで参戦するとは。 しかもギャグではなくマジで。 この三角関係はどうなることやら。 島田八段と林田先生、どちらもグイグイくるタイプには思えないので、先が予想できない。 妻子捨男のせいで負ったあかりのトラウマも深そうで、一筋縄ではいかないだろうし。 桐山は結構リアリストだから、この2人だと島田八段を推しそうだけど。 さて、どうなるのか続きが気になる。 あかり視点と林田先生視点の話はあったけど、島田八段視点が無かったので、ぜひとも描いて欲しいところだ。
緯度経度を使って住所を検索する必要があったが、 SQL Server の地理空間データ型(geography)を使うことで手軽に実現することができたのでメモしておく。
-- 緯度経度と住所を格納するテーブルを作成 CREATE TABLE addresses ( id bigint IDENTITY(1,1) NOT NULL, address nvarchar(100) NOT NULL, location geography NOT NULL, CONSTRAINT pk_addresses PRIMARY KEY CLUSTERED ( id ASC ) ) GO -- geography 型の location にインデックスを作成する CREATE SPATIAL INDEX spatial_addresses ON addresses ( location )USING GEOGRAPHY_GRID GO -- テストデータを投入 INSERT INTO addresses ( address, location ) VALUES ( N'福岡県福岡市中央区天神一丁目', geography::STGeomFromText(N'POINT(130.401396 33.590878)', 4326) ); INSERT INTO addresses ( address, location ) VALUES ( N'福岡県福岡市中央区天神二丁目', geography::STGeomFromText(N'POINT(130.398043 33.589390)', 4326) ); INSERT INTO addresses ( address, location ) VALUES ( N'福岡県福岡市中央区天神三丁目', geography::STGeomFromText(N'POINT(130.396224 33.593515)', 4326) ); INSERT INTO addresses ( address, location ) VALUES ( N'福岡県福岡市中央区天神四丁目', geography::STGeomFromText(N'POINT(130.399850 33.594580)', 4326) ); INSERT INTO addresses ( address, location ) VALUES ( N'福岡県福岡市中央区天神五丁目', geography::STGeomFromText(N'POINT(130.398918 33.596781)', 4326) ); GO -- 天神二丁目の2の緯度経度に一番近い住所を取得 DECLARE @g GEOGRAPHY; SET @g=GEOGRAPHY::STGeomFromText('POINT(130.398821 33.589085)', 4326); SELECT TOP(1) id, address FROM addresses WHERE location.STDistance(@g) IS NOT NULL ORDER BY location.STDistance(@g);