]> Panopticon :: 2007年5月 Archive

推薦で受けた某メーカに内々定をいただけました。就活終了です。

筆記試験のマークシートの半分をマークせずに出す(解答にチェックはしていた)というありえないミスをしてしまい完全に諦めていたのですが、おそらく人力採点だったのでしょう。温情のある会社です。来年から馬車馬のように働きたいと思います。

みんなの就職活動日記から、内定者コミュニティに参加してみることにしました。楽天のSNS内にコミュがあるようです。

友達はいません

友達がほしいです。

アメリカのプログラミングコンぺ、TopCoderに参戦してみました。TopCoder内ではいくつかの競技があるようですが、僕が参加したのはAlgorithmという制限時間内に問題を解き早さを競うというものです。三問出題され、それぞれ早く解けば解くほど高い点数がもらえ、参加者全員に点数に応じたレートがつけられます。使用できる言語はJava、C++、C#、VBですが、基本的にプログラムの速さは得点に関係ありません(でも1入力に2秒以上かかるものは遅すぎるのでアウト)。とにかく早く提出するのが全てなので、得意なもので書けばいいと思います。僕はJavaです。全然得意じゃないですけど。

まず、参加者は大きく二組にわけられます。それまでのレートが1200以上の人はDiv1、それ以下や初参加の人はDiv2。僕は初参加なのでDiv2です。当然こちらのほうが問題もやさしい。レートはDivisionの中の相対評価でつけられるようです。

それぞれのDivisionで、さらに20名ずつのグループにわけられます。競技はこのグループの中で行われることになります。問題は三問(それぞれ250点、500点、1000点)。最初の75分でこれらを全て解きます。休憩をはさみ、15分間でグループ内の他人のコードを見てその間違いを探します(他人のミスを見つけるとポイントがもらえます)。ここで競技は終了となり、各解答に対してシステムのチェックが入ります。これに無事パスすると正式にポイントが与えられ、レートがつけられます。

250ポイント問題
文字列を検索する系。 5分くらい。230ポイント獲得。

500ポイント問題
二円の交点の数を求める系。 8分くらい。410ポイント獲得。

1000ポイント問題
ワードパズルみたいなもの。 一時間以上考えた。例示された入力に対しては正しい答えが返せるが、メソッド内で再帰していたためワーストケースどころかちょっと長い入力に対してはタイムアウト。とりあえずぎりぎりにダメもとで提出してみた。420ポイント獲得。

The Challenge Phase
他人のバグ探しをする時間。ここが一番楽しかった。バグをみつけたらそれを誘発するようなテストケースを作成して送り込んでやる。うまく行けば+50ポイントで相手のポイントは帳消しになるが、失敗すると-25ポイント。この撃墜も早いもの勝ちだ。とりあえず1000点問題については自分の解答がタイムアウトすることがわかっていたので似たようなものがないか探す。再帰してるヤツがいた!しかしその解答に対してタイムアウトする入力を作っている間に他の人に撃墜されてしまった。メインの画面に戻ったら、僕の1000点問題も撃墜されていてワロス。-420ポイント。最終的に一つバグを見付けて撃墜。50ポイント獲得。解答者は日本人の方でした。でも戦場だから!

The System-Testing Phase
競技が終った時点で690ポイントくらいだったのだが、システムチェックで500点問題が落ちた…。500点問題は作っている途中に穴に気づいたのでそれを埋めて、Challenge Phaseでも二人の攻撃を弾いていたので大丈夫だと思っていたが甘かった。-410ポイントで最終的な結果が280ポイントと大変残念なことになってしまいましたが、それでもなんとかDiv1にいけるくらいのレートがつきました。250ポイント以下は団子になるので、それをわずかに超えていたことが幸いしたのでしょう。しばらくDiv1に居られるように頑張りたいと思います。

謎解き色が強くなってきたので、コードは載せません。6は強敵だった。つーかPythonで解きました。

Lv4

文字列で示されたURIをたどる

ex. "http://www.pythonchallenge.com/***/***?***=12345"というページの内容が
"*** *** **** *** 67890"であれば、
"http://www.pythonchallenge.com/***/***?***=67890"を開く。これを繰り返す。

Haskell HTTP packageを使う。ここのexample、test/get.hsをちょっと改変。

main = do
  id <- getLine
  getLinkedList id
getLinkedList id = case parseURI $ urip ++ filter (/= '\n') id of
  Nothing -> err "Could not parse URI"
  Just uri -> do
    cont <- get uri
    putStrLn cont
    getLinkedList $ head $ reverse $ words cont
  where
    urip = "http://www.pythonchallenge.com/***/***?****="

ここ以外はtest/get.hsをそのまま。実行して末尾の数字を入れると動くけど……。getのたびにpythonchallenge.comに接続しなおしてるっぽいので遅い。Networkパッケージ本体を使えばもっと低レベルなところから作れるようだ。

Hackage DB

The Glorious Glasgow Haskell Compilation System User's Guide: 4.8. Packages

Network Libraryをインストールしてみる。

Cabalというパッケージ管理ツールがghcに付属している。圧縮ファイルを落して解凍すると、Setup.hsというものができる。これがhaskell版makeみたいなものなのかな。ディレクトリに移動して

$ runhaskell Setup.hs configure
$ runhaskell Setup.hs build
# runhaskell Setup.hs install

これだけ。最後のはスーパーユーザで。

インストールしたLibraryのリストは

ghc-pkg list

で確認できる。

このとき、()に囲まれているものは不可視になっており、単にimportしただけだとコンパイル時に認識されないので、

$ ghc -package packagename filename.hs

と引数で指定してやるか、

# ghc-pkg expose packagename

として可視にする必要がある。

interact :: (String -> String) -> IO()
interact f = do x <- getContents
                putStr (f x)

文字列をとって文字列を返す関数fを引数とし、fを入力に適用したものを出力する。これは便利。

Python Challenge(1)

import Char

shift :: Char -> Char 
shift a
 | a == 'z' = 'a'
 | isLower a = chr $ ord a + 1
 | otherwise = a

main :: IO()
main = do x <- getContents
          putStr $ last $ take 3 $ iterate (map shift) x

main :: IO()
main = interact $ last . take 3 . iterate (map shift)

Python Challenge(3)

import Text.ParserCombinators.Parsec 

bodyguards :: Parser String
bodyguards = try ( do { upper; upper; upper ; c <- lower
                ; upper; upper; upper; many1 lower; return [c] })
            <|> do { skipMany1 upper ; skipMany1 lower ; return ""}

bg :: Parser String
bg = do { many lower ; r <- many1 (try bodyguards)
        ; many upper ; eof ; return $ concat r }

readBdGrs :: String -> String
readBdGrs input = case parse bg "" input of
   Left err -> "No match: " ++ show err
   Right val -> show val

main :: IO()
main = do x <- getContents
          putStrLn $ readBdGrs $ filter (/= '\n') x

main :: IO()
main = interact $ readBdGrs . filter (/= '\n')

RSSを登録する際、そのURIがRSSのものかを判定したい。フォーム入力内容の確認を行うには、バリデータを使えばいいようだ。validators.pyにはよく使われそうないくつかのバリデータが定義されているが、ここではfeedparserが拾ってくるversionによって判別を行うものを作ってみることにした。

Django オンラインドキュメント和訳: フォームとフィールド,マニピュレータ

UNIVERSAL FEED PARSER: Feed Type and Version Detection

feedparserによってRSSのversionが判別できないと空列が返ってくるので、そのときはValidationErrorを投げてやればよい。models.pyを少し書き換える。

from django.db import models
from django.core import validators
import feedparser

# Create your models here.
class Site(models.Model):
        title = models.CharField(blank=True, maxlength=100)
        rss = models.URLField(blank=False, verify_exists=True, maxlength=100)
        last = models.DateTimeField(auto_now_add=True)

        def save(self):
                f = feedparser.parse(self.rss)
                if not f.version :
                        raise validators.ValidationError("URL is not valid RSS")
                if not self.title:
                        self.title = f.feed.title.encode('utf-8')
                super(Site, self).save()

        def __str__(self):
                return self.rss
        class Admin:
                list_display = ('title', 'rss', 'last')

しかし、これではValidationErrorが適切にキャッチされず、入力が不適切な場合django自体のエラー画面が出力されてしまう。これを防ぐにはモデルのそれぞれのフォームを定義する際にバリデータのリストを引数として渡してやるのがよいようだ。例えばアルファベット小文字のみを入力とする属性hogeは、django/core/validators.pyで定義されたバリデータisLowerCaseを使って

hoge = models.CharField(maxlength=100, validator_list=[isLowerCase])

と書くことができる。

validators.pyにおいて、isLowerCaseの実装は

def isLowerCase(field_data, all_data):
    if field_data.lower() != field_data:
        raise ValidationError, gettext("Uppercase letters are not allowed here.")

となっているので、同様に入力がRSSであるか判定するバリデータisRSSが書ける。

def isRSS(field_data, all_data):
        f = feedparser.parse(field_data)
        if not f.version :
                raise validators.ValidationError("URL is not valid RSS")
rss = models.URLField(blank=False, verify_exists=True, \
                     maxlength=100, validator_list=[isRSS])

raiseをlambda式中に埋め込めればなあ…。

ついでに、RSSのURIはユニークになるはずなのでunique=Trueにしておく。Django1.0でDateTimeFieldのauto_now_addがなくなるようなのでそちらも修正。

from django.db import models
from django.core import validators
import datetime
import feedparser

def isRSS(field_data, all_data):
        f = feedparser.parse(field_data)
        if not f.version :
                raise validators.ValidationError("URL is not valid RSS")

#Create your models here.
class Site(models.Model):
        title = models.CharField(blank=True, maxlength=100)
        rss = models.URLField(blank=False, unique=True, verify_exists=True, \
                maxlength=100, validator_list=[isRSS])
        last = models.DateTimeField(null=True)

        def save(self):
                f = feedparser.parse(self.rss)
                if not self.title:
                        self.title = f.feed.title.encode('utf-8')
                self.last = datetime.datetime.now()
                super(Site, self).save()

いつのまにかFedoraかよ、という感じですが自宅ではDebianです。Debianラブです。

研究室にnVidia GeForce FX5200が転がっていたので入れてみました。

@IT: Fedora Core 6で3D GUI環境Compizを使うには(NVIDIA編)
@IT: Fedora Core 6で3D GUI環境Berylを使うには

IT Pro: 【動画付き】画面で見る最新デスクトップ環境「Beryl」

上記サイト執筆時にはβ版となっていたドライバが既にリリースされているようなので、livnaから直接落とせます。

# rpm -ivh http://rpm.livna.org/livna-release-6.rpm
# yum install xorg-x11-drv-nvidia

GNOME-Look.orgBeryl-Themes.orgで格好いい壁紙を探して……。

image070502_1.jpg

image070502_2.jpg

Shortcuts memo

Ctrl+Alt+[矢印, ドラッグ]
キューブの回転

Ctrl+Alt+PageDown
すべてのワークスペースを帯状に表示(Ctrl+Altを押したまま矢印で切替)

Alt+Tab
次のウィンドウへ(Ctrl押下で全ワークスペース対象)

Alt+Shift+Tab
前のウィンドウへ(Ctrl押下で全ワークスペース対象)

Win+Tab
全ウィンドウを表示、切替(Ctrl押下で全ワークスペース対象)

マウスを右上に
全ウィンドウを表示

Ctrl+Alt+Return
全画面とウィンドウ表示を切替

Alt+F5
ウィンドウ最小化

Alt+F4
ウィンドウを閉じる

Win+ホイール
画面の拡大縮小

Lv3

アルファベットのみからなる文字列中から、大文字ちょうど三つにはさまれた小文字を抜き出す

ex. "aPQRbVWXcYZABd" -> "b"

import Text.ParserCombinators.Parsec 

bodyguards :: Parser String
bodyguards = try ( do { upper; upper; upper ; c <- lower
                ; upper; upper; upper; many1 lower; return [c] })
            <|> do { skipMany1 upper ; skipMany1 lower ; return ""}

bg :: Parser String
bg = do { many lower ; r <- many1 (try bodyguards)
        ; many upper ; eof ; return $ concat r }

readBdGrs :: String -> String
readBdGrs input = case parse bg "" input of
   Left err -> "No match: " ++ show err
   Right val -> show val

main :: IO()
main = do x <- getContents
          putStrLn $ readBdGrs $ filter (/= '\n') x

Haskellに正規表現は似合わない、と思ったものの、いまいちParsecが使いこなせなくて困る。skipManyやconcatのあたりとか、もうちょっと恰好よくできないものか。

解決しました。

JJinuxLand: Python: Django Custom Tags

登録されたRSSから各記事を取得したいとき。RSSのURIを引数として、タイトルや記事の内容のディクショナリを返すカスタムタグ、rss_getを定義した。しかし、テンプレート内で

{% load rss_get %} 
<html>
<head>
<title>Admin's RSS Reader</title>
</head>
<body>
{% for site in object_list %}
   <h1>{{ site.title }}</h1>
   {% rss_get site.rss as entries %}
   {% for entry in entries  %}
      <h2>{{ entry.title }}</h2>
      <p>{{ entry.description }}</p>
   {% endfor %}
{% endfor %}
</body>
</html>

のように書くと、rss_getの引数site.rssがURIに置き換えられず、文字列"site.rss"がそのまま渡されてしまう(rss_getタグ以外の場所、例えばforタグ内でsite.rssを参照した場合には正しくURIに置換される)という問題。

与えられた文字列についてcontextを参照して解決するメソッドtemplate.resolve_variables()を使えばよい。引数としてcontextをとるのでrenderメソッドなどcontextが参照できるところから呼び出してやる。

template.resolve_variables(variable_str, context)

この場合は

def render(self, context):
        f = feedparser.parse(template.resolve_variable(self.rss, context))
        context[self.variable] = f.entries
        return ''

結果rss_get.pyはこうなりました。

from django import template
from rss.site.models import Site
import feedparser

register = template.Library()

def do_rss_get(parser, token):
        bits = token.contents.split()
        if len(bits) != 4:
                raise template.TemplateSyntaxError, "'%s' requires 3 arguments" % bits[0
]
        if bits[2] != "as":
                raise template.TemplateSyntaxError, "'%s' 's 2nd argument must be 'as'"
% bits[0]
        return RSSNode(bits[3], bits[1])

class RSSNode(template.Node):
        def __init__(self, variable, rss):
                self.variable = variable
                self.rss = rss
        def render(self, context):
                f = feedparser.parse(template.resolve_variable(self.rss, context))
                context[self.variable] = f.entries
                return ''

register.tag('rss_get',do_rss_get)

そしてこんな感じに。

python070501.jpg