Hatena::ブログ(Diary)

None is None is None このページをアンテナに追加 RSSフィード

2012-05-17

逆FizzBuzz問題

近未来、2016年(・・・って、中途半端に近い未来ですね)では、

Googleの入社試験では、逆FizzBuzz問題を解くことになるそうな。

fizzbuzzした結果のリストから、それを生成するような最短の数値の範囲を求める、という問題です。

f:id:doloopwhile:20120517221434p:image

Pythonでやってみた感想は「やっぱり無名関数が欲しいなぁ」。名前を付けたほうが後々役立つのはそうなんでしょうが、こういった処理ではイマイチ良い名前が浮かばないことも多い。コードで言うと、candidatesとか。

#!python3
# -*- coding: ascii -*-
#invertfizzbuzz.py
"""
>>> from invertfizzbuzz import invert_fizzbuzz
>>> invert_fizzbuzz("fizz".split())
range(3, 4)
>>> invert_fizzbuzz("buzz".split())
range(5, 6)
>>> invert_fizzbuzz("fizz buzz".split())
range(9, 11)
>>> invert_fizzbuzz("buzz fizz".split())
range(5, 7)
>>> invert_fizzbuzz("fizz buzz fizz".split())
range(3, 7)
>>> invert_fizzbuzz("fizz fizz".split())
range(6, 10)
>>> invert_fizzbuzz("fizz fizz buzz".split())
range(6, 11)
"""

from iterutils import unique_everseen, count

def fizzbuzz(x):
    if x % 15 == 0:
        return "fizzbuzz"
    elif x % 3 == 0:
        return "fizz"
    elif x % 5 == 0:
        return "buzz"
    else:
        return x

def fizzbuzz_words_with_length(start, length):
    words = []
    for n in count(start):
        v = fb(n)
        if isinstance(v, str):
            words.append(v)
        if len(words) == length:
            return words, n + 1


def invert_fizzbuzz(words):
    words = list(words)
    if not words:
        return range(1,1)
    
    start_points = [3, 5, 6, 9, 10, 12, 15]
    invert_fizzbuzz = {}
    candidates = []
    for begin in start_points:
        fb, end = fizzbuzz_words_with_length(begin, len(words))
        if fb == words:
            candidates.append(range(begin, end))
    return min(candidates, key=len)

if __name__ == "__main__":
    import doctest
    doctest.testmod()

就職してから1回もブログを更新してないぜ。

2012-03-22

setup.pyについてのいくつかのメモ

モジュールにソースコード以外のファイルを添付する

添付の種類は4種類あります.

(1 - 3) setupの引数として指定する(scripts, package_data, data_files).

setup.py内のsetup関数引数としてファイルを指定します. scripts, package_data, data_filesの違いは, インストール先です.

scripts
Scriptsフォルダ(C:\Python32\Scripts)
data_files
パッケージ(C:\Python32\Lib\site-packages\(パッケージ名))
package_data
サブパッケージ(C:\Python32\Lib\site-packages\(パッケージ名)\(サブパッケージ))

括弧内はPython 3.2.x Windows版の場合の位置です.


次のようなsetup.pyを書いて, python setup.py installすると…

from setuptools import setup

setup(
    #パッケージの定義, 2つのサブパッケージを含む
    name="foods",
    packages = ["fish", "meat"],
    package_dir={'fish':'fish', "meat":"meat"},
    
    #サブパッケージごとのデータファイル
    #元々のフォルダ構造を反映する
    package_data={
        "fish": ["red/tuna.js"],
        "meat": ["beaf.js"],
    },
    
    #サブパッケージには所属しないデータファイル
    #元々のフォルダ構造は無視される
    #("配置先フォルダ名", ["ファイル", "ファイル", "ファイル"...]), ... の形式
    #配置先フォルダはネストしてよい
    data_files = [
        ("raw-fish", ["salmon.txt"]),
        ("fishery/processing/kamaboko", "sasa.bmp kamaboko/yaki.rb".split()),
    ],
    
    #スクリプトファイル
    #元々のフォルダ構造は無視される
    #配置先もScriptsの直下のみ
    scripts = "sashimi.sh sh_files/arajiru.bat".split(),
    
    #zip_safe=Trueにすると, 全てのファイルを単一の.egg(zipファイル)にまとめる
    #デフォルトでは, zip_safe=True
    zip_safe=False,
)

#ちなみに, str.split を使うと perlrubyの%w() のように
#空白で区切って, 文字列のリストが作れるので便利

こういうふうに配置されます.

C:\Python32\lib\site-packages\seafoods\EGG-INFO\scripts\arajiru.bat
C:\Python32\lib\site-packages\seafoods\EGG-INFO\scripts\sashimi.sh
C:\Python32\lib\site-packages\seafoods\fish\red\tuna.js
C:\Python32\lib\site-packages\seafoods\fish\__init__.py
C:\Python32\lib\site-packages\seafoods\fishery\processing\kamaboko\sasa.bmp
C:\Python32\lib\site-packages\seafoods\fishery\processing\kamaboko\yaki.rb
C:\Python32\lib\site-packages\seafoods\meat\beaf.js
C:\Python32\lib\site-packages\seafoods\meat\__init__.py
C:\Python32\lib\site-packages\seafoods\raw-fish\salmon.txt
C:\Python32\Scripts\arajiru.bat
C:\Python32\Scripts\sashimi.sh

(4) MANIFEST.in

READMEやHISTORYのように, パッケージの使用時には役に立たないが, sdistには含めたいようなファイルは, MANIFEST.inファイルに記述します.

include README
include HISTORY
include junk/food/*.txt

ワイルドカードも使用可です.


落とし穴

1 - 3 の setup引数に指定する方法ですが, そのままではpython setup sdistした時に, ziptarにファイルが追加されません!

少なくとも, 私が試した限りではそうでした. なんてこった!バグか!?

ziptarに追加するには, MANIFEST.inに含めたいファイルを全て書き下す必要があります. 二度手間ですが, 仕方ありません.


参照

これらについての正確な情報は,

などをご覧下さい.

2012-02-04

Python 2.6との後方互換性を保つための4種のTips または, 2.7の新機能紹介

Pythonは2.7が2010年7月にリリースされ, 現在では2.8をリリースするかが問題になっているようですが, 今でも旧バージョンである2.6への需要は存在するようです.

ここでは, 2.6互換にするために注意する点を紹介します.

1. 標準ライブラリ

当たり前ですが, 2.7で新しく入ったライブラリは2.6にはありません. しかし PyPIを探せば, 同等品があるはずです.

例えば, collections.OrderedDictの場合は, ordereddictがあるので,

try:
    from collections import OrderedDict
except ImportError
    from ordereddict import OrderedDict

とすれば, 2.6でも動きます.

他にも, argparseやunittestの新機能(PyPIではunittest2)などが該当します.

2. with文

2.7(や, 3.x)では, with文に複数の as を書くことができます.

with open("a.txt") as fa, open("b.txt") as fb:
    # faとfbを処理…

これは, 2.6にはありません. 代わりに, contextlib.nestedを使います.

from contextlib import nested

with nested(open("a.txt"), open("b.txt")) as (fa, fb):
    # faとfbを処理…

3. 辞書内包表記

{i:2**i for i in range(10)}

辞書内包表記は, 2.6用のコードでは, dict()を使うよう気をつけましょう.

dict(i:2**i for i in range(10))

4. 文字列format

2.7では文字列フォーマットの, 番号を省略できます.

'{}-{}.txt'.format(3, 14) # => '3-14.txt'

しかし, 2.6では番号は省略出来ません. 必ず明示的に各必要があります.

#2.6互換な書き方. 番号を明示する.
'{0}-{1}.txt'.format(3, 14) # => '3-14.txt'

辞書内包表記と違って, formatを間違えた時は, シンタックスエラーにならないので注意が必要です.

ただし, そもそも番号で指定するよりも, キーワード引数で指定し, その値が何を表すのか明示したほうが良いかも知れません.

#ベターな書き方. これも2.6互換
'{month}-{day}.txt'.format(month=3, day=14) # => '3-14.txt'

2.5との後方互換

2.5と3.xの両方で動くコードを書くのは困難です.

なぜなら,

from __future__ import unicode_literals

が動かないので, 文字列関係で大規模な変更が必要になるかもしれません.

2.5〜2.7の互換性に限るならば, 2.5では使えないprint関数など避けて, ライブラリPyPIからインストールすれば可能です.

またwith文も,

from __future__ import with_statement

とすれば, 2.5でも使用可能です.

2012-01-28

グローバル変数は必要です

Life is beautiful: Python Hack : 噛めば噛むほどおいしくなるクロージャの話クロージャと無名関数を使うと、こんな風に実装することができる。

    import yaml

    def _get_from_disk():
         data = open('config.yaml').read().decode('utf8')
         config = yaml.load(data) # クロージャ内に隠蔽・保持されるローカル変数
         global get
         get = lambda : config   # 二回目からはconfigを返す無名関数を呼ぶ様に変更
         return get()

    get = _get_from_disk   # 初回のみローダーを実行

いやいや、それはいけません!

このコードは、config.py というモジュールの一部で、

import config

#config.get()を使って処理

という使い方を想定していますが、こういう使い方も出来てしまいます。

from config import get as config_get

#get()を使って処理

こうすると、config_getの参照先は、config.pyの中でどんな処理をしようと、_get_from_diskのままなので、config_getを呼び出すと毎回ファイルを読み込むことになってしまいます。


同じ問題は、メソッドでも発生します。メソッドオブジェクトを変数に代入した場合です。たとえば、

#!python3
# -*- coding: cp932 -*-

class Config:
    def get(self):
        self.get = lambda : 1 #2回目以降は1を返すつもり
        return 0 # 初回のみ0を返すつもり

c = Config()
get = c.get #メソッドを変数に代入

print(c.get()) # => 0
print(c.get()) # => 1
print(get())   # => 0

というわけで、「初回のみ〜する」場合は、諦めてグローバル変数やメンバ変数を使いましょう。

全ての大学は職業教育の場です!

ちょうど気になる記事が紹介されたので、

修士論文の代わりに退学願を提出してきた http://blog.riywo.com/2009/02/27/120733

私自身も、就職活動に七転八倒した口で、昨日修士論文を提出し終わった所なので、思うことを書いてみます。

学問の府?

「やる気がある学生だけ来ればいい」「大学は学問の府」という意見は、しばしば重要な点を見落としています。それは、研究者になるのは学生のうちほんの僅かという点、そして研究者にならないからと言って劣った学生とは限らない点です。

続きを読む

2012-01-20

声に出して読みたい7つのPython用語

冷凍マグロスクリプト言語として知られるPythonの、もっともすばらしい用語7点をご紹介します。これらのネーミングセンスを身につけることによって、PHPerやRubyistでは不可能な変態力を身につける事が出来るでしょう(Perl Mongerには負けるかも知れない)。

PyPI(ぱいぱい or ぱいぴいあい)

PyPIPythonライブラリを誰でも登録・ダウンロードできる、PerlCPANRubygemに当たるものです。4月になったら新人プログラマーに教えてあげましょう。

例:PyPIは基本だ!

PIL(ぴる)

PILは画像処理用のライブラリです。高度な処理はできませんが、手軽に画像を扱う事ができるので、簡単な処理にもフォトショを使っている人を見たらこう言っておきましょう。

例:PILは絶対使っとけ!

なお、Pillow(ぴろー)(英語で枕の意)というforkがあり、インストーラでしかインストール出来ないPILと違い、PillowはPyPIからeasy_installできます。

PyPy(ぱいぱい)

PyPyはPythonの実装の一つです。C言語ではなくRPythonというPythonのサブセットで書かれています。近年JITコンパイラによる高速化で注目されており、PyPyのベンチマークを見たあなたは思わずこう叫ぶはずです。

例:PyPyには感動した!

Pyzuri(ぱいずり)

PyzuriはPythonからpixivを扱うことができるライブラリです。IDから画像やユーザーの情報を取得できます。pixivのデータが欲しい人は、一度Pyzuriを試してみましょう。すると、こんな感想を抱くかも知れません。

例:Pyzuriにトライしたけど、良かったよ!

pypants(ぱいぱんつ)

pypantsはPythonライブラリPyPIgithub登録できるWEBサービスです。そんなに流行っているサービスでは無いというか、ぶっちゃけ使い方がよくわからないのですが、コミット頻度などが見れるのが役立つのかも知れません。

例:pypantsは見たのか!?

Pychinko(ぱいちんこ)

PychinkoはRete algorithmのライブラリで、CWMクローンらしいです。ぶっちゃけ、どう使うライブラリなのかイマイチわかりません。日本ではその印象的な名前のみが知られている状態のようです。でも、きっと、必要な人には必要なのでしょう。SVNは落ちてますけど。

例:Pychinkoを試してみるぞ!

pypan(ぱいぱん)

pypanはPythonのパッケージマネージャーです、以上!distributeやpipとはどう違うのか、とかは分かりません。だって、PyPIにはソースは置いてないし、ホームページも404なんだもん。でも、完成の暁には、思わずこう叫んでしまうでしょう。

例:pypanじゃなきゃダメなんだ!


実践編

明日の朝職場に出たら、これらの例文を大きな声で叫びましょう。あなたが真のPython系(下半身的な意味で)プログラマーとして認められ、名誉の懲戒免職処分が下ること請け合いです。

え?もしクビにならなかったら?むしろ、賛同の声が上がったら?……その職場はもう駄目なのかも知れません。


オマケspam(スパム)

Spamです。SpamPythonプログラマーspam力の源泉ですから、直ちにeasy_install spamに失敗した後, spam 3.0 : Python Package Indexでそのspamっぷりを感じるべきスパム

例:Spamspamspamspamspamspamspamspamspamspamspamspamspamspam