何日までに○○しなさいというメールが届いた!けどほったらかしておいたら期限を過ぎていた!そういう経験、あるよね?
そこで、そういうメールが届いたら自動でMacの「リマインダー」に登録されるようにしよう!という話。
続きを読む
何日までに○○しなさいというメールが届いた!けどほったらかしておいたら期限を過ぎていた!そういう経験、あるよね?
そこで、そういうメールが届いたら自動でMacの「リマインダー」に登録されるようにしよう!という話。
続きを読む
ECMAScriptの変数のスコープが不思議な挙動をするのはよく知られた話だ。ECMAScript 5での変数のスコープは以下のいずれかに大別される(はず)。ブロックスコープがない。
普通に使う変数が関数スコープしかないのは不便、ということで、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で「有界な自然数の冪乗を計算する」finPower2 : Fin n -> Fin (power 2 n)
という関数を実装したが、いくつか後回しにした証明があった。今回はIdrisの証明支援機能を使ってこれを埋める。
【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がリリースされた。目玉機能としては Union Types とか ECMAScript 6 出力モードの搭載とかになるのだろうが、他にもいろいろ地味だが嬉しい機能追加などがあるようだった。
前にTypeScriptで組み込みオブジェクトを拡張できないというようなことを書いたが、そのとき感じた問題点が直っている。具体的には以下。
interface ほにゃららConstructor
というものに変更されている。よって、各種組み込みオブジェクトのコンストラクタに勝手なプロパティーを追加できる。あと地味に便利だと思ったのはコンパイラ tsc
に追加された --noEmitOnError
というオプションである。今まではソースコードが解釈さえできれば型チェックが通らなくても ECMAScript の出力が生成(!)されていて、Makefile で TypeScript のコードをコンパイルする時に不便だった。だが、このオプションがあれば型チェックが通ったコードしか出力されないので、Makefile との相性が上がる。
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をゲームパッドで操作したい。いわゆる「左手デバイス」とかいうの。まあ自分はそんなにお絵描きするわけじゃないんだけど。
ここの話はLinuxでの話で、WindowsとかOS Xとかは知らん。
1. メニューの「Edit > Preferences」を選択。
2. Preferences ダイアログの Input Devices > Input Controllers を選択
3. Available Controllers の Linux Input を選択し、→ボタンで Active Controllers に追加。
4. Configure Input Controller ダイアログが出てくるので、Deviceとして PC Game Controller を選択。
という感じでゲームパッドのボタンにGIMPの操作を割り当てることができる…と期待したがそうはいかなかった。ゲームパッドのボタンを押しても何も起こらない。Dump events…にチェックを入れてターミナルの出力を見ると、ボタンを押した事自体は認識されてはいるようだ。なぜだ。
しかたがないからGIMPのソースコードを見ていじくろう。GIMP Developer WikiのHacking: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のドライバと競合するのか知らないがなにかうまくいかない。