2008年10月2日木曜日

AutoHotkey で Hello, world!

AutoHotkey のダウンロードとインストール

AutoHotkey Download より、AutoHotkey をダウンロードしてインストール。

追記 (2011.3.22) : AutoHotkey - Wikipedia によると、

As of October 10, 2010, the author has stated that AutoHotkey_L has become the ongoing development of AutoHotkey and this has been placed in the download section on the website.

とのことなので、Installer for AutoHotkey_L の方を使うようにした。

 

Hello world をダイアログに表示

テキストエディタで、以下のように記述。

MsgBox "Hello, world!"

ファイル名に `hello.ahk’ と名前を付けて保存。ファイルの拡張子を

.ahk

とすることにより AutoHotkey のスクリプトであることを示す。

コマンドや変数は大文字・小文字を区別しないことに注意。

用意されているコマンドは、

を参照。

 

実行方法

上記で作成した hello.ahk をダブルクリックして実行。以下のようなダイアログが表示される。

081002-003

ダイアログが表示されている間は、タスクトレイに AutoHotKey のアイコン 081002-002 表示されている。終了と共に消える。

 

ホットキーで Hello world

次に、Ctrl + H をキーボードから入力することにより、”Hello, world!” と出力されるように変更する。

^H::MsgBox "Hello, world!"

「ホットキー::コマンド」

という形式によって、ホットキーを押したときにスクリプトが実行されるようになる。`^’ はコントロールを表わす。

 

ホットキーによる実行

先ほどと同じようにファイル `hello.ahk’ をダブルクリック。今度はすぐにダイアログが表示されないが、タスクトレイに AutoHotKey のアイコン 081002-002 表示されている。

この状態で `Ctrl + H’ を押すと、`Hello, world’ のダイアログが表示される。

タスクトレイに AutoHotKey のアイコンが表示されている間は、このキーボードの入力が有効になっている。終了させるには、アイコン 081002-002  を右クリック > Exit を選択する。

 

ホットキーで複数の文を実行

追記 (2011.3.22) : 複数の文に対して、ホットキーを付けたい場合は、AutoHotkey Tutorial: Macro and Hotkey Creation によると、

To have more than one command executed by a hotkey, put the first line beneath the hotkey definition and make the last line a return.

つまり、以下のような形式となる。

ホットキーの設定

実行したい文

…

return

先ほどの Hello world の後に hoge, piyo, fuga とダイアログを表示させたい場合は、

^H::
MsgBox "Hello, world!"
MsgBox "hoge"
MsgBox "piyo"
MsgBox "fuga"
return

 

参考

Windows Live Writer Beta のアップデート

ブログを書くときに欠かせない、Writer Beta - Windows Live がアップデートした。更新しようとしたら、デフォルトでは色々と他のソフトもインストールする設定になっていたので気をつける。(+_+)  Writer だけが選択されるようにした。

起動したら、これまでベータ版は英語のメニューだったけれど、日本語で表示されるようになった。ぱっと見たところ、全体的にツルンとした印象に。最近の Office ってこんな感じなのかな?

字を入力したら、表示が以前よりも綺麗だ。

081002-005

 

前のバージョンと比較したら少し上がっているのがわかる。

windowslive

 

いらないスペルチェック

うわ!(@_@;) しかし、書きはじめたら、いきなりスペルチェックの嵐。メニューより、ツール > オプション > スペルチェック で、「辞書の言語」において「なし」を選択。

081002-006

 

プラグイン

Flickr

プラグインでは、Flickr に画像を投稿するための Flickr4Writer を使っている。このダイアログの表示が以前よりもスムーズになった。

 

Amazon

今回はじめて、Amazon 用のプラグインをインストールしてみた。(Amazon Associate Japan) これは使いやすそうだ。

Amazon の ID は ツール > オプション > プラグイン より、Amazon Associate JP を選択し、「プラグインの詳細」において「オプション」ボタンを押す。ダイアログが表示されるので、Associate ID を入力。

081002-008

 

使ってみると、挿入した画像を左右に寄せたりできるので便利。今までは G-Tools を使うことが多かったけれど、こちらに乗り換えようかな。

 

その他

ショートカットキー

メニューより、編集 > カテゴリ を見たら、Ctrl + Shift + C のショートカットがついていた。これって以前からあったのかな?このおかげで、記事を書いているときにさっとタグを付けることができる。 ^^

 

プログラムのショートカット

あれ?今まで使っていたショートカットはどこへ?

スタート > すべてのプログラム > Windows Live > Windows Live Writer Beta にあった。

 

バグの解消

これまで 1 画面に納まらない量を書いた場合に、文章に「見出し」をつけると、なぜか文章の先頭まで戻ってしまっていた。非常にわずらわしかったけれど普通に使えるようになっていた。 ^^ 正直これが一番のメリットかも。

 

テーマの更新

久しぶりにブログのテーマの更新をしておこう。(表示 > テーマの更新) これをしておくと、プレビュー表示において、実際のブログの表示と同じ見え方にしてくれる。誤字脱字をチェックするときはプレビュー表示にしている。編集モードとは違う見え方にすることによって、ミスの検出率が上がる。これって心理学でいう何て効果だっけ?忘れてもうた。 ^^; 見たいときは、ショートカットキー F12 でさっと見ることができる。

2008年10月1日水曜日

Python でネストしたリストから Composite パターンを生成

「Python でリストに対する再帰的な関数の適用」では、ネストしたリストに対して、リストのまま関数を適用した。今回はネストしたリストから Composite パターンとなるようにオブジェクトを生成してみる。 Composite にしておけば、後々の操作がしやすくなるかな。

 

Composite パターン

リストを、オブジェクトを使って次のような構造へ変換する。とりあえず、リスト → オブジェクトへの変換だけなので、要素に相当するクラスは作らない。リストを表わす List クラス、値を表わす Value クラスのみを作成。要素クラスは、List, Value に共通の操作が必要になったときに作成することに。

080930-001

 

まずは、List と Value クラス。

class List:
    BRACKETS = "<>"

    def __init__(self):
        self.elems = []

    def add(self, elem):
        self.elems.append(elem);
        return self

    def __str__(self):
        return List.BRACKETS[0] + \
               ",".join(str(e) for e in self.elems) + \
               List.BRACKETS[1]

    def __iter__(self):
        return iter(self.elems)

class Value:
    def __init__(self, val):
        self.val = val

    def __str__(self):
        return str(self.val)

( cf. Python のイテレータ, Python のイテレータ (3) )

 

Composite を生成する関数

リストからオブジェクトを生成する関数は、前回の再帰的な関数を真似る。

  1. 渡されたリストがリストであれば、要素に対して再帰的に生成関数を適用する。
  2. 値であれば、 Value クラスを作成。
def createList(L):
    if isinstance(L, list):
        newList = List()
        for e in L:
            newList.add(createList(e))
        return newList
    else:
        return Value(L)

 

List クラスに要素を追加するメソッドは、最後に自身を返すようにしているので、reduce で書換えることができる。

def createList(L):
    if isinstance(L, list):
        return reduce(lambda a,b: a.add(createList(b)), L, List())
    else:
        return Value(L)

print createList([1,2,[21,22,[231],24,1],2,3,4,5])

上記を実行した結果は、

<1,2,<21,22,<231>,24,1>,2,3,4,5>

Python で flatten - ネストしたリストをフラットにする

「Python でリスト内のリストを連結」 のつづき

1. Ruby の flatten メソッドと同じ関数を書きたい

前回、Python で「リスト内のリストを結合する」処理を書いた。

Ruby で「ネストしたリストを結合する」メソッドは Array#flatten 。Ruby の flatten は、どれだけネストが深くてもフラットな配列にして返す。また、リスト内の要素のネストの深さが異なっていても、フラットにしてくれる。

irb(main):001:0> [1,[2,3,[4],5],6].flatten
=> [1, 2, 3, 4, 5, 6]

このような Ruby の flatten メソッドと同じ関数を、Python で書きたい。

 

2. ネストしたリストを平坦化する関数 flatten

「ネストしたリスト」に対する処理を書いたことがある。

def map2(L):
    if isinstance(L, list):
        if L == []:
            return []
        else:
            return [map2(L[0])] + map2(L[1:])
    else:
        return L

print map2(L)

これを元にコードを考える。

def flatten(L):
    if isinstance(L, list):
        if L == []:
            return []
        else:
            return flatten(L[0]) + flatten(L[1:])
    else:
        return [L]

L = [1,[21,22],3,[41,[421,422],43,44],5]
print flatten(L)

参考にしたコードとの違いは、対象がリストの場合の処理。リストの先頭要素に対する、関数の呼出し部分が異なる。この関数 flatten では、返ってきた結果をリストにしていない。もし、リストにしたら、ネストした構造を保ってしまう。

リストでない場合は、要素1つのリストにして返す。これは再帰的に呼び出されたときに、呼び出し元にリストを返し、他のリストと結合させるため。

上記を実行した結果は、以下のようになる。

[1, 21, 22, 3, 41, 421, 422, 43, 44, 5]

 

3. リストに対する処理を reduce で置き換える

前回と同じように、リストの要素に対する処理を、組込み関数 reduce に置き換えて実装する。

def flatten2(L):
    if isinstance(L, list):
       return reduce(lambda a,b: a + flatten(b), L, [])
    else:
        return [L]

print flatten2(L)

 

4. ジェネレータを使って定義する

次に、ジェネレータを使って定義してみる。

Python で順列を生成するときに読んだ

を参考にした。

def gflatten(L):
    if isinstance(L, list):
        for i in xrange(len(L)):
            for e in gflatten(L[i]):
                yield e
    else:
        yield L

print list(gflatten(L))

ジェネレータを使うと、動作のイメージがしにくい。 (+_+) なぜこれで動いているんだろう?

デバッガを利用して動作を見ると、実際に値を取得するために yield が 2 回呼出されているのが確認できる。あたかも、yield 間で値を受渡しているような感じに見える。

 

ジェネレータについて再考

これを理解するために、ジェネレータについて復習。

例えば、次の 2 つのジェネレータを作成。

  • “hoge” と出力する hoge ジェネレータ
  • ジェネレータを受けとると、ジェネレータを返す piyo ジェネレータ

そして、次のようなジェネレータの動作について考える。

  1. piyo ジェネレータに hoge ジェネレータを渡し、
  2. piyo ジェネレータから hoge ジェネレータを呼出すためには、
  3. next () の呼出しを 2 回呼ばなくてはならない。
def hoge():
    yield "hoge"

def piyo(g):
    yield g

print piyo(hoge()).next().next()

 

gflatten 関数の見直し

これを前提に、上記の gflatten 関数を見直す。

次のように、一つの要素 100 があるリストに対して、gflatten関数を適用したとき、

  1. [100] はリストなので、for 文において、 gflatten(100) が実行される。
  2. 再帰呼出しがされたので、gflatten(100) が独立した文脈で実行される。今度は引数が値なので、 yield 100 が実行。これは、next() の呼出しで 100 が返されるジェネレータが作成されたということ。
  3. gflatte(100) の呼出しに戻り、 for 文がジェネレータの next() を呼出し、e に 100 が渡される。これで next() を呼出すと 100 が返されるジェネレータが作成された。
  4. 呼出し元の list 関数において、ジェネレータの next() 呼出しによって値が取り出される。

多分、このような動作かな… QQQ

081001-001

Python でリストに対する再帰的な関数の適用

1. リストに対する再帰的な考え方のポイント

リストに対して、再帰的な関数を適用する場合、次の二つの視点を頭に入れておく。

  1. 先頭要素と、それ以外の残りのリスト。
  2. リストは要素として、リスト、または、値を持つ。

 

2. フラットなリストに対する再帰的な処理

ネストのないフラットなリストに対して適用する関数を考える場合、

  1. 先頭要素
  2. 先頭要素以外の残りの要素

に分けて考える。先頭要素に適用した場合の処理と、それ以外の要素を含むリストに対して、再帰的に関数を適用するように記述する。

080326-002

何もしない関数

一気に考えると脳みその容量をオーバーするので段階的に考える。 (+_+)

まず、フラットなリスト L を受けとったら、そのまま返す関数 map1 を考える。

L = [1,2,3,4,5]

ただし、そのまま返すと言っても、関数を再帰的に適用する。

実装を考えるとき、

  1. 空のリストが渡されたら、空のリストを返す。
  2. リストの先頭要素と、それ以外の残りの要素に再帰的に関数を適用したものを結合する。
def map1(L):
    if L == []:
        return []
    else:
        return [L[0]] + map1(L[1:])

print map1(L)

先頭の要素を取り出した後、後続のリストと結合するため、要素を「要素一つのリスト」にする。

 

関数を適用する関数

次に、リストの各要素に関数を適用する関数に変更する。

引数として要素に適用する関数 f を追加する。

def map1(f,L):
    if L == []:
        return []
    else:
        return [f(L[0])] + map1(f,L[1:])
    
print map1(lambda x: x*2, L)

これで組込みの map 関数のような動作の関数ができた。

 

3. ネストのあるリスト

ネストのあるリスト L の場合を考える。

このとき、リストは Compositeパターン になっていると見なせる。このようなリストに対する関数は、リストと値の場合に分けて考える。リストのときは、その要素に再帰的に関数を適用する。

L = [1,[21,22],3,[41,[421,422],43,44],5]

080930-001

何もしない関数

リストを受けとったら、何もしないで返す関数 map2 を定義する。

def map2(L):
    if isinstance(L, list):
        if L == []:
            return []
        else:
            return [map2(L[0])] + map2(L[1:])
    else:
        return L

print map2(L)

最初に、対象がリストであるか、値であるか判定をしてから処理を進める。

  1. 値であれば、それをそのまま返す。
  2. リストの場合は、先ほどのフラットなリストと同じ処理を行う。ただし、今回は、取り出した先頭の要素が値ではなく、リストである可能性もあるので、それに対して再帰的に関数を適用する。

 

関数を適用する関数

上記の関数を、リストの各要素に適用する関数を引数として受けとるように変更する。

def map2(f,L):
    if isinstance(L, list):
        if L == []:
            return []
        else:
            return [map2(f,L[0])] + map2(f,L[1:])
    else:
        return f(L)

print map2(lambda x: x*2, L)

上記を実行した結果、ネストしたリストにも関数が適用されるようになった。

[2, [42, 44], 6, [82, [842, 844], 86, 88], 10]

 

再帰処理において map を使う

map2 関数の再帰処理はごちゃごちゃしている。 (+_+)

リストである場合の処理において、組込みの map 関数を利用して再帰的に関数を適用してみる。一気に変更するとわからなくなるので、まずは何もしない関数 map2b を定義。

def map2b(L):
    if isinstance(L, list):
        result = []
        for e in L:
            result += [map2b(e)]
        return result
    else:
        return L

こちらの方がコードがシンプルで読みやすい。

同じように、要素に関数を適用できるように変更する。

def map2b(f,L):
    if isinstance(L, list):
        result = []
        for e in L:
            result += [map2b(f,e)]
        return result
    else:
        return f(L)

print map2b(lambda x: x*2, L)

折角なので map で書き直してみる。

def map2b(f,L):
    if isinstance(L, list):
        return map(lambda x: map2b(f,x), L)
    else:
        return f(L)

reduce で書くなら、map の部分を以下のように書ける。

return reduce(lambda a,b: a+[map2b(f,b)], L, [])

 

関連記事