8.29.2013

Scala: Making List of the Result of 'n' Times Function Calls with Stream

Scala: 関数をn回適用した結果のリストを作る

 

scala.collection.immutable.Stream を使うと簡単にできる。

以下は、適当な文字列の左右を「#」で囲む関数を 1回, 2回, ... と適用した結果をリストにして返す例。
(Scala 2.10, REPLで実行) 

 

scala> def f(s: String) = s"#${s}#"
f: (s: String)String

scala> def g(s: String): Stream[String] = s #:: g(f(s))
g: (s: String)Stream[String]

scala> g("x").tail.take(5).toList
res0: List[String] = List(#x#, ##x##, ###x###, ####x####, #####x#####)

 

 

2013-08-30 追記

今回の目的であれば、iterate メソッドを使った方が適しているとご指摘をいただきました。
List#iterate, Iterator#iterate, Stream#iterate を使った例です。 

scala> def f(s: String) = s"#${s}#"
f: (s: String)String

scala> List.iterate(f("x"), 5)(f)
res0: List[String] = List(#x#, ##x##, ###x###, ####x####, #####x#####)

scala> Iterator.iterate("x")(f).drop(1).take(5).toList
res19: List[String] = List(#x#, ##x##, ###x###, ####x####, #####x#####)

scala> Stream.iterate("x")(f).tail.take(5).toList
res26: List[String] = List(#x#, ##x##, ###x###, ####x####, #####x#####)

こちらの方が素敵ですね。

Mac: How to Improve Your Performance with Alfred

Mac: Alfred 活用術

 

Alfred は、Mac を使うなら是非インストールしておきたいフリーソフト。

呼び出し用のキーバインド(私は option + SPACE に設定)を叩いた後、テキストを数文字打って
Enter を押すだけで様々なアプリケーション・OS機能を呼び出すことができる。

WEB のカスタムサーチは特に秀逸で、

  • 「ds キーワード」と打って Dash と連携させたり
  • 「r チケット番号」と打って Redmine のチケットを直接呼び出したり
    • Search URL: (ご利用中のRedmineのURL)/{query}
    • Keyword: r
  • 「a 英単語」または「a 日本語の単語」と打って アルク の和訳・英訳辞書を直接呼び出したり
    • Search URL: http://eow.alc.co.jp/search?q={query}
    • Keyword: a

することが可能だ。 

有料の PowerPack を使えば、より生産性が上がるらしいので目下検討中。

8.26.2013

Mac: How to Use Option as Meta Key in KeySnail

Mac: KeySnail で option キーを メタキーとして使う方法

 

Firefox の (主にEmacsユーザ向けの) キーボード・ブラウジング環境構築プラグイン KeySnail を使っている。

これを Mac 環境で使用したところ、option キーをメタキーとして使おうとしてもうまくいかず、
メタキーを使うキーバインドが一切動作しない状況に陥った。

試行錯誤の末、正しく動作するようになったのでその方法を記録しておく。

 

環境

  • Hardware: MacBook Air (USキーボード)
  • OS: OS X 10.8
  • Firefox: 23.0.1
  • KeySnail: 2.0.1

事象

デフォルト状態の KeySnail では、Altキー・Command キーの入力がメタキーとして解釈される。
keysnail/wiki/howto.ja.wiki at master · mooz/keysnail

Firefox では、Mac の optionキーが Altキーの働きと同等となるので、
optionキーを使ってメタキーのキーバインド (M-w, M-x など) ができると期待した。

(Command キーをメタキーとして扱うのは違和感があるのであまり使いたくない。) 

ところが optionキーを使ったメタキー操作は全く動作しない。

  • control と option を同時に押した時のキーバインドは正しく動作する。(C-M-r など)
  • C-q でエスケープしたあとに option+x などと入力しても、表示されるのは「null」

 

原因

OS X では (デフォルト状態では)、option キーを押しながら他のキーを入力すると特殊文字(å∫ç∂...)の入力が
行われる。

これらのキーを押下したときのイベントでは event.altKey が true にならないので
メタキーかどうかを判定する key.isMetaKey 関数を素通りしてしまう。

 

対応方法

key.keyEventToString 関数をフックして、イベントを特定するための文字列を書き換えればそれなりに動くようだ。

charCode を取得し、特殊文字 かどうかを判定した上で "M-" で始まる文字列に置換する。
(例えば、∑ (charCode=8721) が入力されたらなら "M-w" に置き換える)

このような処理を行う option-as-meta というプラグインが既に存在していたので、それをそのまま利用する。

これで optionキーをメタキーとして使えるようになった。

 

補足

optionキーではなく ESCキーをメタキーとして使用したいのであれば、「Metaplus」プラグインを利用するのが
最も確実な方法と思われる。

Plugin · mooz/keysnail Wiki 

 

8.24.2013

How to Use Option as Meta Key in iTerm2

iTerm2 で Mac の option キーを Meta キーとして使う方法

iTerm -> Preferences -> Profiles -> Keys

Preferences

設定としては、「Meta」よりも「+Esc」が推奨されるとのこと。

これで Emacs 周りの操作性が改善された。

Python: Increasing All Numbers in Text File

Python: テキスト中の数字を全てインクリメントする

目的

Vim における Ctrl-A (Increasing or decreasing numbers - Vim Tips Wiki) のように
テキスト中の数字を全てインクリメントする処理を Python で行いたい。

コード

textprocessor を利用する。

正規表現モジュールの split 関数を利用して1行分の文字列を「数字部分」と「数字以外」に切り分けて、
数字部分のみを +1 することでシンプルに書けた。 

今回は「0」で始まる2文字以上の数字(00, 01 など)は特別にインクリメント対象から除外している。

また、マイナス記号は単なる「数字以外の文字」として扱われるため、負数のインクリメントには対応していない。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import re
import textprocessor

tokenizer = re.compile(r'(\d+)').split


def convert(token):
    if token != '0' and token.startswith('0'):
        return token
    return str(int(token) + 1)


def increment(line):
    tokens = tokenizer(line)
    tokens = [x if i % 2 == 0 else convert(x) for (i, x) in enumerate(tokens)]
    print(''.join(tokens))


if __name__ == '__main__':
    textprocessor.process(increment)

実行例

  • 準備したファイル
    a
    1
    b2
    3c
    d4e
    5f6
    7 8 9
    w0rd
    -5 -4 -3 -2 -1 0 1 2 3 4 5
    [0, 00, 01, 02, 03, 04, 05]
    0.0 1.1 2.2 3.3 4.4 5.5
    9.99 999.9999 99999.999999
    
  • 実行結果
    $ ./increment.py ./test.txt
    a
    2
    b3
    4c
    d5e
    6f7
    8 9 10
    w1rd
    -6 -5 -4 -3 -2 1 2 3 4 5 6
    [1, 00, 01, 02, 03, 04, 05]
    1.1 2.2 3.3 4.4 5.5 6.6
    10.100 1000.10000 100000.1000000
    
GitHub

 

Related Posts

Python: Helper Function for Text Processing

Python: テキスト処理のためのヘルパー関数

AWKのようなテキスト処理に特化した作業を Python で楽に行うための関数を作った。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Helper function for text stream processing
"""

import sys


def process(function):
    paths = (sys.argv + [None])[1:max(2, len(sys.argv))]
    for path in paths:
        try:
            fp = sys.stdin if path is None else open(path)
            for line in fp:
                function(line.rstrip("\n"))
        except (KeyboardInterrupt, EOFError):
            pass
        except Exception:
            exc_type, exc = sys.exc_info()[:2]
            print('%s: %s: %s' % (sys.argv[0], exc_type.__name__, exc))

コマンドライン引数が指定されない場合は標準入力から、指定された場合はそれぞれの引数をファイルパスと
見なして全ての入力を順に1行ずつ読み込み、何らかの処理を行う。

process 関数の引数にはパラメータを1個取る関数を渡す。
そのパラメータには、読み込まれた入力が1行ずつ、末尾の改行が除去された文字列として渡される。 

 

利用例

入力をそのまま標準出力へ出力するだけの処理

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import textprocessor


def identity(line):
    print(line)


if __name__ == '__main__':
    textprocessor.process(identity)

 

実行例
    • 準備したファイル
line1
line2

 

  • 標準入力から流し込む

 

$ cat ./test.txt | ./identity.py
line1
line2

 

  • ファイルパスを指定して実行

 

$ ./identity.py ./test.txt
line1
line2

 

  • 複数のファイルを指定して実行

 

$ ./identity.py ./test.txt ./test.txt
line1
line2
line1
line2

 

  • エラーケース

 

$ ./identity.py xxxxxx
./identity.py: IOError: [Errno 2] No such file or directory: 'xxxxxx'

 

Related Posts

8.23.2013

Getting Started with KeySnail - Mouseless Browsing on Firefox

Firefox + KeySnail でマウスレス・ブラウジングを始める

 

今更ながらマウスレス・ブラウジングの便利さに目覚めた。

Firefox でのキーボード操作拡張プラグインは以下の2つが主流のようである。

Emacs 修行中の私としては、KeySnail を選ばざるを得ない。

 

本体のインストール

こちらのページから keysnail.xpi をダウンロードして Firefox で開き、インストールする。

keysnail japanese · mooz/keysnail Wiki

とても簡単。

 

初回起動

Firefox を再起動すると、設定ファイルを新規作成するか尋ねられる。

新規作成の場合、設定ファイルのパス、デフォルトのキーバインドを選択。
今回はとりあえず Dropbox 上にファイルを作っておいた。 

Screenshot 8 23 13 03 14

 

プラグイン導入

今日のところは、キーボードでリンク遷移を行うための HoK をインストールするところまで。

  • HoK
    こちらのページから、HoK を右クリックして Install this plugin。
    Plugin · mooz/keysnail Wiki 

    インストールが済んだら、.keysnail.js (_keysnail.js) の末尾に以下の内容を追記。
    key.setViewKey('e', function (aEvent, aArg) {
        ext.exec("hok-start-foreground-mode", aArg);
    }, 'Hok - Foreground hint mode', true);
    
    key.setViewKey('E', function (aEvent, aArg) {
        ext.exec("hok-start-background-mode", aArg);
    }, 'HoK - Background hint mode', true);
    
    key.setViewKey(';', function (aEvent, aArg) {
        ext.exec("hok-start-extended-mode", aArg);
    }, 'HoK - Extented hint mode', true);
    
    key.setViewKey(['C-c', 'C-e'], function (aEvent, aArg) {
        ext.exec("hok-start-continuous-mode", aArg);
    }, 'Start continuous HaH', true);
    
    key.setViewKey('c', function (aEvent, aArg) {
        ext.exec("hok-yank-foreground-mode", aArg);
    }, 'Hok - Foreground yank hint mode', true);
    

    そして、設定ファイルをリロードすれば準備完了。(Tools -> KeySnail -> Reload init file)

    Webサイトを開いて「e」キーを押せば、各リンク先に対応したショートカットキーが表示され、
    そのキーボード操作でリンクを開けるようになる。 

 

これから慣れていきたい。

 

References

 

8.22.2013

How to Convert Int to ByteArray in Python

Python: 数値などをバイト配列に変換する方法

Python 2.5 より登場した struct モジュールを使うと便利。

詳細は以下を参照。
7.3. struct — 文字列データをパックされたバイナリデータとして解釈する — Python 2.7ja1 documentation 

下記は全てリトルエンディアンのマシン下での実行例。

>>> import struct
  • Int からバイト配列の変換
    >>> struct.pack('i', 1)
    '\x01\x00\x00\x00'
    >>> [struct.pack('i', x) for x in (0, 1, 65, -1, 2000000000)]
    ['\x00\x00\x00\x00', '\x01\x00\x00\x00', 'A\x00\x00\x00', '\xff\xff\xff\xff', '\x00\x945w']
    >>> [len(struct.pack('i', x)) for x in (0, 1, 65, -1, 2000000000)]
    [4, 4, 4, 4, 4]
  • ビッグエンディアンで格納
    >>> struct.pack('>i', 1)
    '\x00\x00\x00\x01'
    >>> struct.pack('!i', 1)
    '\x00\x00\x00\x01'
    
  • Int 以外の型のエンコード
    >>> struct.pack('3?Q', True, False, True, 5L)
    '\x01\x00\x01\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00'
    >>> struct.pack('dc10s', 0.025, 'X', 'python')
    '\x9a\x99\x99\x99\x99\x99\x99?Xpython\x00\x00\x00\x00'
  • バイト配列から Int へのデコード
    >>> struct.unpack('i', '\x01\x02\x03\x04')
    (67305985,)
    
    要素が一つでもタプルが返る。
  • Struct クラスを使う
    >>> s = struct.Struct('i')
    >>> s.pack(1)
    '\x01\x00\x00\x00'
    
    フォーマット文字列がコンパイルされた状態で保持される。
    同じフォーマットを何度も使い回す場合に効率的。

8.04.2013

How to Find Invisible Characters in Emacs/Vim

Emacs/Vim: 特定のASCIIコードを検索する方法

0x0c (改ページ) を検索する場合

  • Emacs
    Ctrl-s Ctrl-q Ctrl-l
    または
    Ctrl-s Ctrl-q 1 4 Enter
    (8進数で指定する(0x0c(16進)=014(8進))、16進で指定する方法はないのだろうか?) 
  • Vim
    /\%x0c

Handling UTF-8 in Python 2.x

Python 2系で UTF-8 を取り扱う際の心得

 

Python 2系での日本語の扱い方について、忘れた頃に忘れてしまうのでメモ。

ソースで日本語(UTF-8)を使う

まずは基本から。

ソースの1行目または2行目にコメントを入れ、正規表現 coding[:=]\s*([-\w.]+) にマッチする文字列を書けばよい。

普通は以下のように書く。

# -*- coding: utf-8 -*-

shebang 付き (ここで Python のバージョンを指定したほうがよい場合もある)

#!/usr/bin/env python
# -*- coding: utf-8 -*-

 

3つの文字列クラス

Python 2系の場合、今扱っているオブジェクトが何者かを意識することが大切。

  • str
    ASCII文字列向けに設計された文字列クラスだが、その中にはテキスト以外も格納できるので
    実態はバイト配列のようなものとなっている。
    >>> 'abc'
    'abc'
    >>> '\x03'
    '\x03'
    >>> 'あいう'
    '\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86'
    >>> type('abc')
    <type 'str'>
    
  • unicode
    あらゆる文字コードのUnicode文字列を抽象化したクラス。 
    >>> u'abc'
    u'abc'
    >>> u'\x03'
    u'\x03'
    >>> u'あいう'
    u'\u3042\u3044\u3046'
    >>> print u'\u3042\u3044\u3046'
    あいう
    >>> type(u'abc')
    <type 'unicode'>
    
  • basestring
    基底の抽象クラス。
    文字列クラスかどうかを判定する場合に使用。
    >>> isinstance('abc', str)
    True
    >>> isinstance(u'abc', str)
    False
    >>> isinstance('abc', basestring)
    True
    >>> isinstance(u'abc', basestring)
    True

 

2つのメソッド

str 型から unicode 型への変換をデコード、その逆をエンコードと呼ぶ。
その変換処理は str クラスのメソッドとして定義されている。

これらのメソッドは、encoding と errors という 2つの引数を取る。

  • encoding: 変換に使用する codec 名。
        デフォルトは defaultencoding の値。
        defaultencoding は sys.getdefaultencoding() で取得できるもので、そのデフォルトは 'ascii'。 
  • erros: エラーハンドラ名。デフォルトは 'strict' (変換に失敗したら例外を発生)

 

明示的にエンコードする

厄介なエンコードエラーが発生する主な要因は、暗黙的なエンコード・デコード処理と
エンコーディングのミスマッチにある。 

>>> str(u'あいう')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)

これは前述の str.encode() メソッドにおける encoding が省略された状態で実行されたのと同じ現象が起きている。
この場合は utf-8 でデコードされた unicode オブジェクト(u'あいう')が、
defaultencoding である ascii でエンコードされたためにエラーとなってしまったのだ。

とはいえ実行時に defaultencoding を変更するのは、不可能ではないが特別な理由が無い限りすべきではない。
他のモジュールへもその影響が波及してしまうため、思わぬトラブルを引き起こすリスクがある。

解決策の一つは、以下のように明示的にエンコード・デコードを行うこと。そうすればエラーは起こらない。

>>> str(u'あいう'.encode('utf-8'))
'\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86'

"Zen of Python" の『曖昧さにあたっては推測をしたがるな』に従おう。 

>>> import this
(略)
In the face of ambiguity, refuse the temptation to guess.
(略)

 

IO境界でラップする

Unicode の取り扱いに関するたった一つのベストプラクティスは、IO境界 --
つまりファイルの読み書きや、ネットワーク通信、画面出力などのタイミングで変換することだ。

 

  • 入力のタイミングで、str から unicode にデコード
  • 出力のタイミングで、unicode から str にエンコード
  • 内部処理は全て unicode 文字列として取り扱う

Python 2.6 から利用可能になった io モジュールを使えばラッピングを簡便にしてくれる。

  • ファイル入出力
    >>> import io
    >>> with io.open(FILEPATH, mode='w', encoding='utf-8') as file:
    ...     file.write(u'あいう\n')
    ...
    4L
    
  • 標準入出力
    標準出力を io.open() で開き直せば、適切なエンコーディングが選択される。 
    >>> import io, sys
    >>> stdout = io.open(sys.stdout.fileno(), 'w')
    >>> stdout.write(u'あいう\n')
    あいう
    4L
    
    io.open() の場合、encoding パラメータを指定しなくても、環境に応じてよしなに
    エンコーディングを判断してくれるのが素晴らしい。
    実際には後述の locale.getpreferredencoding() の値が参照されているようだ。

Python クックブック(1.22章)には codecs.lookup を使った以下のような方法も掲載されていた。

>>> import codecs, sys
>>> old = sys.stdout
>>> sys.stdout = codecs.lookup('utf-8')[-1](sys.stdout)

 

環境に応じたエンコーディングを取得する

Python が実行された環境の標準的なエンコーディングを動的に取得するにはどうすればよいか。

完全な方法ではないものの、以下の2種類のアプローチを順に試せば大半の環境で意図した動作となるようだ。

  • ファイルの encoding 属性
    属性が存在しない場合もあるので、getattr() を使ったほうが安全。 
    • 例) Mac (utf-8)
      >>> import sys
      >>> getattr(sys.stdout, 'encoding', None)
      'UTF-8'
    • 例) Windows (SJIS)
      >>> import sys
      >>> getattr(sys.stdout, 'encoding', None)
      'cp932'
    • 例) Solaris (SJIS)
      >>> import sys
      >>> getattr(sys.stdout, 'encoding', None)
      >>>
  • locale.getpreferredencoding()
    • 例) Mac (utf-8)
      >>> import locale
      >>> locale.getpreferredencoding()
      'UTF-8'
    • 例) Windows (SJIS)
      >>> import locale
      >>> locale.getpreferredencoding()
      'cp932'
    • 例) Solaris (SJIS)
      >>> import locale
      >>> locale.getpreferredencoding()
      'PCK'

 

まとめ

  1. まず始めに、プログラムの設計段階で全てのIO境界を洗い出す

    (図に書くのが一番よい)
     
  2. それぞれのIO境界において、どのエンコーディング(utf-8等)で
    変換(decode, encode)を行うのか整理する

    (同時に、環境に依存する部分や動的に取得する部分を明確にする)
     
  3. 明示的な変換(decode, encode)、明示的なエンコーディングの指定をする

    (実行環境の defaultencoding (デフォルトはUS-ASCII) に依存しない実装をする)
    (ライブラリ or 自作のラッパーを利用してもよい)
     
  4. それでも UnicodeDecodeError, UnicodeEncodeError が発生してしまったら、
    変換前のデータ・データ型・変換しようとしたエンコーディングを再確認する 

 

 

References