[[0]*m]*nの注意点、その2

  • ドキュメントを読む

前回の括弧の件ですが、pythonのドキュメントを読んだところ、次のような記述がありました。
4. 組み込み型 — Python 3.3.3 ドキュメント

ノート:

0 より小さい n の値は 0 として扱われます (s と同じ型の空シーケンスを与えます)。また、コピーは浅いコピーです; ネストされた構造はコピーされません。これは新人 Python プログラマーによく出没します; 次のコードを考えてください:

>>>
>>> lists = [[]] * 3
>>> lists
[[], [], []]
>>> lists[0].append(3)
>>> lists
[[3], [3], [3]]

ここで、[] が空リストを含む 1 要素のリストなので、[] * 3 の 3 要素はこの一つの空リスト (へのポインタ) です。lists のいずれかの要素を変更すると、その一つのリストが変更されます。別々のリストのリストを作るにはこうします:

>>>
>>> lists = [[] for i in range(3)]
>>> lists[0].append(3)
>>> lists[1].append(5)
>>> lists[2].append(7)
>>> lists
[[3], [5], [7]]

なんか、新人()がやってしまう「あるある」な例として挙げられてました。うへぇ
それはともかく、このリストのリスト構造は、よく使う方法なのか、単に説明のために用意されたものなのか。。

[[0]*m]*nの注意点

 最近、C言語で書いていたMASのコードをpythonに書き直してみようかといろいろ画策しております。ですが、C言語でできたことがpythonでもできるわけではなかったりするので注意が必要です。私の場合、得てして、ベクトルや行列を扱いたいわけで、その代替品がリストや配列です。

  • リストと配列は違う

pythonには配列が存在しないので、リストを配列のように使うことが多いと思います。numpyのarrayを使う人はそちらのほうがよいと思いますが、僕はあいにく使っていないので、現時点では生のpythonでやることにします。
さて、次のような0を含む配列の配列に、一定数の個数1を代入したいとすれば、僕はC言語で次のように記述します。

#include<stdio.h>
#define N  10
#define Ns  3
int main(){
  int i,j,k=0;
  int a[N][N];
  for(i=0;i<N;i++) for(j=0;j<N;j++) a[i][j]=0;
  while(k<Ns){
    i=rand()%N;
    j=rand()%N;
    if(a[i][j] == 0) {
      k++;
      a[i][j] = 1;
    }
  }
  for(i=0;i<N;i++) {for(j=0;j<N;j++) printf("%d ",a[i][j]);printf("\n");}
}

そうすると、次のように行列を表示してくれます。

$ gcc -o array array.c
$ ./array.exe
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 1 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
1 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 1 0 0 0 0 0 

pythonで同じことをしようとしたところ、初期値0の入力で、気を抜くと変なことが起こる場合があるので注意が必要です。

>>> a = [[0]*3]*3#ここがだめ
>>> a
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> a[0][0]
0
>>> a[0][0]=1
>>> a
[[1, 0, 0], [1, 0, 0], [1, 0, 0]]

 [[0]*m]*nって別段普通に使えると思ってたのですが。いざ代入しようと思った時に僕としては、一か所だけ変更したくても、つられて一列変わっちゃうわけで。これは、使いにくい。折角短くかけるのに、どうしてこうなるの~。
ちゃんとやろうと思ったら、

>>> a = [[0 for i in range(3)] for j in range(3)]
>>> a
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> a[0][0] = 1
>>> a
[[1, 0, 0], [0, 0, 0], [0, 0, 0]]

これならちゃんと、一か所だけ変わってくれます。なんで揃ってないのだろうか。うーん、"zen of python"なんでしょうきっと。
まぁ、ともあれ、ちゃんと意図した動きをしてくれるコードは以下のようになりました。

import random

Lx, Ly = 3, 3
a  = [[0 for i in range(Lx)] for j in range(Ly)]

k  = 0
Ns = 2
while k < Ns:
    i, j = random.randrange(Lx), random.randrange(Ly) 
    if a[i][j] == 0:
        k += 1
        a[i][j] = 1
[print(i) for i in a]

インデックスkを必要としない書き方ができるならそうしたいものです。
おまけにクラスを使っても書いてみよう。

class Matrix:
    def __init__(self, raw, col):
        self.Lx = raw
        self.Ly = col
        self.m = [[0 for i in range(raw)] for j in range(col)]

    def show(self):
        [print(i) for i in self.m]

    def puts(self, sup):
        import random
        k = 0
        while k < sup:
            i, j = random.randrange(self.Lx), random.randrange(self.Ly)
            if self.m[i][j] == 0:
                k += 1
                self.m[i][j] = 1

if __name__ == "__main__":
    a = Matrix(3,3)
    a.puts(3)
    a.show()

phpデビュー

  • 「○○君、研究は後回しかい?(暗黒微笑)」

研究とは関係のない勉強第二弾としてphpをちょいと触ります。
pythonフレームワークも触ってないのにphpばっかやっててもアレなんで、
まぁ、ちょっとおつまみ程度に。。
さて、こういう時、計算機科学専攻の友人(X君)がいると僕みたいな門外漢でもphpを弄れるようになります。
それは、、

僕「ねぇねぇ、X君、phpできる道具だしてよ~」
X君「しょうがないな~」(ピコピコン)
X君「Ubuntuサ~バ~」
僕「それどうやって使うの?」
X君「それはね~」

ざっとこんなもんよ。
phpのやり方知りたかった人はごめんなさい。他を当たってください。ググれば出てくるはず。
僕もよくわかってないけどsshでログインしてvimphp書いたら動くようにしてもらいました。
環境構築は今度やります。

就活終了

  • 唐突に終わった就活

ぬるっと内定貰えました。Webとかスマフォアプリ系になるのかな…?
SIをことごとく蹴られましたが、Web系は選考がサクサク進みました。
MySQLphpはできません!」と断言して、零細企業に落ちたりもしましたが、
そんな僕の面倒を見てくれる企業がいてくれてました。有りがたいものです。
1年後は研究生活ではなく、開発をやっていると想像すると。。。ちょっとできない。
なーんか、単に開発を淡々とする生活とは程遠そうな気も。。

paizaハッカソンの問題(ペアプロ)

土曜日の夜、こんなものを見つけました。

女子大生とペアプロするだけの簡単なお仕事です!|paizaオンラインハッカソンVol.2

彼女はスマフォアプリでも作ってるのでしょうか。それならば、CやJavaが良いのでしょうが、僕はpythonでやってみました。

if __name__ == "__main__":
    H, W = map(int, raw_input().split())
    mat = [map(int, raw_input()) for i in xrange(H)]
    
    for i in xrange(input()):
        S, T = map(int, raw_input().split())
        pat = [[0]*S]*T

        HS = H + 1 - S
        WT = W + 1 - T

        mat1 = [mat[i:i+S] for i in xrange(HS)]

        tmp1 = []
        for mat2 in mat1:
            tmp2 = [list(mat3) for mat3 in zip(*mat2)]
            tmp1  += [tmp2[i:i+T] for i in xrange(WT)]

        N = sum(1 if pat == i else 0 for i in tmp1)

        print N

*1

キャンペーン終了してるので速度は測ってませんが。まーいいや。

この問題を解いていて面白かったのは、パターン認識の基本となるような問題であると思えた点です。(いやいや、こんなに簡単なものはコードパズルであって、パターン認識ではないという理解が普通か。。。)簡単なお題でありながら、結構骨が折れました。
パターン認識 - Wikipedia
動的計画法など別の解法はこちらへ(↓)
http://paiza.jp/poh/code_index

*1:python2で記述されています

python文法の謎

  • pythonにはポインタが存在する?

突然ですが、pythonってポインタが存在するんでしょうか?僕はポインタってC言語でしか見たことがなかったのでpythonでポインタもどきに出会ってしまい、困惑しています。

>>> x, *y = [1, 2, 3]
>>> x
1
>>> y
[2, 3]

これってyがリスト(0~2番)の1番目の場所を指してるポインタってこと?ふーむ*1
他にもこんなことができるのです。

>>> x, *y, z = [1, 2, 3, 4, 5]
>>> x
1
>>> y
[2, 3, 4]
>>> z
5

ほうほう、なるほど。つまりは*がついてる部分の長さは任意なのね。
で、ここからいろんなことを試していく。

>>> *a,*b = [1, 2, 3]
SyntaxError: two starred expressions in assignment
>>> *a,*b = 1, 2, 3
SyntaxError: two starred expressions in assignment

*がついてる部分の長さは任意だから、aとbのポインタがどこからどこまでなのかわかっていないのかな。2つあるともう1つのポインタは迷子になってるのかな?まぁ、なんにせよ無理です。
Learning Python(by Mark Lutz)をひも解いてみると次のように書いてありました。

>>> seq
[1, 2, 3, 4]
Finally, errors can still be triggered if there is more than one starred name, if there are too few values and no star (as before), and if the starred name is not itself coded inside a sequence:
>>> a, *b, c, *d = seq
SyntaxError: two starred expressions in assignment

>>> a, b = seq
ValueError: too many values to unpack

ははぁ・・・、2つ以上のスターは使うなと。で他にも目についた変なこととして

>>> *a,=range(10)
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> *a = [1,2,3,4]
SyntaxError: starred assignment target must be in a list or tuple
>>> a,=[1]
>>> a
1
>>> a,=[1,2,3]
Traceback (most recent call last):
  File "<pyshell#142>", line 1, in <module>
    a,=[1,2,3]
ValueError: too many values to unpack (expected 1)

>>> x,*y,z='abc'
>>> x
'a'
>>> y
['b']
>>> z
'c'
>>> *y
SyntaxError: can use starred expression only as assignment target
>>> print(*y)
b

カンマにいかほどの意味があるのかは現時点では不明。どうなってるのかなぁ
あと、なんでprintで表示できるのに単体ではダメなんだ?お前strオブジェクトなんだろ、前出て来いよ。
というかzは'c'だからstrオブジェクトだろうけど*yのオブジェクトって何だろう。

>>> type(z)
<class 'str'>
>>> type(y)
<class 'list'>
>>> type(*y)
<class 'str'>

あれ、strだた。ならなぜ出てきてくれないのか。ただ注意しなくてはいけないのは、次のような場合にはエラーになるということ。

>>> *a,=1,2
>>> a
[1, 2]
>>> type(*a)
Traceback (most recent call last):
  File "<pyshell#170>", line 1, in <module>
    type(*a)
TypeError: type() takes 1 or 3 arguments
>>> a=[1]
>>> type(*a)
<class 'int'>

このようにリストの要素が複数あれば、タイプを聞いてもエラー。単体ならばいける。

最後にもう一つ

>>> a=b=[1]
>>> b.append(3)
>>> a,b
([1, 3], [1, 3])
>>> a=b=[1]
>>> b+=[3]
>>> a,b
([1, 3], [1, 3])
>>> a=b=[1]
>>> b=b+[3]
>>> a,b
([1], [1, 3])

ここで、コメントしておきたいのは、なぜ最後の場合のみaに追加の命令が反映されていないのかということです。
奥が深いのか、それとも粗さの表れなのか、まー専門家ではないので良し悪しはわかりませんね。

*1:python3.3.2での実行結果です。python2.7.6では、エラーになりました。

【絶賛】就職活動中

なんとなく就活

 12月から4月まで就活がめんど…研究中心の生活を送っていました。
 4月にマターリSI系の企業廻ってたんですが、なーんか違うなと思い業界を変えてみることに。なんか、今、タブレットとかクラウドとか変革期らしく、企業の上の方の人たち&人事の人たちは、新しいものをどんどん取り入れていきたいって気持ちがあるらしい。どこのIT企業からもそういった上昇志向を感じた。でも、SI系の某会社なんかは社員面談で話した内容が「上がなんか言ってるけど、そういう革新についてくのめんどくせーわ」っと言った回答だった。無駄にワクワクしていた自分としては内心つまんなさそうな会社だなぁと思ってしまった。そのあとも数社廻ってみたが、どこの会社も昨今のSEイメージダウンを受けてか、ホワイトさアピールが半端なく、学生もそのあたりをよりどころとしている感じだった。
 5月現在は趣向を変えてweb・オープン系で就職活動してみることに。僕自身、情報系の人間ではないのだが、物理系の数値計算や社会科学系の授業でシミュレーションをするのでプログラムを書くわけです。ただ、職業準備としての勉強ではなくって、あくまで研究なのでやることが全然違う。そもそも周りの連中はコードなんて書かない。コード書かない人がSE,SIerになっていくのを見ると、どうやら自分はもっと違う仕事をやれるんじゃないか、そう思うようになった。

  • 業界研究ってなにすればいいんだろ

 業界研究ってやる必要あると思うんですけど、自分はあんまり積極的に今までやってませんでした。どうしても学校での研究内容の方が高尚な内容のような気がして馬鹿にしがち。でも、web系はフランクな業界紹介+作ってあそぼ的なノリの本があるようだ。ふむふむ。もっと言語や開発の工程について知っておいたほうがよいようだ。

Webサービスのつくり方 ~「新しい」を生み出すための33のエッセイ (Software Design plus)

Webサービスのつくり方 ~「新しい」を生み出すための33のエッセイ (Software Design plus)

Webアプリエンジニア養成読本[しくみ、開発、環境構築・運用…全体像を最新知識で最初から! ] (Software Design plus)

Webアプリエンジニア養成読本[しくみ、開発、環境構築・運用…全体像を最新知識で最初から! ] (Software Design plus)

 ただ、やっぱりなってみないことには実感わかない気がするなぁ。