はてブロ@ama_ch

https://twitter.com/ama_ch

pyspa challenge 第1回やってみた

トラバ送っていきなり答えのリンク張ってしまうのはどうかな?
と思ったので、リンクは張らないでおきます><
と思っていたけど、解答編からトラバがきたのでリンク張ります!

問題

pyspa challenge 第1回 - 西尾泰和のはてなダイアリー
http://d.hatena.ne.jp/nishiohirokazu/20081129/1227952375

*****の部分に適当なコードを入れてpyspaと表示されるようにせよ

第1問
print "".join(chr(ord(x) + 1) for x in *****)

chr(ord(x) + 1)が"pyspa"になるxを探します!
アルファベット1文字戻ればいいから、o,x,r,o,・・・"a"の前ってなんだっけ?


調べてみよう。

>>> ord("a")
97
>>> chr(96)
'`'

なるほど、"`"か!


というわけで、答えは

>>> print "".join(chr(ord(x) + 1) for x in "oxro`")
pyspa
第2問
import os
print "".join(os.__doc__[x] for x in[os.__doc__.*****(x) for x in "pyspa"])

os.__doc__ってなんだっけ・・・見てみよう。

>>> type(os.__doc__)
<type 'str'>
>>> print os.__doc__
OS routines for Mac, NT, or Posix depending on what system we're on.

This exports:
  - all functions from posix, nt, os2, mac, or ce, e.g. unlink, stat, etc.
  - os.path is one of the modules posixpath, ntpath, or macpath
  - os.name is 'posix', 'nt', 'os2', 'mac', 'ce' or 'riscos'
  - os.curdir is a string representing the current directory ('.' or ':')
  - os.pardir is a string representing the parent directory ('..' or '::')
  - os.sep is the (or a most common) pathname separator ('/' or ':' or '\\')
  - os.extsep is the extension separator ('.' or '/')
  - os.altsep is the alternate pathname separator (None or '/')
  - os.pathsep is the component separator used in $PATH etc
  - os.linesep is the line separator in text files ('\r' or '\n' or '\r\n')
  - os.defpath is the default search path for executables
  - os.devnull is the file path of the null device ('/dev/null', etc.)

Programs that import and use 'os' stand a better chance of being
portable between different platforms.  Of course, they must then
only use functions that are defined by all platforms (e.g., unlink
and opendir), and leave all pathname manipulation to os.path
(e.g., split and join).

ふむふむ、os.__doc__は文字列で、osモジュールの解説が入ってるのか。
文字列だと分かれば簡単だ!


問題のおさらい。

import os
print "".join(os.__doc__[x] for x in[os.__doc__.*****(x) for x in "pyspa"])

文字列os.__doc__の中から、"pyspa"の各文字のインデックスを取り出して連結する感じかな?


ん、インデックスを取り出す?

>>> import os
>>> "".join(os.__doc__[x] for x in[os.__doc__.index(x) for x in "pyspa"])
'pyspa'

できたー!

第3問
import os
os.system("python -m ***** | python -c 'import sys;(lambda x=sys.stdin.read():[sys.stdout.write(x[i]) for i in [67, 12, 31, 67, 36]])()'")

うおー全然わからない!!


先に言っておきますが、ものすごく無理矢理解きました...orz
とりあえず、ひとつずつ解読します。


-mという起動オプションは、

-m module module を sys.path 中から探し、スクリプトとして実行する

Python 2.4 クイックリファレンス

-cという起動オプションは、

-c command 与えられたコマンドを実行する (以降のセクションを参照)。 このオプションの後には、別のオプションを記述できない (コマンド自体の引数として扱われる)

Python 2.4 クイックリファレンス

なるほどなるほど。


問題のlambda式のところを抜き出してみます。

lambda x=sys.stdin.read():[sys.stdout.write(x[i]) for i in [67, 12, 31, 67, 36]])()

sys.stdin.read()で受け取った内容の、インデックス67,12,31,67,36*1を書き出すんだな。
受け取る内容が、

python -m ****

というわけか!
コマンド"python -m hoge"の出力が、インデックス67が"p",12が"y"となるhogeを探せば答えになりそうです。
-mオプションなんて使ったことないから、どうやって探せばいいんだろう><


・・・よし、総当たりで探そう!

#!usr/bin/env python
# -*- coding: utf-8 -*-
""" test.py
テストスクリプト
"""
import commands
import os
import sys

modulepath = "/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5"
modules = commands.getoutput("ls *.py %s" % modulepath).split("\n")
modules = modules[modules.index(modulepath+":")+1:]

# 拡張子を取り除く
modulenames = []
for i in modules:
    try:
        modulenames.append(i[:i.index(".")])
    except:
        None
modulenames = set(modulenames)    # ダブりをまとめる

# 動かなくなるモジュール名
excepts = ["code", "pprint", "base64", "smtplib", "imaplib", "shlex", "quopri", "SimpleHTTPServer", "BaseHTTPServer", "UserString", "formatter", "uu", "pstats", "dis", "fileinput", "tokenize", "SimpleXMLRPCServer"]
# 動かないやつを削除
for i in excepts:
    modulenames.remove(i)

# 答え探し
for i,j in enumerate(modulenames):
    print "%d/%d executing" % (i,len(modulenames)) , j
    os.system('echo "%s" | pbcopy' % j)    # Mac用。オプションに与えるモジュール名をクリップボードへコピー(除外リストにすぐ加えられるように)
    output = commands.getoutput("python -m %s" % j)
    try:
        if output[67] == "p" and output[12] == "y":
            print j, "found!!!!!"
            break
    except:
        None

このスクリプトを実行すると、

$ python test.py
0/176 executing filecmp
1/176 executing heapq
2/176 executing LICENSE
・・・ 中略 ・・・
117/176 executing keyword
118/176 executing this
this found!!!!!

こんな感じで見つけてきます。
答えを見つけるまでは、たびたび入力待ち?みたいになって動かなくなるので、そういうのは適宜exceptsリストの中に放り込んでいきます。このへんを自動でできないのが僕の限界><


thisというモジュールが見つかったので、これを入れて実行してみます。

>>> os.system("python -m this | python -c 'import sys;(lambda x=sys.stdin.read():[sys.stdout.write(x[i]) for i in [67, 12, 31, 67, 36]])()'")
pyspa0

おぉ??
できたっぽいけど0ってなんだろう??


あ、

対話的環境でやっているときはos.systemが0を返すのでpyspa0って表示されちゃう。

だそうです。これで合ってるみたいですね。

12/3追記:答え

pyspa challenge1 解答編 - 西尾泰和のはてなダイアリー
thisってイースターエッグだったんだ、またひとつ勉強になりました!


pyspa challenge01 に挑戦してみた - 牌語備忘録
おぉ、問2間違ってた\(^o^)/ 4つってそういう意味だったのか・・・4つある同じ問題の1番短いのが問2の問題なのかと思(ry

*1:p,y,s,p,a