2017年10月30日月曜日

C#のforとforeachに関する思想録

どうも、はざまです。つい先日、とあるニコ生を見ている際に、forとforeachの違いについて言及される場面があり、私がforeachを使うことを勧めたところ、foreachの方がiteratorオブジェクトの破棄がループごとに発生するから遅くなるという指摘をいただき、気になったので速度比較してみることにしました。私はそのとき、え〜Releaseビルドなら、ループ全体でiteratorを使い回すように最適化してくれるんじゃないと答えたのですが、果たして結果は(よく考えたら、iteratorは使いまわさないとおかしいですね。ループ状態を内包しているはずなので)。
今回試したコードは、以下のようなものです。
const int Max = 10_000_000;

var stopwatch = Stopwatch.StartNew();
int result = 0;
foreach(var i in Enumerable.Range(0, Max))
    result += i;

stopwatch.Stop();
Console.WriteLine("foreach Result: {0}/{1}ms", result, stopwatch.ElapsedMilliseconds);

var stopwatch2 = Stopwatch.StartNew();
int result2 = 0;
for(int i = 0; i < Max; ++i)
    result2 += i;

stopwatch2.Stop();
Console.WriteLine("for Result: {0}/{1}ms", result2, stopwatch2.ElapsedMilliseconds);


Stopwatchインスタンスを生成して、foreach/forループ内で足しこむだけです。最後にそれぞれの結果を出力するのは、デッドコード削除で足しこみ自体省略されたら、嫌だなという意図です。このコードだとそれぞれ、確実にオーバーフローが発生しますが、何も書かなければC#はオーバーフローを無視してくれるはずなので、今回は特に考慮していません。さて、この単純なコードで出た結果がこちら。

Debugビルド時
foreach Result: -2014260032/197ms
for Result: -2014260032/41ms

Releaseビルド時
foreach Result: -2014260032/171ms
for Result: -2014260032/26ms

テスト環境はMacBook Pro (Retina, 13-inch, Early 2015)で2.7Ghz Intel Core i5、メモリ16GBです。Debugビルド時でおよそ5倍、Releaseビルド時で7倍弱程度の差ができてしまいました。これを見る限り、ループごとに一時変数を用意してGC走らせていそうなのは間違いなさそうですね。それにしても、結構インパクトでかいです。そっか〜、foreachって遅かったのか〜。とは言っても、可読性重視したいので、使い続けますけどね。for使うとめんどくさいですしね。
速度にうるさいC++のrange-based forとかはどんな実装になってるんだろう。ループ全体で一時変数を使いまわしたりしてないのかな〜。まあ、そうしたら、この一時変数のスコープが大きくなっちゃうんですがね。
では、また(๑╹ω╹๑ )

2017年10月4日水曜日

Rust本の翻訳始めました

お久しぶりです。ご無沙汰してました、はざまです。昔のハンドルネームに戻したりしましたが、ここではこのままはざまと名乗り続けると思います。

さて、本題なのですが、題名の通り、Rustプログラミング言語の公式ドキュメントであるThe book第2版の翻訳を始めました。実際には数ヶ月前から行っているので、もっと早くにご報告していればよかったですね。公式リポジトリで今後大きな変更がないと考えられるFrozen扱いされた章から順に翻訳を進めているので、牛歩ではありますが、お付き合い頂ければと思います。
ドラフトの執筆が完了するまでは、こちらのgithubページのsecond-editionディレクトリで読んでいただくしかないかと思われます。目次がなく読みにくいかとは存じますが、どうぞご容赦ください。

では、今回はここまで。どうぞ、よしなに。

2016年9月12日月曜日

プログラミングにおける演算子と言語の進化に関する思想録

いきなり紛糾から本文を始めてしまいますが、現代において最も幅広く使われている言語の直系の始祖はC言語です。しかし、このC言語は最大にして不可侵の過ちを犯してしまいました。それはそう、"代入演算子"です。
数学における"="記号は"等価"を表します。a = 1という式は、aは1と等しいという意味であり、それ以外の何者でもありません。しかし、あやつ(C言語)はこの記号に代入という新たな意味を割り当て、代わりに等価演算子は"=="という新たな記号を生み出してしまいました。これにより、数学世界とコンピュータ世界の乖離が発生してしまったのです。

この乖離により、コンピュータ言語への入門のハードルが一段階上がってしまったと言えるでしょう。なぜなら、C言語での代入記号の導入により、その血筋を受け継ぐ言語(いわゆるC系言語)においても、"="記号は、代入演算子として使用される羽目となり、その結果、これら後継言語においても数学世界との乖離を生み出してしまったためです。
代入記号の導入により生み出される混乱は以下の通りです。


  1. if文内で数学の"="記号として、この記号を使用する
  2. 実際には代入となるため、左辺の値が変わってしまう
  3. 特にC言語系においては、if文の条件式になんら制約がないので、コンパイルが通ってしまう
  4. 結果、if文内で変数の値が変わってしまい、想定と違う動作をするバグを作りこんでしまう

もちろん、この程度の落とし穴ならば、人間が細心の注意を払えば回避可能ではありますが、現代のコンピュータ業界において、性能の向上は著しいものがあります。このような、多少のコンピュータリソースの消費で回避できうるミスならば、回避できる機構を用意して、使うべきだというのが持論です。
その持論に沿うように、最近作られた言語では、if文の条件式にbool値を返す式しか書けないようになっていたりして対策が施されています。
登場当初は、その万能性から高級言語と呼ばれていたであろうC言語も、今となっては、中級言語と呼ぶべき存在になってしまいました。今後も、言語が進化を続け、ヒューマンエラーの排除を言語が行ってくれるようになるといいですね。

※思想「録」と言いつつ、1エントリーしかありませんσ^_^;

2016年9月9日金曜日

TS LISPソース

どうも、はざまです。
前回の記事で、TS LISPの紹介をしました。今回の記事は、そのTS LISPのソースを全掲載しようと思ったのですが、さすがに長くなりすぎる上に、一覧で見せられても、閲覧性が悪くなるだけなので、その役目はGithubに譲るとして、解説を軽くふわりとするだけに留めようと思います。
中身自体、TypeScriptのコンパイラのバージョンがまだ0.8.*だか、0.9.*の時代に書いたものなので、現在のコンパイラでコンパイルしたら、時代遅れ感が否めませんが、まあ参考になる箇所もあるでしょう。
まあ、解説と言っても、そのファイルが何をしているか概要を説明するだけの簡便なものです。人によっては煩わしく感じるかもしれませんが、お付き合いください。


  • Common.ts - IEnumeratorやDictionaryなど、.NET環境の実行環境で広く必要になる基本的なクラス群を独自定義しています。ただ、このソースを書いた時点のTypeScriptの制限で、ちょっと本物の.NET環境とは異なるメソッド定義になっている箇所があります。新しいTypeScript環境では、解消されているのかな
  • WebHelpers.ts - 見た目をコンソール状にする外部ライブラリ"jqconsole"のラッパと、同じ機能を持つクラスを独自定義しようとしているファイル。ただ、独自実装は、途中で力尽きてます( ;´Д`)
  • ErrorFactory.ts - 様々な種類の例外を投げる"例外ファクトリ"クラスを定義
  • Utils.ts - 全体で必要になる種々のユーティリティ関数群を定義
  • LispTypes.ts - LISPの実行環境となるクラス群を定義
  • Reader.ts - LISPのトークンを識別する文法解析器と"クォーサイクォート"と呼ばれる特殊形式の内部表現を行うクラスを定義
  • LispFunctions.ts - 基本的なLISP関数のネイティブ実装を定義
  • Interpreter.ts - 実際にLISPの処理を行うインタープリタ
  • Snippets.ts - LispFunctions.tsだけでは足りない、よく使われる関数やマクロをLISPとして定義し、文字列で保持するモジュール。load-sample関数で読み込めるS式もこの中に定義されてます
  • main.ts - インタープリタ自体のブートアップを行う処理が記載されている
以上が概要です。こんなほとんど中身のない記事ですが、参考に(?)していただけると光栄です。
今更、なんでこんな記事を書いたのかって怒る方もいらっしゃるかもしれませんね。その理由は、なんとなくとしかお答えできませんε-(´∀`; )

2016年9月7日水曜日

新技術と旧技術の融合

みなさん、ご無沙汰してます。はざまです。
今回は、以前何の目的で作ったかは忘れましたが、作成したLISP処理系の紹介です。といっても、実装内容自体は、完全に借用しているため、そのポーティング程度しか語ることはありません。
そのポーティング先となったのが、今やVisualStudioでも、正式にサポートされ、一線級で活躍していると思われる初期のTypeScriptです。まだ、コンパイラのバージョンが0.8.*だか、0.9.*時代に書いたものなので、今から見ると粗がある可能性も否めませんが、大筋は今の思想と合致しているはずなので、今回、紹介するに至った次第です。これを期に、今後、TypeScriptの記事を増やせていけたらいいな〜とか思ったり、思わなかったり。
Jsdo.itがTypeScriptに対応したとの話も聞くので、修正するなら参考実装もそれに合わせて変更する形になるでしょうかね。
そうそう、Web上で動くLISP実装としての活用もしていただけると、ありがたい限りです。

2016年8月3日水曜日

AppStoreに公開するのになかなか苦労した話

どうも、ご無沙汰になってしまいました。はざまです。
最近になって初めて、App Storeでのアプリ公開を経験し、これについて調べている過程で色々思うことがあったので、メモ代わりに記事を残しておこうと思います。

まず、私の場合、iOS Developer Programに登録するところでちょっとはまりました。本名がローマ字表記すると、2通りの表記ができる名前なので、アカウントの登録時と、Developer Programへの登録申請時に記載した名前の表記が合わなかった結果なのか、自動承認がなされず、サポートに問い合わせる結果となってしまいました。
もっとも、サポートの対応が非常に素晴らしく、1営業日中には返答があったため、特にストレスにはなりませんでした。
この部分に関しては、のちにiTunes Connectを使ってアプリの登録申請を行う際にまで尾を引きます。なので、iDPを申請する際には、アカウントと申請内容を統一しておくことをお勧めします。

そして、アプリ公開申請を行う上で、最大の注意点となるのが、iTunes Connectを使用した申請手続きです。頻繁にiTunes ConnectのUIが変わっているのか、検索で引っかかった手順と実際行った手順が一致したことはほぼありませんでした。こちらで、その手順を逐一記述しても、またすぐに変わってしまうでしょうから、手順の詳細を記述することはしません。
iTunes Connectの申請手順に関しては、手順をググるよりも、公式のドキュメントを参照した方がやりやすいかなと思いました。Appleの場合、ちゃんとドキュメントもローカライズされて日本語のものがリリースされているのもそう思った理由の一つです。

ちなみに、iTunes Connectでのアプリ審査申請から、承認までには1営業日しかかかりませんでした。承認されるまでに2週間もかかったというような記事も見受けられたので、だいぶ、そこら辺個人差があるのかもしれません。

2016年5月15日日曜日

クロージャの変数捕捉タイミングが変わってる!(JS編)

みなさん、こんにちは。また間が空いてしまいました。はざまです。
今回は久しぶりにJSの話題をしようと思います。

今日、昔書いたJSアプリの修正を行っていたのですが、以下のようなコードを書いてみて愕然としました。

jsdo.itの説明文にも書いた通り、コメント内の記法でChromeやSafariで評価を行うと、これまたコメント内のように、関数の実行順とリンクした結果が表示されたんですね。一昔前の挙動は、jsdo.it内のテストコードのように、順繰りにカウントアップされるというものだったんですが、いつからこのような挙動になったんでしょうか。
そもそも、jsdo.it内だと、ループ内にクロージャを定義できなくなっていること、Chrome、Safariともに同じ挙動だったことから、おそらく標準規格が変わったんだと思いますけど、今はそこまで追う余裕もないので、推測だけ記載しておきます。きっと、パフォーマンスの観点から、ループ内に無名関数を定義できなくした上に、既存のコードを吟味して下方互換性を保たなくても大丈夫という判断がなされたんでしょうけど、私個人としては、かなり衝撃的な仕様変更でした。……って、めちゃくちゃ個人的な理由ですね。
ワークアラウンドとしては、例にも示した通り、Function.prototype.bindを使うことです。クロージャを保持するための一時変数が必要になったり、thisにbindするわけでなければ、第1引数にnullを指定しなきゃいけなかったり、クロージャをさっと書けていた時代に比べると、ちょっと不格好なのは否めませんが、ループ最適化でパフォーマンスが向上するのなら、我慢しようといったところでしょうか。

6/7 追記: TypeScriptのマニュアルを読んでいて見つけた別のワークアラウンド。即時実行関数を挟むパターン。例に出している構文も酷似してるし、MS内に私のクローンがいるんでしょうか……

ではでは、またそのうちお会いしましょう^^/