ラベル JavaScript の投稿を表示しています。 すべての投稿を表示
ラベル JavaScript の投稿を表示しています。 すべての投稿を表示

2015年3月8日日曜日

Firefox で警告「応答のないスクリプト」ダイアログが邪魔

Firefox で、Google ドキュメントInoreader のようなフィードリーダーなど、JavaScript を多用しているサイトを利用すると、「応答のないスクリプト」という警告ダイアログが表示されることがある。

SnapCrab_No-1631

SnapCrab_No-1625

一々ボタンを押すのは面堂なので、このようなダイアログがなるべく表示されないようにしたい。

そのために、about:config

  • dom.max_script_run_time
  • dom.max_chrome_script_run_time

の値をデフォルトの 2 ~ 3 倍くらいに設定しておいた。

ただし、この設定値は、JavaScript が長時間実行されることにより、UI が反応しなくなることを防ぐためにあるので、あまり大きな値を入れない方が良さげ。

Dom.max script run time - MozillaZine Knowledge Base

… When a script is executing, Mozilla's UI will be unresponsive until the script’s thread ends. Correspondingly, Mozilla will alert you when a script is taking a long time to run and let you stop the script. This preference lets you define what “a long time” is.

Dom.max chrome script run time - MozillaZine Knowledge Base

… a check is built in to allow long-running scripts to be aborted. That check is now able to differentiate between scripts running in web pages (content) and scripts running in the application’s chrome. This preference determines how long scripts running from chrome are allowed to run before the user can choose to abort the script.

2015年2月19日木曜日

Firefox で Flash Player からできるだけ足を洗う - Flashblock

Flash Player が原因で Firefox (Cyberfox) が固まったり、落ちたりすることがある。(+_+)

YouTube、メイン再生プレーヤーをFlashからHTML5にようやく移行 - ITmedia ニュース (2015年01月28日) によると、

GoogleのChrome、米MicrosoftのInternet Explorer(IE)11、米AppleのSafari 8、米MozillaのFirefoxのβ版でのYouTube動画はHTML5ベースのプレーヤーで再生されるようになる。

SnapCrab_No-13462Firefox もそろそろ対象になりそうな感じがしている。

YouTube の動画は内蔵プレーヤーで HTML5 により再生するアドオンを利用することにした。

SnapCrab_No-1351これを機に、Flash Player から足を洗っても良い頃かな?

ということで、今まで敬遠してきた Flashblock を利用することにした。

  • オプション > 一般

において、全てチェックを付けておいた。

これに対して、JavaScript の方はどうすれば良いか思案中。。

cf. 高木浩光@自宅☆の日記 - 「NoScript」をやめて「RequestPolicy」にした

 

関連記事

2014年9月9日火曜日

Google 検索結果が昔に戻った - 期間指定しやすいインターフェイス

1. 以前の表示に変った

何かの拍子に、Google の検索結果の表示が変ってしまった。(@_@;

SnapCrab_No-1440

昔、このようなインターフェイスだった。

タイトルの文字は小さく、左側には「期間指定」のリンクが表示され、情報の鮮度で抽出がしやすい。

 

2. 表示を戻す

表示を元に戻すためには、検索 URL の末尾に

&gbv=2

を付け加え、エンターキーを押す。

SnapCrab_No-1439

一度、検索をすると、それ以降同じ表示となる。

また元の表示に戻すには、検索 URL の末尾に

&gbv=1

を付けて、エンターキーを押す。

 

3. JavaSctipt をオン・オフ

このように表示が変化する理由は、検索のパラメータの一つである `gbv’ が JavaScript のコントロールを指示しているためのようだ。

What is the porpose of the Google search parameter "gbv"? - Stack Overflow によると、

The gbv parameter is to control the presence of javascript on the page, 1=no javascript, 2=javascript.

ブラウザの JavaScript を無効にした場合、&gbv=1 を URL に加えたときと同じ表示になる。

恐らく自分の場合、Google 検索で JavaScript を一度無効にしたため、上記のような表示になったのだろう。

 

関連記事

2013年6月25日火曜日

Firefox の KeySnail で設定したホットキーをマウスジェスチャーから呼び出す

1. ホットキーをマウスジェスチャーから呼び出す

SnapCrab_No-0310前回、KeySnail で「最小フォントサイズ」を変更するホットキーを設定した。ホットキーとして割り当てたのは、

Alt + 0

今回は、マウスジェスチャーを利用するためののアドオン FireGestures から、KeySnail で設定したキーバインドを呼び出したい。

 

2. FireGestures のスクリプトでキー入力を行う

  • Firefox ボタン > アドオン > FireGestures の設定ボタンを押す

「マッピング」タブを選択し、画面下部にある「スクリプトを追加」ボタンを押すと、編集ダイアログが表示される。

「名前」フィールドを「最小フォントサイズを変更」とした。

SnapCrab_No-0311

「スクリプト」欄には、以下を貼り付ける。

var event = document.createEvent("KeyEvents");
event.initKeyEvent("keypress",
		   true,
		   true,
		   null,
		   false,  // holds Ctrl key
		   true,   // holds Alt key
		   false,  // holds Shift key
		   false,  // holds Meta key
		   0,      // presses a special key   , @see http://mxr.mozilla.org/mozilla/source/dom/public/idl/events/nsIDOMKeyEvent.idl
		   event.DOM_VK_0  // presses a normal key, e.g. "A".charCodeAt(0),
		   );
document.documentElement.dispatchEvent(event);

ジェスチャーは RL とした。これは自分の環境では、マウスジェスチャー R に対して「表示の拡大」、L を「表示のリセット」に割り当てているため、類似した動作にした。

 

3. スクリプトについて

上記のスクリプトは、以下を参考にした。

キーイベントを初期化するためのメソッドの解説は、

 

関連記事

2013年6月24日月曜日

Firefox のアドオン KeySnail で「最小フォントサイズ」を変更するホットキーを設定する

1. Firefox で「最小フォントサイズ」を指定すると表示が崩れる

SnapCrab_No-0291Firefox 21 で Google ドライブのスプレッドシートを開くと、表示が崩れる。「行番号」が大きくなるに連れ、隣のセルが徐々にズレていく。

このような表示になった原因は、「最小フォントサイズ」を指定していたため。

  • オプション > コンテンツ > フォントと配色 > 詳細設定

において、「最小フォントサイズ」を「なし」に設定すると、正常に表示される。

最近では、Feedly のバージョン 16.0.500 でも同じような問題が生じていた。

だたし、Google Chrome で「最小フォントサイズ」を指定しても、どちらのアプリケーションとも問題なく表示される。

 

2. KeySnail で最小フォントサイズを変更するホットキーを設定する

Firefox で「最小フォントサイズ」を指定するには、いくつか手順を踏む必要がある。「最小フォントサイズ」を指定したり、「なし」に設定する操作に対してホットキーを付けたい。

SnapCrab_No-0305keysnail は特定の操作に対してキーを割り当てることができるアドオン。このアドオンを用いて、「最小フォントサイズ」を切り替えるホットキーを設定する。

最初に、KeySnail のインストールと初期設定を行う。

次に、「最小フォントサイズ」を設定する操作に対して、ホットキーを設定する。

  • アドオンバーにある KeySnail アイコンを右クリック > 設定ダイアログを開く

キーバインド」タブを選択し、下部に表示される「追加」ボタンを押し、「オリジナルのコマンド」を選択する。

SnapCrab_No-0306

  1. コマンド名」を「最小フォントサイズを変更する」とした。
  2. 今回は、Alt + 0 をホットキーとして割り当てる。「キー」フィールドで同キーを入力する。
  3. 「関数」の欄には、以下のコードを挿入して、OK ボタンを押す。
function (ev, arg) {
    const MIN_FONT_SIZE = 14, // 最小フォントサイズ
	CONFIG_PREFIX = "font.minimum-size.",
	COUNTRY_CODES = ["ar", "el", "he", "ja", "ko" , "th", "tr",
			 "x-armn", "x-baltic", "x-beng", "x-cans", "x-central-euro",
			 "x-cyrillic", "x-devanagari", "x-ethi", "x-geor", "x-gujr",
			 "x-guru", "x-khmr", "x-knda", "x-mlym", "x-orya", "x-sinh",
			 "x-tamil", "x-telu", "x-tibt",
			 "x-unicode", "x-user-def", "x-western",
			 "zh-CN", "zh-HK", "zh-TW" ];
    var minFontSizeNow = util.getIntPref(CONFIG_PREFIX + "ja"),
	setMinFontSize = function(fontSize){
	COUNTRY_CODES.forEach(function(countryCode){
		util.setIntPref(CONFIG_PREFIX + countryCode, fontSize);
	    });
    };
    minFontSizeNow == MIN_FONT_SIZE ?
	setMinFontSize(0) :
	setMinFontSize(MIN_FONT_SIZE);
}

ここでは「最小フォントサイズ」を 14 とした。もし、別の大きさに設定したい場合、この値を変更すれば良い。

 

3. 関数について

about:config で最小フォントサイズを変更

最小フォントサイズは、abuot:config の設定において、

  • font.minimum-size.

により設定名を抽出し、各々の値を変更した。

 

about:config の設定値を KeySnail で変更

KeySnail で、about:config の値を変更するには、

KeySnail 0.5.2 をリリースしました - mooz deceives you > お手軽 about:config によると、

util.setPrefs() メソッドを追加しました。このメソッドを使うと about:config の値を .keysnail.js 内で簡単に設定することが出来ます。

util.js · mooz/keysnail Wiki · GitHub に util のドキュメントが存在する。ただし、全ての関数の解説があるわけではない。

より、getIntPref(), setIntPref() 関数が定義されていたので、これを利用して about:config の値を設定した。

2012年6月21日木曜日

Google Chrome でリンクを新しいタブで開く - Tab Position Customizer

1. JavaScript によるポップアップをさせたくない

インターネットラジオを聴くサイトとして、TuneIn を利用している。いつも聴く番組を登録し、分類できるところが良い。

SnapCrab_NoName_2012-6-20_23-48-54_No-00ただし、番組を「聴く」ボタンを押すと、小さなウィンドウがポップアップし、そこに音量や再生・停止ボタンがある点が使いにくい。

ポップアップしたウィンドウが、他のアプリケーションの下に隠れてしまい、アクティブにするためにウィンドウを探すのが面倒。 (+_+)できることなら、ポッポアップせずに新しいタブに開くか、現在のタブに表示してほしい。

 

2. Ctrl + クリックで新しいタブに開く

Google Chrome では、リンクを

Ctrl + クリック

することにより、リンク先を新しいタブに開いてくれる。

上記の「聴く」ボタンは、JavaScript により制御されている。一度、Ctrl + クリックにより、ポップアップしていたウィンドウをタブに開くと、次から別の番組をクリックした場合、同タブに開いてくれる。

 

3. Tab Position Customizer でタブの動作をカスタマイズ

リンクをクリックしたときに、開かれるタブの動作をカスタマイズしたい場合、Tab Position Customizer を使う。

SnapCrab_NoName_2012-6-20_23-59-42_No-00インストールしたら、

  • Google Chrome の設定 > ツール > 拡張機能

より、Tab Position Customizer のオプションを選択。

上記の対策には、JavaScript によるウィンドウのポップアップをコントロールすれば良いので、

  • その他 > ポップアップをタブとして開く

にチェックを入れる。

これに加え、タブを開く・閉じる動作を、Firefox の Tab Mix Plus を利用した設定にできるだけ近づけたい。

以下の設定をした。

  1. 新しいタブを開いた場合、「カレントタブの右」に開く。
  2. タブを閉じたら、「アクティブにした順」に沿ってタブのフォーカスを移す。

 

4. Firefox の Tab Mix Plus で JavaScript によるポップアップの設定

Firefox の場合、JavaScript によるポップアップを新しいタブに開くには、Tab Mix Plus を利用する。

Tab Mix Plus のオプションより、

  • リンク > JavaScript のポップアップの設定:

において、「すべてのポップアップをタブに開く」を選択する。

SnapCrab_NoName_2012-6-20_22-48-30_No-00

2012年4月17日火曜日

JavaScript 1.7 の配列内包

1. Python, Haskell のリスト内包表記

Python で、リスト内包表記を使い、リストの要素を2倍する。

>>> [x*2 for x in range(1, 6)]
[2, 4, 6, 8, 10]

Haskell も、リスト内包表記を使える。

Prelude> [x*2 | x <- [1..5]]
[2,4,6,8,10]

 

2. JavaScript の配列内包

JavaScript 1.7 でも、上記と同じような 配列内包 を使える。

Firefox で Firefox ボタン > Web 開発 > スクラッチパッドを起動し、以下を実行。(cf. Tools - MDN

[x*2 for each (x in [1,2,3,4,5])]
/*
2,4,6,8,10
*/

ただし、配列内包を使えるブラウザは限られている。

JavaScript tests, compatibility tables and examples

for each の each を忘れると、結果が異なる。

[x*2 for (x in [1,2,3,4,5])]/*
0,2,4,6,8
*/

これは、配列の添え字を取り出しているからかな。

JavaScript の配列内包が、Python のリスト内包表記に似ているのは、Python から取り入れられたため。

python - What is cross browser support for JavaScript 1.7's new features? Specifically array comprehensions and the "let" statement - Stack Overflow

According to wikipedia, JavaScript 1.7 implements ECMAScript "Edition 3 plus all JavaScript 1.6 enhancements, plus Pythonic generators and array comprehensions ([a*a for (a in iter)]), block scope with let, destructuring assignment (var [a,b]=[1,2])". So these features are not part of ECMAScript.

Google Chrome には、いつ取り入れられるのかな?

 

関連記事

関連サイト

2012年3月13日火曜日

レキシカルスコープとダイナミックスコープ

1. レキシカルスコープとダイナミックスコープの違い

言語によって、変数のスコープに関する仕様が異なる。スコープには、レキシカルスコープとダイナミックスコープがある。採用しているスコープにより、変数の参照の仕方が違う。

レキシカルスコープでは、プログラムとして書かれた字句を解析すれば、変数のスコープを把握できる。実行時のことは考えなくて良い。これに対して、ダイナミックスコープでは、実行時における関数の呼び出され方により、参照できる変数が異なる。

用語の説明を見る前に、具体例を見た方が理解しやすい。

Scope (computer science) - WikipediaLexical scoping and dynamic scoping によると、

… if function f invokes a separately-defined function g, then under lexical scoping, function g does not have access to f's local variables (since the text of g is not inside the text of f), while under dynamic scoping, function g does have access to f's local variables (since the invocation of g is inside the invocation of f).

(同上より、装飾は引用者による)

関数 f から関数 g を呼び出す場合を想定する。関数は、各々独立に定義されており、一方の関数がもう一方をネストしてないとする。

関数 f で定義されているローカル変数を、関数 g から、

  1. レキシカルスコープでは、参照できない。
  2. ダイナミックスコープでは、参照できる。

img_0058

 

各言語が採用しているスコープ

言語によって、採用しているスコープが異なる。もしくは、両方使える。

Lexical scoping によると、

Lexical scoping is standard in all ALGOL-based languages such as Pascal, Modula2 and Ada as well as in modern functional languages such as ML and Haskell.

Dynamic scoping

Some languages, like Perl and Common Lisp, allow the programmer to choose static or dynamic scoping when defining or redefining a variable. Logo and Emacs lisp are examples of languages that use dynamic scoping.

多くの言語で、レキシカルスコープが採用されている。ダイナミックスコープを採用している言語として、Emacs Lisp がある。

 

レキシカルスコープの例

Scope (computer science) - Wikipedia に書かれていた例を、各言語で試してみる。

Python

x = 7
def g(): return x
def f(): 
    x = 9
    return g()
print f()           #=> 7

javascript

var x = 7;
function g(){ return x; }
function f(){ 
    var x = 9;
    return g();
}
f();                //=> 7

Haskell

x = 7
g = x
f = let x = 9 in g
main = print f      #=> 7

scheme

(define x 7)
(define g (lambda () x))
(define f
  (lambda ()
    (let ((x 1))
      (g))))
(f)   ;=> 7

関数 f が呼び出された後に、関数 g が呼び出されても、関数 g の中にある変数の参照先は変わらない。

レキシカルスコープの実行結果を見ると、「コレ普通じゃない?」と感じる。理由は、

Dynamic scoping によると、

dynamic scoping can be dangerous and few modern languages use it.

ダイナミックスコープは取り扱いが難しいので、モダンな言語でほとんど採用されていない。そのため、レキシカルスコープの考え方の方が馴染みがある。

 

ダイナミックスコープの例

続いて、ダイナミックスコープを採用している Emacs Lisp の例。

Emacs Lisp

(setq x 7)
(defun g () x)
(defun f ()
  (let ((x 9))
    (g))
(message "%d" (f))  ;=> 9

レキシカルスコープの言語に慣れていると、ダイナミックスコープの結果に違和感を感じる。

関数 f を呼び出した後、関数 f の中で関数 g が呼び出される。これにより、関数 g の中にある変数の参照先が変わってしまう。

もし、関数 f が呼び出されず、関数 g だけが実行されたら、

(setq x 7)
(defun g () x)
(message "%d" (g))  ;=> 7

関数の呼び出され方により、参照できる変数が変わる。実行時のことを考える必要なければならない。そのため、コード全体を見通すことが大変そう。 (+_+)

では、ダイナミックスコープにメリットはあるのだろうか?

Emacs Lisp - Wikipedia によると

Emacs Lispは、アプリケーション・プログラミングで使われる方言群であるSchemeCommon Lispとは根本的に異なる。大きな違いの1つは、デフォルトで字句的スコープではなく動的スコープを使うことである。つまり、呼出し関数の局所変数は、ポインタや参照を渡さなくとも、呼び出された関数から参照できる。

関数から呼び出された側の関数は、自分のローカルにある変数の内容が、呼び出された文脈により変化する。

レキシカルスコープ的な視点で考えると、呼び出された側の関数は、呼び出した側の関数にネストされたかのように見える。

ところで、なぜ Emacs Lisp は、ダイナミックスコープを採用したのだろう?

Scope (computer science) - Wikipedia, the free encyclopedia によると、

Correct implementation of static scope in languages with first-class nested functions is not trivial, as it requires each function value to carry with it a record of the values of the variables that it depends on (the pair of the function and this environment is called a closure).

Dynamic scoping is fairly easy to implement. To find an identifier's value, the program could traverse the runtime stack, checking each activation record (each function's stack frame) for a value for the identifier.

レキシカルスコープより、ダイナミックスコープの方が実装が簡単とのこと。

Scope - GNU Emacs Lisp Reference Manual にも、同様のことが書かれている。

Emacs Lisp uses dynamic scoping because simple implementations of lexical scoping are slow. In addition, every Lisp system needs to offer dynamic scoping at least as an option; if lexical scoping is the norm, there must be a way to specify dynamic scoping instead for a particular variable. It might not be a bad thing for Emacs to offer both, but implementing it with dynamic scoping only was much easier.

 

2. レキシカルスコープとダイナミックスコープにおける変数の生存期間

書かれたテキストにより決まるレキシカルスコープ

Scope (computer science) - WikipediaLexical scoping and dynamic scoping によると、

In lexical scoping (…), if a variable-name's scope is a certain function, then its scope is the program text of the function definition: within that text, the variable-name exists, and is bound to its variable, but outside that text, the variable-name does not exist.

レキシカルスコープでは、関数の中で定義されてる変数のスコープは、プログラムの字句によって決まる。関数の中に書かれている変数は、その関数が記述されている内側で存在するが、外側では存在しない。

 

実行時に決まるダイナミックスコープ

Scope (computer science) - WikipediaLexical scoping and dynamic scoping によると、

By contrast, in dynamic scoping (or dynamic scope), if a variable-name's scope is a certain function, then its scope is the time-period during which the function is executing: while the function is running, the variable-name exists, and is bound to its variable, but after the function returns, the variable-name does not exist. (同上より)

ダイナミックスコープでは、関数の中で定義されてる変数のスコープは、その関数が実行されている期間と同じ。関数が実行されている間、変数は存在するが、関数の実行が終了すると変数は消える。

 

3. 実装から見る、名前の解決

実装の難しさ

レキシカルスコープとダイナミックスコープを実装する難しさについて、以下のように述べられている。

Lexical scoping

Correct implementation of static scope in languages with first-class nested functions is not trivial, as it requires each function value to carry with it a record of the values of the variables that it depends on (the pair of the function and this environment is called a closure).

レキシカルスコープを実装することが難しいのは、関数に対して、クロージャと呼ばれる、その関数が依存する変数(環境)を持って回る必要があるため。

Dynamic scoping

Dynamic scoping is fairly easy to implement. To find an identifier's value, the program could traverse the runtime stack, checking each activation record (each function's stack frame) for a value for the identifier. In practice, this is made more efficient via the use of an association list, which is a stack of name/value pairs. Pairs are pushed onto this stack whenever declarations are made, and popped whenever variables go out of scope.[1]

ダイナミックスコープの実装が簡単なのは、実行中の関数に関する情報を持つスタックの中から、参照したい値を探すだけで済むため。

スタックとは、Call stack - Wikipedia によると、

Since the call stack is organized as a stack, the caller pushes the return address onto the stack, and the called subroutine, when it finishes, pops the return address off the call stack and transfers control to that address. If a called subroutine calls on to yet another subroutine, it will push another return address onto the call stack, and so on, with the information stacking up and unstacking as the program dictates.

サブルーチンを呼ぶときに、利用されるデータ構造。サブルーチンが呼び出されるとき、スタックにリターンアドレスが積まれ、呼び出されたサブルーチンが終了すると、リターンアドレスが取り出され、そのアドレスに制御が移される。

プログラムを実行するときの、メモリの利用のされ方と名称について、詳しくは、 Data segment - WikipediaProgram memory を参照。

The computer program memory is organized into the following:

 

名前(識別子)の探し方

変数を参照するとき、レキシカルスコープとダイナミックスコープでは、以下の点が異なる。

Lexical scoping によると、

With lexical scope, a name always refers to its (more or less) local lexical environment. This is a property of the program text and is made independent of the runtime call stack by the language implementation. Because this matching only requires analysis of the static program text, this type of scoping is also called static scoping. …

Static scoping allows the programmer to reason about object references such as parameters, variables, constants, types, functions, etc. as simple name substitutions. This makes it much easier to make modular code and reason about it, since the local naming structure can be understood in isolation.

レキシカルスコープでは、「名前」が指し示す対象は、プログラムの字句を見れば分かる。実行時のことを考える必要ない。そのため、プログラムのテキストにおいて、参照しているものの名前を置き換えるだけで、何を指し示しているか把握できる。

Dynamic scoping によると、

With dynamic scope, each identifier has a global stack of bindings. Introducing a local variable with name x pushes a binding onto the global x stack (…), which is popped off when the control flow leaves the scope. Evaluating x in any context always yields the top binding. In other words, a global identifier refers to the identifier associated with the most recent environment. Note that this cannot be done at compile-time because the binding stack only exists at run-time, which is why this type of scoping is called dynamic scoping.

ダイナミックスコープでは、実行時に変数のような識別子があると、スタックに積まれる。その後、識別子が必要になったら、スタックに積まれている順に検索される。つまり、実行中の関数から、一番近い場所にある環境から、対象を見つけ出そうとする。

先ほどの例で考えると、

(setq x 7)
(defun g () x)
(defun f ()
  (let ((x 9))
    (g))
(message "%d" (f))  ;=> 9
  1. 変数 x は 7 に束縛され、スタックに積まれる。
  2. 関数 f の呼び出しにより、変数 x は 9 に束縛され、スタックに積まれる。
  3. 関数 g が呼び出され、変数 x が参照される。このとき、直前にスタックに積んだ、中身が 9 の変数 x が取り出される。

これにより、結果が 9 となる。

2012年2月21日火曜日

Ruby のネストしたメソッドと、変数のスコープ

0. 目次

  1. JavaScript, Haskell, Python でネストした関数を定義する
  2. Ruby でネストしたメソッドは定義できるが、変数のスコープに注意
  3. 内部スコープから、外部スコープを参照できない
  4. トップレベルに定義したメソッドの所属先は Object
  5. ネストしたメソッドの所属先は、外側のメソッドと同じクラス

 

1. JavaScript, Haskell, Python でネストした関数を定義する

2 つの値を足し合わせる関数を定義したい。

a. JavaScript

JavaScript で書くなら、

function sum(x, y){ return x + y; };
sum(1, 2);    //=> 3

JavaScript では、ネストした関数を定義できる。

JavaScript Reference - MDN入れ子の関数とクロージャ によると、

関数の内部に関数を入れ子にする事ができます。入れ子にされた (内側の) 関数は、それを含む (外側の) 関数に対してプライベートです。それは同時に クロージャ を形成します。

よって、次のように sum 関数を書くことができる。

function sum(x){ 
    function _sum(y){
	return x + y;
    };
    return _sum;
};
sum(1)(2);    //=> 3

sum 関数を利用するには、関数の呼び出しを 2 回行い、一つづつ引数を与える。

上記の sum 関数の定義は、最初に定義した関数をカリー化したもの。

カリー化 - Wikipedia とは、

複数の引数をとる関数を、引数が「もとの関数の最初の引数」で戻り値が「もとの関数の残りの引数を取り結果を返す関数」であるような関数にすること。

次に、ネストした関数を無名関数に変更。JavaScript で無形関数を定義するには、関数定義から名前を削除すれば良い。

JavaScript Reference - MDN関数を定義する によると、

function name([param[, param[, ... param]]]) {
   statements
}
name
関数名。省略する事ができ、その場合関数は無名関数と見なされます。

関数を定義するとき、`function’ と書くのは長くて面倒。しかし、無名関数の作り方は、素直でわかりやすい。

function sum(x){
    return function(y){
	return x + y;
    }
}
sum(1)(2);   //=> 3

sum 関数を、全て無名関数で定義して、実行するには、

(function(x){
    return function(y){
	return x + y;
    }
})(1)(2);     //=> 3

 

b. Haskell

Haskell で sum 関数を定義するなら、

sum' x y = x + y
main = print $ sum' 1 2   -- 3

Haskell で関数をネストするには、let 式か、where 節を用いる。

sum' x = sum''
    where
      sum'' y = x + y

無名関数の書き方は、The Haskell 98 Language Report3.3 カリー化された適用とラムダ抽象 によると、

ラムダ抽象は \ p1 ... pn -> e と書く。ここで、piパターンである。

JavaScript のように、関数定義から名前を削除したら、無名関数になる訳ではない。最初に書いた sum’ 関数の定義は、

と呼ばれる。無名関数を使った書き方と、同等であることが保証されている。

ネストしている sum’’  関数を、無名関数に変更すると、

sum' x = \y -> x + y

引数 2 つの定義よりも、上記の方が、Haskell では本質的な形。

理由は、Haskell - Wikipedia によると、

Haskell において、2つの引数を持つ関数は、1つの引数をとり「1つの引数をとる関数」を返す関数と同義である。…

f x = ... x ... は、 f = (\x -> ... x ... )シンタックスシュガーであるとも言える。

Currying - HaskellWiki

In Haskell, all functions are considered curried: That is, all functions in Haskell take just single arguments.

このため、引数 2 つの関数束縛と、カリー化した定義は、関数を適用するとき、同じように書ける。この点、Haskell の記述はシンプルで使いやすい。

ところで、Haskell - Wikipedia によると、

Haskell の定義は変数に束縛するのが定数であるか関数であるかにかかわらず、「変数 = 値」という一貫した形でも定義できる。

JavaScript でも、関数を定義するときに、無名関数を変数に代入する形式で書くことができる。

var sum = function(x, y){ return x + y; };

Haskell で、sum 関数を全て無名関数で定義し、関数を適用するには、

main = print $ (\x -> \y -> x + y) 1 2

 

c. Python

Python でも、sum 関数を定義してみる。

def sum(x, y):
    return x + y
print sum(1, 2)    #=> 3

Python も、ネストした関数を定義できる。

実行モデル — Python 2.6ja2 documentation によると、

プログラムテキスト中に名前が出現するたびに、その名前が使われている最も内側の関数ブロック中で作成された 束縛 (binding) を使って名前の参照が行われます。

ブロック(block)は、Python のプログラムテキストからなる断片で、一つの実行単位となるものです。モジュール、関数本体、そしてクラス定義はブロックです。…

ある名前がコードブロック内で使われると、その名前を最も近傍から囲うようなスコープ (最内スコープ: nearest enclosing scope) を使って束縛の解決を行います。こうしたスコープからなる、あるコードブロック内で参照できるスコープ全ての集合は、ブロックの環境(environment)と呼ばれます。

ある名前がブロック内で束縛されている場合、名前はそのブロックにおけるローカル変数 (local variable) です。ある名前がモジュールレベルで束縛されている場合、名前はグローバル変数 (global variable) です。 (モジュールコードブロックの変数は、ローカル変数でもあるし、グローバル変数でもあります。) ある変数がコードブロック内で使われているが、そのブロックでは定義されていない場合、変数は自由変数(free variable)です。

(装飾は引用者による、以下同じ)

def sum(x):
    def _sum(y):
        return x + y
    return _sum

sum 関数を呼び出すには、引数を一つづつ渡す。JavaScript と同じ。

print sum(1)(2)    #=> 3

ただし、無名関数を定義するとき、JavaScript のように関数から名前を削除する、という書き方はできない。よって、以下はエラーとなる。

def sum(x):
    return def(y):
        return x + y

無名関数を書くには、ラムダ形式を利用する。

def sum(x):
    return lambda y: x + y

関数の呼び出し方は、ネストした関数と同じ。

全てラムダ形式で定義し、実行するには、

print (lambda x: lambda y: x + y)(1)(2)

 

2. Ruby でネストしたメソッドは定義できるが、変数のスコープに注意

Rubyでも、同様に sum メソッドを定義してみる。

def sum(x, y)
  x + y
end
p sum(1, 2)    #=> 3

他の言語と同じように、ネストしたメソッドを定義することができるだろうか?

def sum(x)
  def _sum(y)
    x + y
  end
  _sum
end
p sum(1)(2)

しかし、これは sum メソッドを呼び出した時点で

syntax error

となる。

また、sum メソッドを引数一つにして呼び出した場合、_sum の呼び出しで、

「引数の数が違う」

とエラーがでる。

 

a. 手続きオブジェクトを使い、外側の変数を参照

これに対処するには、ネストしたメソッドの代わりに、手続きオブジェクトを利用しなければならない。

手続きオブジェクトは、Proc.new で生成するか、もしくは、以下の形式を使う。

lambda{ } または proc{ ]

def sum(x)
  lambda{|y| x + y}
end
p sum(1).call(2)

上記では、sum メソッドの呼び出しにより、手続きオブジェクトが返される。手続きオブジェクトに引数を渡して、計算を行わせるには、call メソッドを呼び出す。call メソッドを書くのが面倒なら、

[]

を使えばよい。

p sum(1)[2]

ただし、[] による手続きオブジェクトの呼び出しは、配列の要素を参照するメソッドと被るので、あまり好きではない。

全て無名関数で書くなら、

p lambda{|x| lambda{|y| x + y }}.call(1).call(2)
p lambda{|x| lambda{|y| x + y }}[1][2]

やはり、スッキリとした書き方とは思えない。(+_+)

 

b. ネストしたメソッドから、外側の変数を参照できない

Ruby では、他の言語のように、ネストしたメソッドは定義できないのだろうか?

メソッド定義のネスト によると、

ネストされた定義式は、それを定義したメソッドが実行された時に定義されます。

ドキュメントにこのように書かれているので、定義することはできるようだ。

「実行されたときに定義される」

という性質は、Ruby のクラスとインスタンス変数の関係に似ている。

メタプログラミングRuby , p45 によると、

Ruby ではオブジェクトのインスタンス変数はクラスと何のつながりもない。インスタンス変数は値を代入したときに初めて出現する。

例えば、 以下の Person クラスのインスタンス変数 @name は、name メソッドを呼び出さなければ、存在しない。

class Person
  def name
    @name
  end
  def name=(name)
    @name = name
  end
end

person = Person.new
p person.instance_variables   #=> []

person.name = "Tarou"
p person.instance_variables   #=> [:@name]

ネストしたメソッドは定義できる。そのため、下記のコードを実行しても、エラーがでなかった。

def sum(x)
  def _sum(y)
    x + y
  end
end

不思議なことに、ネストされた _sum メソッドは、トップレベルから呼び出すことができる。呼び出した結果、

「メソッド内の x が、ローカル変数でもメソッドとしても定義されてない

という内容のエラーが表示された。

NameError: undefined local variable or method `x' for main:Object

class NameError は、

未定義のローカル変数や定数を使用したときに発生します。

_sum メソッドを呼び出したとき、そのスコープに変数 x は存在しない。

他の言語のように、ネストした関数と同じつもりで書くことはできない。

 

3. 内部スコープから、外部スコープを参照できない

内側のメソッドから、外側のメソッドの変数を参照できなかった。理由は、Ruby のスコープの仕様による。

4048687158
メタプログラミングRuby によると、

Java や C# と言った言語には、「内部スコープ」から「外部スコープ」の変数を参照できる仕組みもあるが、Ruby にはこうした可視性の入れ子構造は存在せず、スコープはきちんと区別されている。新しいスコープに入ると、束縛は新しい束縛と交換される。(p113)

プログラムがスコープを切り替えて、新しいスコープをオープンする場所は 3 つある。

  • クラス定義
  • モジュール定義
  • メソッド呼び出し

… この3つの境界線は、class, module, def といったキーワードで印が付けられている。3つのキーワードはそれぞれスケープゲートのように振る舞う。(p115)

クラスやモジュールの定義のコードはすぐに実行される。一方、メソッド定義のコードは、メソッドを呼び出したときに実行される。(p116)

メソッドに関連した箇所を、まとめると、

  1. キーワード def によるメソッドの定義は、メソッドの呼び出しのときに実行される。
  2. メソッドの呼び出しにより、新しいスコープに切り替わる。
  3. つまり、ネストしたメソッドが実行されるとき、新しいスコープに切り替わり、外側のスコープの変数を参照できない。

 

フラットスコープ

ちなみに、class, module, def によるスコープの制約を超えて、変数を参照したい場合、

を利用する。これを

「フラットスコープ」

と呼ぶそうだ。( メタプログラミングRuby, p118)

 

4. トップレベルに定義したメソッドの所属先は Object

先ほどの sum メソッドは、トップレベルに定義した。Ruby では、トップレベルに定義するメソッドは、関数定義のように見える。Java のように、main メソッドを持つ、特別なクラスを作成する必要がない。

では、トップレベルに定義したメソッドは、どこに所属するのだろう?

その手がかりは、特殊な変数である self

「現在のメソッドの実行主体」

変数と定数 より)

にある。

4048687158
メタプログラミングRuby によると、

Ruby のコードはオブジェクト(カレントオブジェクト)の内部で実行される。カレントオブジェクトは self とも呼ばれる。self キーワードでアクセスできるからだ。…

メソッドを呼び出すときは、レシーバが self になる。…

レシーバを明示せずにメソッドを呼び出すと、全て self に対するメソッドの呼び出しになる。 (p63)

自分が Ruby の小さなデバッガになったと考えてみよう。ブレークポイントに当たるまで命令文を次々に渡っていくとする。さて、一息ついて、周りを見てみよう。どんな景色が見えるだろうか?それがあなたのスコープだ。

スコープ一面に束縛があるはずだ。足元をみると、ローカル変数がいくつもある。顔を見上げると、自分がオブジェクトの中に立っていることに気づく。傍にはメソッドとインスタンス変数がいる。ここがカレントオブジェクト、つまり self だ。遠くのほうに定数のツリ-がはっきりと見える。これで自分の位置が把握できた。目を細めてもっと遠くを見ると、グローバル変数がたくさん見える。(p112)

例えば、Person クラスを定義し、class 定義と、メソッドの定義の中で self を出力してみる。

class Person
  p self               #=> Person
  def initialize(name)
    p self             #=> <Person:0x2252928>
    @name = name
  end
end

tarou = Person.new("Tarou")

クラス定義内では、Person クラスが実行主体となる。メソッド内では、Person クラスのインスタンスが実行主体となる。

先ほどの sum 関数において、同様に self を出力すると、main と表示される。

p self         #=> main

def sum(x)
  p self       #=> main
  def _sum(y)
    x + y
  end
end

sum(1)

main とは、第1章 Ruby言語ミニマム によると、

… 実はRubyプログラム実行中はどこにいようとselfが設定されている。つまりトップレベルにもクラス定義文にもselfがあるのだ。

例えばトップレベルでもselfがある。トップレベルのselfは、mainという。なんの変哲もない、Objectクラスのインスタンスだ。

… トップレベルのself即ちmainObjectのインスタンスなので、トップレベルでもObjectのメソッドが呼べるということになる。そして ObjectにはKernelというモジュールがインクルードされており、そこで pputsなどの「関数風メソッド」が定義されている(図10)。だからこそトップレベルでもpputsが呼べるのだ。

(Kernel)
図10: mainObjectKernel

Rubyが抱える課題、NaClの前田氏が講演 - @IT

多くの言語では、トップレベルの関数(メソッド)にはレシーバーがないが、Rubyではトップレベルで関数を定義すると、それは暗黙的にmainオブジェクトの”関数的”メソッドとして定義される。Rubyではすべてがメソッドで、動的束縛を行うためメソッド探索のコストがかかる。

トップレベルに定義するメソッドは、Object クラスのメソッドとなる。Ruby のトップレベルは、Java における main メソッドを持ったクラスの中のようなもの。

 

5. ネストしたメソッドの所属先は、外側のメソッドと同じクラス

a. Ruby を構成する基本的な要素

ところで、Ruby では「オブジェクト」が、プログラムの基本的な要素として扱われる。

オブジェクト によると、

Ruby で扱える全ての値はオブジェクトです。 Ruby のオブジェクトに対して可能な操作はメソッド呼び出しのみです。あるオブジェクトが反応できるメソッドは、そのオブジェクトが所属するクラスによって一意に決定します。

Ruby に純粋な関数は存在しない。関数のように見える記述も、全てオブジェクトのメソッド。

クラスもオブジェクトとして扱われる。先ほど、Person クラスの定義の中で、self を出力したとき、クラス名である

Person

が表示された。このとき、実行主体はクラスを表すオブジェクト。Java のように「静的なクラス定義と、実行中の動的なオブジェクトが別の世界にある」イメージとは趣が違う。

Person クラスは、Class クラスのインスタンス。self に対して、Object#class を呼び出せば、所属するクラスを確認できる。詳しくは、以下を参照。

4048687158
メタプログラミングRuby, p46、

オブジェクトの内部には、インスタンス変数とクラスへの参照があるだけだ。…メソッドは、オブジェクトじゃなくて、クラスにあるんだ。…

クラスはオブジェクトである。… クラスは Class クラスのインスタンスなのだ。

オブジェクトはクラスに所属し、クラスはメソッドを持つ。逆に言えば、メソッドは必ずどこかのクラスに所属する。よって、ネストしたメソッドも、所属先のクラスがある。

では、ネストしたメソッドは、どのクラスに所属するのだろう?

 

b. コンテキスト

メソッドの評価 によると、

引数のデフォルト式も含め、すべてそのメソッドのコンテキストで評価されます

コンテキストとはなんだろう?Ruby のリファレンスマニュアルを読んでいると、「コンテキスト」という言葉を、しばしば見かける。

例えば、Object#instance_eval

オブジェクトのコンテキストで文字列 expr またはオブジェクト自身をブロックパラメータとするブロックを評価してその結果を返します。

オブジェクトのコンテキストで評価するとは評価中の self をそのオブジェクトにして実行するということです。

Person クラスに、name 属性と、与えられたブロックを実行するだけのメソッド test を定義する。

class Person
  attr_accessor :name
  def test
    yield
  end
end

このクラスを使い、instance_eval を呼び出すときに、self を出力。

tarou = Person.new
tarou.name = "Tarou"
tarou.instance_eval{
  p self     #=> #<Person:0x232d640 @name="Tarou">
  p @name    #=> "Tarou"
}

instance_eval メソッドにおけるブロックの self が、レシーバーのインスタンスとなっている。

次に、test メソッドで self を呼び出したときと、上記を比較してみる。

tarou.test{ 
  p self     #=> main
  p @name    #=> nil
}

test メソッドのブロックでは、self がトップレベルのオブジェクトである main となる。よって、インスタンス変数 name を参照しても、nil となる。なぜなら、main オブジェクトに name 属性を設定していないため。

同じ内容のブロックでも、コンテキストが変化することにより、評価の結果が異なる。

Rubyの呼び出し可能オブジェクトの比較 (2) - というよりコンテキストの話 - 世界線航跡蔵 によると、

コンテキストとはおおざっぱに言えばローカル変数の状態self, klassから成る。

ここでローカル変数というのは、参照できる限り外側のスコープも含めた全部だ。selfというのはデフォルトでメソッドを受け取る相手である。Rubyのプログラムにはいつでもselfがいて、擬似変数selfを通じてアクセスできるのであった。

Rubyの呼び出し可能オブジェクトの比較 (3) - なんかklassの話 - 世界線航跡蔵

… Rubyでは、メソッドはselfに定義されると考えたくなるが、そうではない。実はこれが前に名前だけ触れたklassである。klassは正式な用語ではなく、この記事では仮にそう呼ぶというだけである。…

Rubyレベルでは表にあわれないがRuby処理系は内部でここでいうklassを常に保持している。これは「今メソッドを定義したらどこに定義されるか」というクラスである。この存在を考えるとRuby文法の理解がすっきりする。

トップレベルでは"main"と呼ばれるある特定のObjectインスタンスがselfなのに、トップレベルでメソッドを定義するとObjectのインスタンスメソッドになる。defの中に更にdefを書くと、それらのdefを含むクラスのインスタンスメソッドになる。メソッドは「selfに対して」定義されるわけではないのだ。

トップレベルではselfはmainで、klassはObjectだclass文に入るとselfは構築中のクラスのClassオブジェクトになり、 klassも同じになるdefの中を評価するときは(メソッドを実行するときは)selfはレシーバーで、klassはレシーバーのクラスである

また、第14章 コンテキスト によると、

典型的な手続き型言語のイメージだと、手続き呼び出しのたびに、ローカル変数領域や戻る場所など手続き一つの実行のために必要な情報を構造体(スタックフレーム)に格納し、それをスタックに積む。…

… Rubyでは…理由がいろいろあって、スタックはなんと七本もある。…

スタックとして、メソッド定義先クラスを管理する ruby_class 、ローカル変数スコープを管理する ruby_scope などがあるとのこと。

ruby_classdefでメソッドを定義するときに対象となるクラスを表す。普通のクラス定義文ではselfがそのクラスになるのでruby_class == selfだが、トップレベルやevalinstance_evalという特殊なメソッドの途中では self != ruby_classになることがある。  (同上より)

ruby_scopeはローカル変数スコープを表すスタックである。メソッド・クラス定義文・モジュール定義文・特異クラス定義文などが独立したスコープだ。(同上より)

 

c. トップレベルのネストしたメソッドは、Object クラスに所属する

上記から sum メソッドについて考えると、

  1. トップレベルに定義した sum メソッドが評価されることにより、self は main で、メソッドの定義先は Object となる。
  2. 「メソッドの評価は、すべてそのメソッドのコンテキストで評価される。」よって、ネストしたメソッドを評価するとき、外側のメソッド定義先クラス ruby_class は変わらないが、キーワード def により、ローカル変数スコープ ruby_class が刷新されるのではないか?
  3. つまり、ネストしたメソッド _sum のコンテキストは、self は main で、メソッドの定義先は Object となる。しかし、外側のメソッドのローカル変数を参照することはできない。

簡単にいえば、外側のメソッドと同じレジーバーのクラスに、ネストしたメソッドが追加される。よって、トップレベルでネストしたメソッドを定義すると、Object クラスのメソッドになる。そのため、トップレベルではレシーバーを省略して _sum メソッドを呼び出すことができた。

例えば、適当にトップレベルにネストしたメソッドを定義し、呼び出しを行なってみる。

def add1(x)
  def add2(y)
    y + 2
  end
  x + 1
end

p add1(100)    #=> 101
p add2(100)    #=> 102
トップレベルの main オブジェクトに add1, add2 メソッドが定義されたのが分かる。

 

d. クラス定義におけるネストしたメソッドは、そのクラスに所属する

次に、トップレベルでネストしたメソッドを定義した場合と、クラスで定義した場合を比較する。

最初に書いた Person クラスで、ネストしたメソッドを定義し、呼び出してみる。Person#initialize 内に age メソッドを定義。

class Person
  p self               #=> Person
  def initialize(name, age)
    p self             #=> <Person:0x2201b08>
    @name, @age = name, age
    def age
      p self           #=> <Person:0x2201b08 @age=30, @name="Tarou">
      @age
    end
  end
end

tarou = Person.new("Tarou", 30)
p tarou.age  #=> 30

結果、initialize メソッドの内側に定義された age メソッドは、Person クラスのメソッドとして呼び出せた。つまり、ネストしたメソッドは、外側のメソッドが所属するクラスに属する。

ところで、このように書けるメリットはあるのだろうか?あるメソッドを実行したときに、新規にメソッドを追加したり、別のメソッドを上書きして、挙動を変化させることに意味が見いだせれば、使いどころがある。ただし、内側に定義したメソッドにおいて、自由変数を使えないのはデメリットな気がする。

 

e. ネストしたメソッドの、呼び出し制限の違い

あるオブジェクトに定義されたメソッドを、調べるためのメソッドが、Object, Module クラスに定義されている。

Object#methods

そのオブジェクトに対して呼び出せるメソッド名の一覧を返します。このメソッドは public メソッドおよび protected メソッドの名前を返します。

上記のメソッドでは、private メソッドは含まれないことに注意。Object#methods 以外に、以下のメソッドが定義されている。

Module#instance_methods

そのモジュールで定義されている public および protected メソッド名の一覧を配列で返します。

上記と同じく、private メソッドは含まれな。Module#instance_methods 以外に、以下のメソッドが定義されている。

これらのメソッドを使い、先ほどトップレベルに定義した add1, add2 メソッドを調べてみる。

p self.methods.grep /add1/   #=> []
p self.methods.grep /add2/   #=> []

p self.private_methods.grep /add1/  #=> [:add1]
p self.private_methods.grep /add2/  #=> [:add2]

add1, add2 メソッドは、main オブジェクトのプライベートメソッドとして追加されているのが分かる。

これに対して、先ほどの Person クラスに追加した age メソッドについて確認すると、パブリックメソッドとして追加されていた。

p tarou.methods.grep /age/           #=> [:age]
p tarou.public_methods.grep /age/    #=> [:age]

トップレベルに定義されたメソッドと、クラス内に定義されたメソッドで、呼び出し制限に一貫性がないのはなぜだろう?

 

f. ネストしたメソッドを Method オブジェクトとして取り出す

最後に、ネストしたメソッドを、Method オブジェクトとして取り出してみる。

class Method とは、

Object#method によりオブジェクト化されたメソッドオブジェクトのクラスです。

メソッドの実体(名前でなく)とレシーバの組を封入します。 Proc オブジェクトと違ってコンテキストを保持しません。

先ほどの add2 メソッドを Method オブジェクトとして取り出す。

add = self.method :add2
p add.call(100)   #=> 102
p add[100]        #=> 102

Person クラスの age メソッドも取り出してみる。

tarou = Person.new("Tarou", 30)
age = tarou.method :age
p age.call        #=> 30
p age[]           #=> 30

 

参考記事

参考サイト

参考書籍

4048687158
メタプログラミングRuby, p110

ブロックは単体では実行できない。コードを実行するには、ローカル変数、インスタンス変数、self といった環境が必要になる。…

ブロックを定義すると、その時その場所にある束縛を取得する。ブロックをメソッドに渡すときは、その束縛も一緒に持っていく。

2011年11月10日木曜日

ブラウザで表示し見ている記事をその場で即編集 - Windows Live Writer で直接記事を取得する WLW Post Downloader Plugin とブックマークレット

0. 目次

 

1. WLW で過去の投稿一覧を取得するのには時間がかかる

Windows Live Writer (WLW)で、最近投稿したブログの記事を修正する場合、

  • 「ホーム」タブの左横にある Live Writer ボタン > 最近の記事を開く

を選択し、過去の記事を開く。

これに対し、過去の古い記事を修正するには、

  1. Blogger で、投稿した記事を開き、 HTML モードで表示する。
  2. HTML で表示された内容を WLW へコピペ。
  3. 記事を修正する。
  4. WLW で内容をHTML 表示にして、コピーする。
  5. Blogger で、HTML をコピーした元の場所へペーストする。

Windows Live Writer から、直接遠い過去に投稿した記事を編集したい場合は、

  1. 「ホーム」タブの左横にある Live Writer ボタン > 最近の記事を開く
  2. ダイアログが表示されたら、編集したい投稿のあるブログを選択
  3. 「表示する項目の数」で「すべて」を選択
  4. タイトルの一覧が表示されたら、「記事のフィルター処理」に、編集したいタイトルを入力
  5. 対象の記事をダブルクリック

11-10-20111

たくさん記事があると、投稿記事の一覧を取得するの時間がかかる。過去の記事を、直接取得することはできないだろか?

 

2. WLW Post Downloader Plugin を利用して、直接記事を取得

How to edit very old posts with Windows Live Writer」には、プラグイン

を利用して、直接記事を WLW で取得する方法が紹介されていた。早速、上記プラグインをダウンロードして、インストール。

 

記事の取得方法

11-10-20112過去の記事を編集する場合は、

  1. 過去に投稿した、編集したい記事のページを開く。
  2. ロケーションバーに表示されている URL の http wlw に置き換えて Enter キーを押す。
  3. WLWPostDownloader の起動が促されるので、許可する。

ただし、Firefox 8 では、ロケーションバーに http が表示されていないので、URL の先頭に

wlw://

を追加すれば良い。

これで、直接対象の記事を Windows Live Writer で開いてくれる。

追記(2012.4.6): Window Live Essentials 2011 のアップデートを適用したら、WLW Post Downloader Plugin が正常に動作しなくなった。

SnapCrab_No-1621再度、WLW Post Downloader Plugin を実行し、

  • Repair WLW Post Downloader

をする必要がある。

追記(2014/12/20): 上記の方法でも正常に動作しない場合、

  • Windows のコントロールパネル > プログラムのアンイストール

より、WLW Post Downloader を削除した後、再インストールを行う。その後、

  • Firefox のオプション > プログラム

において、

  • ファイルの種類(Content Type); wlw
  • 取り扱い方法; C:\Program Files (x86)\Windows Live\Writer\WLWPostDownloader.exe

に設定されていることを確認する。

SnapCrab_No-1597

 

3. 記事を編集するためのブックマークレット

追記(2012.2.6): ワンクリックで記事を編集するために、ブックマークレット「この記事を編集」をブラウザに保存しておく。

使い方は、

  1. 編集したい記事を、ブラウザで開く。
  2. ブックマークレット「この記事を編集」をクリックすると、WLW で記事が開かれる。

JavaScript の実装

ブックマークレットの内容は、以下の通り。

(function(){
    var l=location, hs=l.hostname.split('.');
    l.href="wlw://" + hs.slice(0,hs.length-1).join('.') + ".com" + l.pathname;
})();

Compress javascript and css. を利用して、上記コードを圧縮した。

追記(2012.3.19): Blogger の URL が .com から .jp へリダイレクトされるようになったので、上記のブックマークレットを修正した。

 

4. WLWPostDownloader.exe の優先度を上げる

追記(2014/10/25): アプリケーションの優先度を最適化するために Process Lasso を利用している。できるだけ素早く、Windows Live Writer を起動したいので、WLWPostDownloader.exe の優先度を Hight に変更した。

SnapCrab_No-1563

  • WLWPostDownloader.exe で右クリック > Priority Class > Always > Hight

を選択。ただし、WLWPostDownloader.exe を起動しているときにしか設定できないので注意。

2011年9月5日月曜日

フィードを全文配信する Full Text RSS Feed Builder を開くためのブックマークレット

1. フィードを全文配信してほしい

フィードを全文配信してないサイトを、Google Reader に登録するとき、「まるごとRSS」 を使っていた。

全文配信されてないサイトは、いつの間にか読まなくなる。部分的にフィードを配信しているサイトは、全文配信されるように変換しておきたい。

 

2. フィードを全文配信してくれる Full Text RSS Feed Builder

Full Text RSS Feed は、まるごとRSS と同じように、フィードを全文配信するためのサービス。

部分的に配信しているRSSフィードを全文配信に変換してくれるサイト「Full Text RSS Feed Builder」 : ライフハッカー[日本版]によると、

「Full Text RSS Feed Builder」は、部分的にRSSを配信しているフィードを全文配信に変換してくれるサービス。…

まず、Full Text RSS Feed Builderにアクセスしましょう。変換したいRSSフィードを入力して「Submit」します

Full Text RSS Feed の特徴を確認すると、

no ads

広告が表示されないので、ありがたい。 こちらに乗り換えよう。

 

3. Full Text RSS Feed Builder を開くためのブックマ-クレット

毎回 Full Text RSS Feed を開くのは面倒なので、ブックマークレットを作っておく。 以下をブックマークに D&D 。

ブックマークレットの内容は、以下の通り。

(function(){
    var l=location,
        url="http://fulltextrssfeed.com/";
    l.href=url+l.host+l.pathname;
})();

 

4. フィードの画像が表示されないときの対策

登録したフィードの画像が表示されないとは、

を参照。

2011年6月19日日曜日

Ruby, JavaScript, Python で既存のクラスを拡張 - オープンクラス(モンキーパッチ) と Scala の暗黙の型変換

1. 「リストの要素を取得する」関数を例として、クラスの拡張を考える

Lisp でリストの先頭要素を得る関数は car 。先頭以外の残りの要素を取得するには cdr 。

(car '(1 2 3 4 5))   => 1
(cdr '(1 2 3 4 5))   => (2 3 4 5)

( cf. CAR and CDR – Wikipedia )

Haskell で上記に相当する関数は、

  • head
  • tail

類似した対照的な関数に

  • init
  • last

がある。

main = do let xs = [1..5]
          print $ head xs   -- 1
          print $ tail xs   -- [2,3,4,5]
          print $ init xs   -- [1,2,3,4]
          print $ last xs   -- 5

これらの関数名を、他の言語でも使えるように、拡張の方法をそれぞれ見ていく。

 

2. Ruby で組み込みのクラス Array を拡張

Ruby で同様の関数名を探すと、Array クラスに要素一つを取得する関数

  • first
  • last

が見つかる。

first -> object | nil

配列の先頭の要素を返します。要素がなければ nil を返します。

first(n) -> Array

先頭の n 要素を配列で返します。n は 0 以上でなければなりません。

[PARAM] n:
取得したい要素の個数を整数で指定します。

複数の要素を取得するには、引数に要素数を与える関数も定義されている。これは last も同じ。

ところで、Haskell と同じ関数名で呼び出したい場合、Ruby には既存のクラスを拡張する方法が用意されている。

まつもと直伝 プログラミングのオキテ 第21回 オープンクラスとRuby on Rails」によると、

Rubyでは既存のクラスに定義を追加できます。…

後から機能を追加できるクラスのことを「オープンクラス」と呼びます。

既存の Array クラスを拡張するには、同クラスを定義する要領で行えばよい。

class Array
  alias :head :first

  def tail
    last(length-1)
  end

  def init
    first(length-1)
  end
end

xs = (1..5).to_a

p xs.head   #=> 1
p xs.tail   #=> [2, 3, 4, 5]
p xs.init   #=> [1, 2, 3, 4]
p xs.last   #=> 5

他の言語から考えると、既存のクラスを変更できることは過激な仕様に見える。なぜなら、既に存在するクラスは、ある種グローバルで固定的な定数のようなイメージを持っていたため。

よって、オープンクラスを利用したら、変更を把握しておくためのコストが発生する。

デメリットは,オープンクラスには悪影響を及ぼす可能性があることです。オープンクラスはクラスライブラリの状態を大きく変更してしまうので,プログラム全体に影響を与えてしまいます。 …

複数のライブラリがオープンクラスを使って既存のクラスを変更した場合,お互いに矛盾があると回避する方法が存在しません。…

オープンクラスを利用したライブラリを使うときには,ライブラリ同士がお互いに矛盾しないかどうかをよく吟味する必要があります。…

(同上より)

オープンクラスの別名は モンキーパッチ – Wikipedia

オリジナルのソースコードを変更することなく、実行時に動的言語(例えばSmalltalk, JavaScript, Objective-C, Ruby, Perl, Python, Groovy, など)のコードを拡張したり、変更したりする方法である。…

当初はモンキーパッチは、ルールを無視して実行時にこっそりとコードを変更することから、ゲリラパッチと呼ばれていた。これらのパッチを複数当てると、時折直感に反するような相互作用が生まれることがあり、Zope 2では、交戦中のパッチと呼ばれていた。

モンキーの語源はおもしろい。 ^^;

ゲリラゴリラ同音異字に近かったため、何人かの人がゲリラパッチの代わりにゴリラパッチという間違った用語を使用し始めた。交戦することのないゲリラパッチ開発者が作成するのが非常に難しかったため、より弱そうに聞こえるモンキーパッチという用語が作られることになった。[2]

 

3. JavaScript で既存の Array オブジェクトのプロトタイプを拡張

上記のような Ruby の仕組みは、プロトタイプベース的な性質と考えておけばいいのかな?

プロトタイプベースの言語と言えば JavaScript 。

Array オブジェクトのプロトタイプに head, tail, init, last を定義するなら、

Array.prototype.head = function(){ return this[0]; };
Array.prototype.tail = function(){ return this.slice(1); };
Array.prototype.init = function(){ return this.slice(0,this.length-1); };
Array.prototype.last = function(){ return this[this.length-1]; };

var ary = [1,2,3,4,5];
console.log(ary.head());   // 1
console.log(ary.tail());   // [2, 3, 4, 5]
console.log(ary.init());   // [1, 2, 3, 4]
console.log(ary.last());   // 5

 

4. Python で既存のクラスを拡張

組み込みクラスは拡張できない

Python では組込みクラスに手を出せない。

例えば「リスト」。

  • type 関数

で任意のリストに適用すると、返される値は

list

この list に属性を追加しようとすると、TypeError となる。

>>> list.x = 100
Traceback (most recent call last):
  File "", line 1, in 
TypeError: can't set attributes of built-in/extension type 'list'

つまり、list を拡張することはできない。

これに対処するには、組込みクラスを拡張することを諦め、リストをサブクラス化したものを使う。

(cf. ruby vs python | Lambda the Ultimate )

class List(list):
    def head(self):
        return self[0]

    def tail(self):
        return self[1:]

    def init(self):
        return self[0:-1]

    def last(self):
        return self[-1]

xs = List(range(1,6))

print xs.head()   #=> 1
print xs.tail()   #=> [2, 3, 4, 5]
print xs.init()   #=> [1, 2, 3, 4]
print xs.last()   #=> 5

 

ユーザが定義したクラスは、実行時に振舞いを変えることができる

ただし、ユーザが定義したクラスでは、後からメソッドを追加できる。

例えば、Ruby でユーザ定義したクラス Person に後からメソッドを追加するには、class 定義を繰り返せばよい。

class Person
    def initialize(name)
      @name = name
    end
end

ps = Person.new("Tarou")
p ps

class Person
  attr_accessor :age
end

ps.age = 30
p ps

ちなみに、Ruby が奇妙な言語だとはじめて感じたのは、この定義の仕方を見たとき。 class の定義が一つの場所に固定されてない。 Java であるなら、クラス定義は一箇所に限定され、実行時にそれが変化することはない。

この点に関して、「メタプログラミングRuby (p41) では class の役割について、わかりやすく説明されている。

Ruby の class キーワードは、クラス宣言というよりもスコープ演算子のようなものである。もちろん、存在しないクラスは作成する。しかしそれは、副作用と言ってもいいかもしれない。 class の主な仕事は、あなたをクラスのコンテキストに連れていくことである。そのコンテキストであなたがメソッドを定義する。

(太字は引用者による)

同様のことを、Python で書く場合は、

  1. クラスを定義
  2. 引数を一つ与える関数を別に定義
  3. 上記関数をクラスの属性として設定

( cf. 和訳 : なぜPythonのメソッド引数に明示的にselfと書くのか | TRIVIAL TECHNOLOGIES on CLOUD )

class Person:
    def __init__(self, name):
        self.name = name

def setAge(self, age):
    self.age = age

p = Person("Tarou")

Person.setAge = setAge
p.setAge(30)

print p.name
print p.age

 

5. Scala の暗黙の型変換は、既存のクラスを拡張したように見せかける

Scala では implicit conversion を利用すると、既存のクラスを拡張したように見せかけることができる。 implicit converson は、コンパイラがあるクラスのオブジェクトを別のクラスに自動的に変換してくれる機能。

定義の仕方は、

  1. 変換元のクラスをラップしたクラスを定義。
  2. 上記クラスに「拡張すると想定したメソッド」を定義。
  3. implicit def により、変換元から変換先を指定。

書き方は以下の通り。

implicit def 適当な名前(変換元のクラス) = 変換先のクラス

これにより、変換元のクラスに対して「拡張したと想定したメソッド」を呼び出したとき、コンパイラが自動的に変換先のクラスにしてくれる。

例えば、Scala の List クラスを拡張し、head, tail に相当するメソッド「先頭, 尾部」を定義したかのように見せかける。

class ListWrapper[T](xs : List[T]){ 
  def 先頭: T = xs.head
  def 尾部: List[T] = xs.tail
}

implicit def list2ListWrapper(xs: List[Any]) = new ListWrapper(xs)

val xs = Range(1,6).toList
println(xs.先頭)   // 1
println(xs.尾部)   // List(2, 3, 4, 5)

( cf. Ruminations of a Programmer: Why I like Scala's Lexically Scoped Open Classes )

コード上では、リストに対してメソッドを拡張したかのように見える。

 

6. Haskell はクラスとメソッドという関係がないので、自由に拡張できる

Haskell では、クラスとメソッドが結合している関係が存在しないので、既存のクラスを拡張という概念がそもそも存在しない。同様のことをしたいなら、単純に適用したい型を引数に与える関数を定義すればいい。

car, cdr をリストに対して適用できるようにするには、

car :: [a] -> a
car (x:_)  = x

cdr :: [a] -> [a]
cdr (_:xs) = xs

別名を付けるだけなら、

car = head
cdr = tail

 

オブジェクトは関数とデータ構造が密結合している

これを見て、「Why OO Sucks」のオブジェクト指向に対する批判を思い出した。

Objection 1 - Data structure and functions should not be bound together
Objects bind functions and data structures together in indivisible units. I think this is a fundamental error since functions and data structures belong in totally different worlds.

( cf. Matzにっき(2007-04-12) )

2011年3月30日水曜日

Ustream から ニコストリーム を開く ブックマークレット

1. ブックマークレットの使い方

  1. ust2nst」のリンクをブラウザのブックマークに保存する。 ( D&D )
  2. Ustream において特定の配信を見ている状態で、保存したブックマークを押すとニコストリームが開く。

 

2. ニコストリームで Ustream を視聴する

ニコニコ動画 で特徴的なコメントの表示方法は、慣れてくると案外読みやすい。これに対し、Ustream は動画の横に Twitter の投稿が表示されるので今一見にくい。

これを解消してくれるのが ニコストリーム というサービス。

「ニコストリーム」はUstream動画上にTwitterのコメントをニコニコ動画風に表示して楽しめるサービスです。

( ニコストリーム - Ustream×Twitterでニコニコ風に再生 より )

Ustream 本家とニコストリーム を比較したら、コメントが流れる速さの違いに驚いた。

CropperCapture[138]

ニコストリームの方が見やすいので、これからはここで見ることに。

 

3. ブックマークレットの内容

JavaScript を圧縮する前のコードは以下の通り。

var l = location,
        url = l.protocol + "//" + l.hostname + l.pathname
        ns = "http://nicostream.info/search?key=";
l.href = ns + encodeURIComponent(url);

 

関連記事

2011年3月27日日曜日

Firefox のアドオン KeySnail で「ブックマークレット」にホットキーを設定 - Google Bookmarks への投稿するウィンドウを呼び出す

1. 「ブックマークレット」をショートカットキーで呼び出したい

Firefox 4 へ移行  したことにより、Google Bookmarks を利用するためのアドオン GMarks が使えなくなった。

その代わりに本家サイトに用意されている ブックマークレット を使い、ブックマークをすることにした。

SnapCrab_No-0953

しかし、マウスでブックマークをクリックするのは面倒。 (+_+) ブックマークレットにショートカットキーを割当てたい。

追記 (2011.3.30) : Gmarks が Firefox 4 に対応してれた。

 

2. KeySnail でショートカットキーを割当てる

ブックマークレットは、JavaScript で記述されている。

アドオン keysnail の機能は、JavaScript に対して、ショートカットキーを割り当てることができる。

あらかじめ用意されたコマンドだけでなく、 JavaScript を使ってオリジナルの関数を書き、それらを 自由なキーの組み合わせ に割り当てることが可能です。

そこで、Google Bookmarks のブックマークレットを、 keysnail を使い、ショートカットキーを割当てることにした。

 

KeySnail のインストールと初期設定

GitHub より keysnail をアドオンに D&D することによりインストール。

最初に起動したとき、設定ファイルを新しく作成する。

CropperCapture[130]

保存場所を尋ねられるので、複数の PC で設定を共有したいので、Dropbox 上を作成先に指定。

デフォルトのキーバインドは、

「とにかく Emacs」

にした。必要なければ、「キーバインドの設定なし」を選択。

CropperCapture[131]

 

キーバインドの設定

メニューより、

  • ツール > KeySnail > 設定ダイアログを開く

を選択。「キーバインド」において、「追加」ボタンを押して「オリジナルのコマンド」を選ぶ。

モードを

View

にして、コマンド名を適当に付ける。

キーは Firefox でブックマークをするときの動作を上書きするために

C-d

を割当てた。

CropperCapture[137]

 

ブックマークレットをキーバインドに設定

次に、上図の「編集」ボタンを押す。

公式のGoogle ブックマークレットの JavaScript の内容は、以下の通り。

javascript:(function(){var%20a=window,b=document,c=encodeURIComponent,d=a.open('http://www.google.com/bookmarks/mark?op=edit&output=popup&bkmk='+c(b.location)+'&title='+c(b.title),'bkmk_popup','left='+((a.screenX||a.screenLeft)+10)+',top='+((a.screenY||a.screenTop)+10)+',height=420px,width=550px,resizable=1,alwaysRaised=1');a.setTimeout(function(){d.focus()},300)})();

これをキーバインドに設定するために、 一部を修正した、以下のコードを貼り付け、「変更の反映」ボタンを押して OK 。

function (ev, arg) {
    var a = window,
        b = a.content.document,
        c = encodeURIComponent,
        d = a.open(
	    'http://www.google.com/bookmarks/mark?op=edit&output=popup&bkmk=' 
		+ c(b.location) 
		+ '&title=' 
		+ c(b.title)
	    , 'bkmk_popup'
	    , 'left=' 
		+ ((a.screenX || a.screenLeft) + 10) 
		+ ',top=' 
		+ ((a.screenY || a.screenTop) + 10) 
		+ ',height=420px,width=550px,resizable=1,alwaysRaised=1');
    a.setTimeout( 
	function () {
	    d.focus()
	}
	, 300)
}

これでブックマークをしたいページで

Ctrl + d

でブックマークできるになる。

 

ブックマークレットの修正点について

上記ブックマークレットで修正したのは、以下の点。

URL 最初の

javascript:

を削除。

decodeURIComponent により文字列をデコード。

全体を囲っている

(function(){  ...  })();

を削除。 ( cf. JavaScript の無名関数に引数を与える )

document.location

でブックマークしたいページの URL を取得できなかったので、代わりに

window.content

経由でアクセス。

 

3. Emacs 風にしたときの操作の注意点

検索をするとき、

  1. 新しくタブを開いて ( Ctrl + t ) 、
  2. 検索バーにフォーカスを移そうと Ctrl + K を押しても、

ロケーションバーにフォーカスが移ってしまう。

これを回避するには、現在閲覧しているページにおいて

  1. Ctrl + K を押し
  2. 検索したい単語を入力
  3. Alt + Enter を押す

ことにより、検索するのが効率的。

 

4. Adblock の設定

追記(2014/6/2): Adblock で特定のフィルタを利用していると、Google Bookmarks のウィンドウが閉じてしまう。

 

参考サイト