プログラミング」カテゴリーアーカイブ

Macの受信メールから自動でリマインダーを作成する

何日までに○○しなさいというメールが届いた!けどほったらかしておいたら期限を過ぎていた!そういう経験、あるよね?

library-mailそこで、そういうメールが届いたら自動でMacの「リマインダー」に登録されるようにしよう!という話。
続きを読む

TypeScript 1.5でのlet文

ECMAScriptの変数のスコープが不思議な挙動をするのはよく知られた話だ。ECMAScript 5での変数のスコープは以下のいずれかに大別される(はず)。ブロックスコープがない。

  • グローバルスコープ
  • 関数スコープ
  • catchスコープ

普通に使う変数が関数スコープしかないのは不便、ということで、MozillaはJavaScript 1.7(2006年頃)でlet文を導入した。が、しかし、他のブラウザには普及せず、Mozillaの独自拡張に留まることになった。

時は過ぎ、ECMAScript 5が制定され、次はECMAScript 6という流れになってきた。そして、ECMAScript 6にはlet文が導入されることになった(MozillaがJavaScript 1.7で導入したものとは微妙に仕様が違う)。めでたしめでたし。

ECMAScriptに静的型を付けたTypeScriptは、当初はlet文をサポートしていなかった。それが、TypeScript 1.4で、ECMAScript 6へコンパイルする場合のみlet文をサポートするようになった。

が、ECMAScript 6(のlet文)に対応していないブラウザはまだまだたくさんある(今調べてみたら、IE11ではES6のlet文に対応しているけど、Safariでは対応していないようだ)。できれば、TypeScriptのようなaltJS言語でlet文を使いつつ、コンパイル後のJavaScriptでは従来のvarを使ってほしい。

が、let文を従来のJavaScript (ECMAScript 5)に変換するのはそう自明なことではない。特に問題になるのは、ループ内の変数を、ループの外に持ち出される関数で使っている場合だ。[code lang=”js”]
var a = [];
for (var i = 0; i < 5; ++i) {
let x = i * i;
a.push(function() { return x; });
}
[/code]上のコードを下のように単純にvarを使うと挙動が変わってしまう。[code lang=”js”]
var a = [];
for (var i = 0; i < 5; ++i) {
var x = i * i;
a.push(function() { return x; });
}
[/code]このコードを正しくECMAScript 5に翻訳するには、即時実行関数(IIFEを使って以下のようにしなければならない。[code lang=”js”]
var a = [];
for (var i = 0; i < 5; ++i) {
(function() {
var x = i * i;
a.push(function() { return x; });
}());
}
[/code]こういう非自明、というかパフォーマンスに影響するところがあるので、TypeScript 1.4ではES5へのコンパイル時にlet文がサポートされなかったのだろう。

が、しかし、TypeScript 1.5ではES5へコンパイルする場合でもlet文が部分的にサポートされるようになるようだ。「部分的に」というのは、ループが絡まない、変数のスコープのチェックと名前の変更で済む場合、のようだ。[code lang=”js”]
{
let x = 123;
console.log(x); // OK
}
console.log(x); // エラー
[/code]
ループが絡む、IIFEを使った変換が必要な場合は、

test2.ts(2,1): error TS4091: Loop contains block-scoped variable 'x' referenced by a function in the loop. This is only supported in ECMAScript 6 or higher.

というエラーが出た。
[code lang=”js”]
let a: Array<() => number> = []
for (var i = 0; i < 5; ++i) {
let x = i*i;
a.push(() => x);
}
a.forEach(console.log);
[/code]

GitHubでの該当するIssue/Pull Requestは以下のようだ。

let文のサポートとしては不完全とはいえ、varの関数スコープのせいで意図しない同名の変数を参照していた〜〜みたいな事故は防げると思われるので、積極的に使っていきたい。

Idrisの有界な自然数で遊んでみる(1) — 証明をつける

前回:Idrisの有界な自然数で遊んでみる(0)

前回はIdrisで「有界な自然数の冪乗を計算する」finPower2 : Fin n -> Fin (power 2 n) という関数を実装したが、いくつか後回しにした証明があった。今回はIdrisの証明支援機能を使ってこれを埋める。

続きを読む

Idrisの有界な自然数で遊んでみる(0)

プログラミング言語Idris自体についての詳しい話はここではしない。このブログの昔の記事で軽く紹介したかもしれないが他人の参考になるかは分からない。

この記事を読むにあたっては、

  • ペアノ算術

は知ってないと厳しいと思われる。あとHaskell風の言語にも慣れていた方がいいか。

この記事で使うIdrisのバージョンは0.9.16である(執筆時点の最新版)。これと異なるバージョンだとコードの修正が必要かもしれない。

続きを読む

Union Typesは直和型ではない

【2017年12月18日 追記】この記事は古いTypeScript (2.0以前) を念頭に置いている。もちろん、現在のTypeScriptにも当てはまる記事はあるだろうし、TypeScript以外の言語における合併型 (union types) についてもある程度読み替えられるかもしれない。ただしElmとは “Union Types” の用法が完全に相入れないのでElmユーザーの方はお帰りください。


TypeScript 1.4について、 TypeScript 1.4.1 変更点 – Qiita という記事が目に留まった。で、その中の
直和型(Union Types)
という項目に引っかかりを感じた。:

なぜ引っかかりを感じたかというと、TypeScriptに今回導入されたUnion Typesと、巷に言う直和型というのは、異なる概念であるからだ。

注意:以下の話は型理論の専門家でもないフツーの学生が適当に書いた程度の信憑性しかありません。

続きを読む

TypeScript 1.4がいい感じ

TypeScript 1.4がリリースされた。目玉機能としては Union Types とか ECMAScript 6 出力モードの搭載とかになるのだろうが、他にもいろいろ地味だが嬉しい機能追加などがあるようだった。

前にTypeScriptで組み込みオブジェクトを拡張できないというようなことを書いたが、そのとき感じた問題点が直っている。具体的には以下。

  • 各種組み込みオブジェクトのコンストラクタの型が interface ほにゃららConstructor というものに変更されている。よって、各種組み込みオブジェクトのコンストラクタに勝手なプロパティーを追加できる。
  • ECMAScript 6 で追加される関数の型定義が追加されている。つまり、自前で組み込みオブジェクトの型定義をいじらなくていい。(ECMAScript 6 出力モードで使えるようになるようだ)

あと地味に便利だと思ったのはコンパイラ tsc に追加された --noEmitOnError というオプションである。今まではソースコードが解釈さえできれば型チェックが通らなくても ECMAScript の出力が生成(!)されていて、Makefile で TypeScript のコードをコンパイルする時に不便だった。だが、このオプションがあれば型チェックが通ったコードしか出力されないので、Makefile との相性が上がる。

Rustから直接C++を叩いてみる

プログラミング言語Rustのα版が出たので遊んでみようかなという。(と言ってやってることはRustである必要性が全然ないけど)

ここで使っている環境はOS X 10.9.5 (アーキテクチャはx86_64)で、C++コンパイラはClangである。ここでやっているのは激しく環境依存なのだが、まあでもコンパイラがGCCかClangなら多くの環境で上手くいくんじゃないかと思う(知らん)。

続きを読む

SVG pathの楕円を描くコマンドが使いにくい

SVGとはScalable Vector Graphicsの略で、2次元のベクター画像を記述するためのXMLベースの言語である。詳しくはググれ。

ベクター画像を構成する要素は、線分とか円とかベジエ曲線とかいろいろある。SVGでこれをどう表すかというと、例えば線分だとline要素を使い、円だとcircle要素を使う。SVGにはこの他にpath要素というものがあって、描画のコマンドを文字列で指定して複雑な曲線を描ける。線分とか円の弧のような単純なやつも描ける。

例を見てみると、

M 100 100 L 300 100 L 200 300 z

というコマンドを与えると、始点(100,100)から(300,100)に線分を描き、そこから続けて(200,300)に線分を描く。最後に、(200,300)から始点に線分を描いてパスを閉じる。この M とか L とか z とかいうのが描画コマンドで、それぞれ move to, line to, close path を意味する。2次元の描画を行うプログラムの経験がある人にはおなじみだろう。

描画コマンドには他にも2次あるいは3次のベジエ曲線を描くもの、楕円の弧を描くものがある。この楕円の弧を描くコマンド(A / a)が曲者で、楕円の指定方法が個性的だった。

このコマンドに与えるパラメーターは、楕円の形状に関する物が3つ、弧の終点(始点は current point を使うので、わざわざ指定する必要はない。指定したい場合は先に move to コマンドを実行する。)、そして、弧の端点と楕円の位置関係を指定するフラグが2つだ。楕円の中心は明示的に指定できない。

フラグが2つもある時点で嫌な予感がする。場合分けとか面倒くさそう。そんなことを感じながら、自前のSVGを出力するライブラリ(と言ったら大げさか)を実装・バグ取りしていたが、なぜか円全体が描けない。おかしい。と思ったが気が付いた。

始点と終点が一致していると、楕円の中心が一意に定まらない。つまり、SVG pathの A コマンドでは1回で楕円を描けない。SVGの楕円に類する物を描く描画コマンドは他にないのに…。

ググってみたら、Stack Overflowの質問を見つけた。
Circle drawing with SVG’s arc path – Stack Overflow
要するに、SVG pathで楕円全体を描くには、楕円の99.99%の弧を描いて満足するか、2個のコマンドで楕円を分割して描かないといけない。腐った仕様だ。

ちなみに、PostScriptの仕様をちらっと見た感じでは、PostScriptで円弧を描く arc コマンドは円の中心と半径、始点と終点の角度を指定する仕様だった。HTML5 Canvasの2D Contextも同様だ。(これらには楕円を描くコマンドはないが)

GIMPとゲームパッド on Linux

フリーのペイントソフト、GIMPをゲームパッドで操作したい。いわゆる「左手デバイス」とかいうの。まあ自分はそんなにお絵描きするわけじゃないんだけど。

ここの話はLinuxでの話で、WindowsとかOS Xとかは知らん。

手持ちのハードウエア

  • パソコン(自作のLinux機)
  • USBゲームパッド(エレコムのやつ)
  • ペンタブ(Wacom Bamboo CTH-661)(今回の主役ではない)PA250017

試した手順

1. メニューの「Edit > Preferences」を選択。

GIMP: Edit > Preferences

2. Preferences ダイアログの Input Devices > Input Controllers を選択

GIMP: Preferences > Input Controllers

3. Available Controllers の Linux Input を選択し、→ボタンで Active Controllers に追加。

GIMP: Configure Input Controller

4. Configure Input Controller ダイアログが出てくるので、Deviceとして PC Game Controller を選択。

GIMP: Configure Input Controller (PC Game Controller)これで、

  1. Grab eventをクリック
  2. ゲームパッドのボタンを押す
  3. Actionを割り当て

という感じでゲームパッドのボタンにGIMPの操作を割り当てることができる…と期待したがそうはいかなかった。ゲームパッドのボタンを押しても何も起こらない。Dump events…にチェックを入れてターミナルの出力を見ると、ボタンを押した事自体は認識されてはいるようだ。なぜだ。

GIMPのソースコードをいじる

しかたがないからGIMPのソースコードを見ていじくろう。GIMP Developer WikiHacking:Building/Linuxを参考にして、ソースコードをgitでチェックアウトする。

Linux Inputに対応するソースはどこだろう。gimp/modules/ が怪しい。見てみると、gimp/modules/controller-linux-input.c というソースファイルがある。これか。

ファイルの頭の方に static const LinuxInputEvent key_events[] という変数の定義がある。Configure Input Controllerダイアログに出てきたボタンの名前はこれか。ゲームパッドのキーを押したときにターミナルに出てくるログと、<linux/input.h> で定義されている定数の値を参考にしながら、BTN_*** に対応する定義を追加する。

差分としてはこんな感じになった。

diff --git a/modules/controller-linux-input.c b/modules/controller-linux-input.c
index 8759d10..de72332 100644
--- a/modules/controller-linux-input.c
+++ b/modules/controller-linux-input.c
@@ -81,8 +81,20 @@ static const LinuxInputEvent key_events[] =
{ BTN_GEAR_DOWN, "button-gear-down", N_("Button Gear Down") },
#endif
#ifdef BTN_GEAR_UP
-  { BTN_GEAR_UP,   "button-gear-up",   N_("Button Gear Up")   }
+  { BTN_GEAR_UP,   "button-gear-up",   N_("Button Gear Up")   },
#endif
+  { BTN_TRIGGER,   "button-trigger",   N_("Button Trigger")   },
+  { BTN_THUMB,     "button-thumb",     N_("Button Thumb")     },
+  { BTN_THUMB2,    "button-thumb2",    N_("Button Thumb 2")   },
+  { BTN_TOP,       "button-top",       N_("Button Top")       },
+  { BTN_TOP2,      "button-top2",      N_("Button Top 2")     },
+  { BTN_PINKIE,    "button-pinkie",    N_("Button Pinkie")    },
+  { BTN_BASE,      "button-base",      N_("Button Base")      },
+  { BTN_BASE2,     "button-base2",     N_("Button Base 2")    },
+  { BTN_BASE3,     "button-base3",     N_("Button Base 3")    },
+  { BTN_BASE4,     "button-base4",     N_("Button Base 4")    },
+  { BTN_BASE5,     "button-base5",     N_("Button Base 5")    },
+  { BTN_BASE6,     "button-base6",     N_("Button Base 6")    },
};

static const LinuxInputEvent rel_events[] =

あとは、さっきのWikiの手順にしたがってGIMPをビルドする。すると、さっきスクリーンショット付きで説明した手順で、ゲームパッドのボタンにGIMPの操作を割り当てることができる。

とりあえず気づいた点は、ボタンを押すときと離すときで2回操作が実行されるのと、ゲームパッドのボタンじゃなくて倒すやつには反応しないこと。既存のソースコードの見よう見まねじゃなくて、ちゃんと調べてコードを書かないといけない気がする。そもそもこのLinux Inputというやつはどのぐらい使われているんだろうか。これは意図した動作なのだろうか。

あと、自分の手持ちのゲームパッド1個で試したが、他のゲームパッドではどうなるのか。この辺の問題が解決したらパッチを投げるかもしれない。その前に開発コミュニティーの雰囲気とかつかんでおきたいが。パッチを投稿するにはBugzillaにBug(?)を登録してそれに添付する感じだろうか。この辺の手間を思うに、GitHubのPull Requestという仕組みは偉大だ。

ペンタブのパッド(ペンタブの感知するエリアの横についてるボタン)に対する操作も、Linux Inputで扱えそうな雰囲気を感じたが、Wacomのドライバと競合するのか知らないがなにかうまくいかない。