トップ 最新 追記

じじぃの日記、ツッコミ可

Twitter: @jijixi_org
Xbox Live: jijixi

初心者が書いた OCaml 入門
Spotlight tips サイト内リンク集
1970|01|02|
2003|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|11|
2011|05|
2012|01|

2006-12-01 [長年日記]

% [雑談] 朝起きたら、もっさり雪が積もってた

さむい……

あー、それにしてももう 12 月か……やべぇ、マジやべぇな、おい……


2006-12-04 [長年日記]

% [clip] この広告は無理があるだろ? (アルファルファモザイク)

何も無理に一つにまとめなくてもな(笑


2006-12-06 [長年日記]

% [PC][雑談] 今時は、100BASE-TX クラスのスイッチングハブなんてすんごく安いのね

5,000 円くらいするもんだと思ってのに、ふと電気屋覗いてみたら 1,500 円とかで買えるのね。びっくり。

てことで一個買っといた。 この値段なら一年で壊れても腹立たないなあ(苦笑


2006-12-14 [長年日記]

% [objc][clip] ダイナミックObjective-C 第59回 デザインパターンをObjective-Cで - Prototype (3) (MYCOMジャーナル)

前から何者なのか疑問に思っていた NSZone についての説明。 なるほど、こういうものだったのか。

有効に使いこなすには、それなりにハードウェアの知識とか必要っぽいけど、そこまで行かなくても長い間保持するインスタンスは特定のゾーンに入れとく、くらいでも結構意味ありそうな気もする。 実際どうだかは知らんけど。

NSAutoreleasePool とか、これとか、メモリ管理に対する気合の入り方がすごいなあ。 メモリ管理は楽をしたい。でも必要ならチューニングだってしたい。 もちろんチューニングも楽にしたい。 だからそこら辺は全部フレームワークに突っ込んでしまえ。そんな感じ?

% [雑談] なにこの違和感の無さw

エウレカのオープニング映像にコードギアスのオープニング曲の組合せ→YouTube


2006-12-18 [長年日記]

% [OCaml] あなたならどうお書きになります1.0 (鍋あり谷あり)

なんとなく書いてみたんで貼っておくけど、ダメだこりゃ。 基本的なプランは、

  1. 先にリストをシャッフル
  2. あとは順番に組み合わせるだけ
  3. 先のことは考えてないので組み合わせに失敗することもあるけど、知らないふりしてやり直しする富豪的アプローチ

そもそもお題どおり『等確率』なのかもわかんない。 まあ、それはテスト書けば良いとしても、正しい組み合わせが存在しない入力を判定できなくて無限ループするとか、いろいろアレ。 止まらないときは Ctrl-C してくださいっていう傲慢プログラム。

素直に考えれば、先に組み合わせを探索してそれからランダムに選ぶのが筋な気はするけど、なんとなくそれだと負けなような気がしたんだよぅ。 結局はむしろもっと負けたような気がするけど(の

そんなこんなで、とりあえず悪い例ってことで(苦笑

ちなみに shuffle 関数は Map モジュールが常に整列済みで fold のときに key の順に出てくるのを利用したインチキ仕様。 それにしても、最終的な結果はソートくらいしとけば良かったかな。

% cat christmas_gift.ml
module M = Map.Make (
struct
   type t = int
   let compare = Pervasives.compare
end)

let () = Random.self_init ()

let list_of_map m =
   M.fold (fun _ item seed -> item :: seed) m []

let shuffle lst =
   let rec loop l ret =
      match l with
      | [] -> ret
      | x::xs ->
           let rec make_id () =
              let tmp = Random.bits () in
              if M.mem tmp ret then make_id () else tmp
           in
           loop xs (M.add (make_id ()) x ret)
   in
   list_of_map (loop lst M.empty)

let flatten llst =
   let rec loop num l ret =
      match l with
      | [] -> ret
      | x::xs ->
           let rec loop' l' ret' =
              match l' with
              | [] -> ret'
              | y::ys ->
                   loop' ys ((num, y) :: ret')
           in
           loop (num + 1) xs (loop' x ret)
   in
   loop 0 llst []

let rec pairing_list llst =
   let tmp = shuffle (flatten llst) in
   let fold_f (receivers, result) (id, name) =
      let rec pair reserve recs =
         match recs with
         | [] -> failwith "error"
         | ((rid, rname) as x)::xs ->
              let rec merge l1 l2 =
                 match l1 with
                 | [] -> l2
                 | y::ys -> merge ys (y :: l2)
              in
              if id = rid
              then pair (x :: reserve) xs
              else (merge reserve xs, (name, rname) :: result)
      in
      pair [] receivers
   in
   try
      let _, ret = List.fold_left fold_f (tmp, []) tmp in
      ret
   with Failure _ -> pairing_list llst
% ocaml
# #use "christmas_gift.ml";;
(略)
# pairing_list [ ["a";"b"]; ["c"]; ["d"] ];;
- : (string * string) list = [("b", "c"); ("c", "b"); ("d", "a"); ("a", "d")]
# pairing_list [ ["a";"b"]; ["c"]; ["d"] ];;
- : (string * string) list = [("a", "d"); ("d", "a"); ("b", "c"); ("c", "b")]
# pairing_list [ ["a";"b"]; ["c"]; ["d"] ];;
- : (string * string) list = [("b", "d"); ("d", "b"); ("c", "a"); ("a", "c")]
本日のツッコミ(全1件) [ツッコミを入れる]

% TrackBack [http://jijixi.azito.com/cgi-bin/diary/index.rb?date=200701..]


2006-12-19 [長年日記]

% [clip][Mac][Mach-O] 494bytesのHello, world! (ミルクを掻き混ぜ(ry )

TrackBack が来てたんで紹介しとこう。 この辺りも参考になる。

……まあ、わしはやらんけど。(バイナリアン精神が足りません)


2006-12-20 [長年日記]

% [clip] ssh のハマり方 (裏表(Phinloda のもう裏だか表だか分からないページ))

はーい、経験あります(笑

ポートの変更に限らず、sshd_config を変更したときは現在のセッションを残したまま別のセッションで試さなきゃ普通にだめー。 あとファイヤウォールの設定いじったときとかも、セッション切る前に確認しないと死ねますね。

% [雑談] 新しいリファラスパム?

うちの 12/19 の日記に付いてきてる yadochou.com だの afkey.com だのって何なんだろ? 見に行っても意味無いし、さくっとフィルタした方が良いのかなあ……

スパムなのかリファラ隠しなのか微妙なんだけど。

% [clip][game] 「ぷよぷよ!」に不具合。無償交換へ (ITmedia +D Games)

最近はやりの(?)ありえないバグってやつですか。 255 回ってあんた。

カルドセプトサーガの話とか、これとか見てるとゲーム業界終わったな…って思ってしまうよな。 不具合のレベルがファミコンブームの時の粗製濫造クソソフトと同じだよ、これじゃ。


2006-12-22 [長年日記]

% [clip] The Path of Least Resistance (Radium Software Development)

「うんうん、そうだよなー」と、しきりにうなずいてしまった。

% [clip] ピッチャー振りかぶって投げたら「良いヘディング」 (Sukima Windows Plus)

一瞬、なんでこんなことになるかわからなかったんだけど、たぶん三塁にランナーがいるんだな、これ。

  1. バッターがタイムをかける
  2. ピッチャーが絶妙のタイミングで三塁を見て、タイムを見逃す
  3. キャッチャーはタイムがかかったので息をついて下を向く
  4. ヘディング

という流れ。 奇跡的なタイミングの連鎖が呼んだ珍プレーってやつですな。おもろい。

% [clip] kwskな画像を貼るスレ (ハムスター速報 2ろぐ 〜きなこもちプロファイル〜)

笑ったら死ぬスレだったら 5 秒で死んでる。


2006-12-23 [長年日記]

% [clip] 雄なのに空子 (満目蕭条)

猛烈におもしろいんだけど、なんで最後だけあんなせつないの?


2006-12-24 [長年日記]

% [game] 世界樹の迷宮

ゲーム自体もなかなかおもしろそうなんだけど、それより何より音楽がヤバすぎ。 もうわしのようなおっさんゲーマーの琴線に触れまくりっす。 ファルコム世代、メガドラ世代の人はまず Podcast を聴いて悶えるべき。 ものすごく古代サウンド。

音楽のためだけに買っても良いくらいだな、これは(苦笑


2006-12-25 [長年日記]

% [clip][OCaml] あなたならどうお書きになります1.0 - 私の実装 (鍋あり谷あり)

3 番目の例を読んだが、いくら考えても基本的にわしがこの前書いたのと同じことをやってるようにしか見えない (まあ、いろいろ向きが逆だったりで正解が無い組合せが検出できないから、わしのがダメなのは確かだけど…)。 なのに、鍋谷さんの例ではすんなり答えが出る『a:1 a:2 a:3 a:4 b:5 b:6 b:7 c:8 c:9』くらいの入力で、わしのでは答えが出ない。

これはわしの実装がウンコなのに違いないと、書きっぱなしで放置してあったコードをじっくり眺める。 つーか書いてた当時のことをよーく思い出してみると、pairing_list 関数で使ってる failwith は組合せが存在しないときしか呼ばれないつもりで書いてたんだけど (要するに鍋谷さんのコードで言えば "no solution found." に相当する部分)、なぜか想定に反してやたら呼ばれるので結局めんどくさくなって try..with でごまかすことにしたんだったよ。

とりあえず、わしのコードの一番のミスは組合せをする場面で『送る側と送られる側に同じリストを渡していた』こと。 これだと、あの実装の場合、組合せが偏ってしまう。 具体的には pairing_list に

[[1;2;3;4];[5;6;7];[8;9]]

という入力を与えたとして、tmp の結果が

[(0, 2); (1, 6); (0, 4); (0, 1); (0, 3); (1, 7); (2, 9); (1, 5); (2, 8)]

とかになってたとすると、結果の頭が必ず

(2, 6); (6, 2); ...

となってしまう。 ちょっとやってみればわかるが、tmp がどんな結果であろうと常に同じ人同士で組み合わさるようになっちゃう。 ダメすぎ。 特に全体の人数が奇数の場合は何度繰り返そうが一人余るので結果が出ない。 アホだな、わし。

そんなわけで、とりあえず pairing_list をいじって毎回送られ側をシャッフルするようにすれば、それなりの答えは出るようになった。

let pairing_list llst =
   let fl = flatten llst in
   let rec loop () =
      let fold_f (receivers, result) (id, name) =
         let rec pair reserve recs =
            match recs with
            | [] -> failwith "error"
            | ((rid, rname) as x)::xs ->
                 let rec merge l1 l2 =
                    match l1 with
                    | [] -> l2
                    | y::ys -> merge ys (y :: l2)
                 in
                 if id = rid
                 then pair (x :: reserve) xs
                 else (merge reserve xs, (name, rname) :: result)
         in
         pair [] (shuffle receivers)
      in
      try
         let _, ret = List.fold_left fold_f (shuffle fl, []) (shuffle fl) in
         ret
      with Failure _ -> print_endline "loop"; loop ()
   in
   loop ()

それでもやっぱり try..with が残ってるのは、いくらシャッフルしても同じ人同士でくっ付いてしまうことがあるから。 鍋谷さんのコードでも同じ問題がありそうな気がするんだけど、それらしきケアはしてないように見えるのに、何度試しても失敗することが無いのは何でだろう……わしが何か見落としてるだけなんだとは思うけどわかんない。 うう、とーちゃんバカでごめんよ(泣

本日のツッコミ(全1件) [ツッコミを入れる]

% TrackBack [http://jijixi.azito.com/cgi-bin/diary/index.rb?date=200701..]


2006-12-26 [長年日記]

% [Mac] Dashcode Beta

via HMDT.

これはおもしろい。 Dashboard widget 作成の敷居がすごく下がりそう。 つーか実際、JavaScript に関する知識が少しありさえすれば、ちょっとしたものは作れてしまいそうだが。

ダウンロードするには ADC のアカウントが必要だけど、まあ Mac 使ってる技術者でアカウント持ってない人なんていないでしょう (ほんとか?)。 一般会員なら無料だし。


2006-12-30 [長年日記]

% [Ruby] こんな書き方するやつはいねぇよ (たぶん)

最近 Ruby のコードを書いた記憶が無いので、久しぶりに何か書いてみたくなったんだけど、ネタが無いのでこの前の OCaml のコード (+修正) を直訳してみることにした。 OCaml だと fold 使いまくりな昨今だけど、Ruby で inject 使ったこと無いしちょうど良いお題かと思って。

んで、書いてみたんだけど、これがまあなんともキモいと言うか、最初っから Ruby で書くなら絶対こんな書き方しないよなーっつー感じでなんともアレなんだけど、どっちかと言うとこういうコードの方が先に頭に浮かぶようになってる今日この頃だというのはアレですか、OCaml 脳?

ともあれこんな感じ。 shuffle は鍋谷さんのをパクりました。 OCaml のコードが何やってるかわかんない人も、これならわかるでしょう (ほんとか?)。 タプルが無くてちょっとせつなかったんだけど、しょうがないから配列にしてごまかした。

% cat christmas_gift.rb
class Array
   def car
      self[0]
   end
   def cdr
      self[1..-1]
   end
   def cons(x)
      [x] + self
   end
end

def shuffle(lst)
   lst.sort_by do rand 0 end
end

def flatten(llst)
   loop = Proc.new do |num, l, ret|
      if l.empty?
         ret
      else
         x, xs = l.car, l.cdr
         loop_ = Proc.new do |l_, ret_|
            if l_.empty?
               ret_
            else
               y, ys = l_.car, l_.cdr
               loop_.call(ys, ret_.cons([num, y]))
            end
         end
         loop.call(num + 1, xs, loop_.call(x, ret))
      end
   end
   loop.call(0, llst, [])
end

def pairing_list(llst)
   fl = flatten(llst)
   loop = Proc.new do
      fold_f = Proc.new do |rec_res, id_name|
         receivers, result = rec_res[0..1]
         id, name = id_name[0..1]
         pair = Proc.new do |reserve, recs|
            raise "error" if recs.empty?

            rid, rname = (x = recs.car)
            xs = recs.cdr
            merge = Proc.new do |l1, l2|
               if l1.empty?
                  l2
               else
                  y, ys = l1.car, l1.cdr
                  merge.call(ys, l2.cons(y))
               end
            end
            if id == rid
               pair.call(reserve.cons(x), xs)
            else
               [merge.call(reserve, xs), result.cons([name, rname])]
            end
         end
         pair.call([], shuffle(receivers))
      end
      begin
         ret = shuffle(fl).inject([shuffle(fl), []], &fold_f)
         ret[1]
      rescue
         p "loop"
         loop.call
      end
   end
   loop.call
end
irb(main):001:0> load "christmas_gift.rb"
=> true
irb(main):002:0> pairing_list([[1,2,3,4],[5,6,7],[8,9]])
"loop"
=> [[8, 2], [7, 1], [9, 3], [6, 4], [4, 7], [3, 5], [1, 9], [2, 6], [5, 8]]
irb(main):003:0> pairing_list([[1,2,3,4],[5,6,7],[8,9]])
=> [[6, 2], [9, 4], [3, 7], [5, 1], [7, 8], [2, 5], [8, 3], [1, 9], [4, 6]]
irb(main):004:0> pairing_list([[1,2,3,4],[5,6,7],[8,9]])
"loop"
"loop"
=> [[1, 6], [9, 5], [8, 2], [6, 1], [7, 3], [5, 4], [4, 7], [3, 8], [2, 9]]

% [Ruby] 関数型脳の落とし穴

上記のコードは最初「30 分もあれば書けるだろ」と思って書き始めたんだが、実際は 2 時間近くかかったのである。 ぶっちゃけ関数内関数の挙動に悩まされまくったのが原因で、なんか結構絶望を感じたので書き記しておこう。

flatten 関数は最初こんな風に書いていた。

% cat array_ex.rb
class Array
   def car
      self[0]
   end
   def cdr
      self[1..-1]
   end
   def cons(x)
      [x] + self
   end
end
% cat flatten01.rb
require "array_ex"

def flatten(llst)
   def loop(num, l, ret)
      return ret if l.empty?
      x, xs = l.car, l.cdr
      def loop_(l_, ret_)
         return ret_ if l_.empty?
         y, ys = l_.car, l_.cdr
         loop_(ys, ret_.cons([num, y]))
      end
      loop(num + 1, xs, loop_(x, ret))
   end
   loop(0, llst, [])
end

正味な話、実行してみるまでこれでばっちり動くと思ってたんだけど……

irb(main):001:0> load "flatten01.rb"
=> true
irb(main):002:0> flatten([[1,2,3,4],[5,6,7],[8,9]])
NameError: undefined local variable or method `num' for main:Object
        from ./flatten01.rb:10:in `loop_'
        from ./flatten01.rb:12:in `loop'
        from ./flatten01.rb:14:in `flatten'
        from (irb):2

……絶望した。 関数内関数は親関数のローカル変数を参照できないのか...orz

しかたなく関数内関数じゃなく Proc オブジェクトにすることに。

% cat flatten02.rb
require "array_ex"

def flatten(llst)
   loop = Proc.new do |num, l, ret|
      return ret if l.empty?
      x, xs = l.car, l.cdr
      loop_ = Proc.new do |l_, ret_|
         return ret_ if l_.empty?
         y, ys = l_.car, l_.cdr
         loop_.call(ys, ret_.cons([num, y]))
      end
      loop.call(num + 1, xs, loop_.call(x, ret))
   end
   loop.call(0, llst, [])
end
irb(main):001:0> load "flatten02.rb"
=> true
irb(main):002:0> flatten([[1,2,3,4],[5,6,7],[8,9]])
=> [[0, 4], [0, 3], [0, 2], [0, 1]]

……は? な・ぜ・で・す・か ??

激しく悩んだあげく、ようやく気付いた。 Proc オブジェクト内の return で、Proc どころか flatten 関数まで一気に抜けている...orz

さらに絶望した。 結局、最終的にはこうなった。

% cat flatten03.rb
require "array_ex"

def flatten(llst)
   loop = Proc.new do |num, l, ret|
      if l.empty?
         ret
      else
         x, xs = l.car, l.cdr
         loop_ = Proc.new do |l_, ret_|
            if l_.empty?
               ret_
            else
               y, ys = l_.car, l_.cdr
               loop_.call(ys, ret_.cons([num, y]))
            end
         end
         loop.call(num + 1, xs, loop_.call(x, ret))
      end
   end
   loop.call(0, llst, [])
end
irb(main):001:0> load "flatten03.rb"
=> true
irb(main):002:0> flatten([[1,2,3,4],[5,6,7],[8,9]])
=> [[2, 9], [2, 8], [1, 7], [1, 6], [1, 5], [0, 4], [0, 3], [0, 2], [0, 1]]

今日の教訓。

  • 関数内関数は上位関数のローカル変数が見えないので不便。Proc 使った方が良い。
  • Proc 内で return すると意図しないところまで飛んでいく可能性有り。つか return なんて使わない方が良いのかも知れないよ。

……よく考えると、わりと FAQ だったかも知れない。 いくら情報を見聞きしてても、実際に遭遇してみないと身につかないのだねえ……

% [Python] ちょっと Python が好きになった

Ruby が関数型脳にとってちょっとがっかりだったので、じゃあってんで Python でも書いてみた。 なんかあれだね、Python の方が関数型脳には馴染みやすいかも知れないね。

% cat christmas_gift.py
import copy
import random

def car(lst):
   return lst[0]
def cdr(lst):
   return lst[1:len(lst)]
def cons(x, xs):
   return [x] + xs

def shuffle(lst):
   ret = copy.copy(lst)
   random.shuffle(ret)
   return ret

def flatten(llst):
   def loop(num, l, ret):
      if l == []:
         return ret

      x, xs = car(l), cdr(l)
      def loop_(l_, ret_):
         if l_ == []:
            return ret_

         y, ys = car(l_), cdr(l_)
         return loop_(ys, cons((num, y), ret_))
      return loop(num + 1, xs, loop_(x, ret))
   return loop(0, llst, [])

def pairing_list(llst):
   fl = flatten(llst)
   def loop():
      def fold_f((receivers, result), (id, name)):
         def pair(reserve, recs):
            if recs == []:
               raise Exception('error')

            x, xs = car(recs), cdr(recs)
            rid, rname = x
            def merge(l1, l2):
               if l1 == []:
                  return l2
               y, ys = car(l1), cdr(l1)
               return merge(ys, cons(y, l2))
            if id == rid:
               return pair(cons(x, reserve), xs)
            else:
               return (merge(reserve, xs), cons((name, rname), result))
         return pair([], shuffle(receivers))
      try:
         _, ret = reduce(fold_f, shuffle(fl), (shuffle(fl), []))
         return ret
      except Exception, e:
         if str(e) == "error":
            return loop()
         raise e
   return loop()
% python -i christmas_gift.py
>>> pairing_list([[1,2,3,4],[5,6,7],[8,9]])
[(8, 2), (6, 4), (4, 9), (7, 1), (1, 7), (3, 6), (2, 5), (5, 8), (9, 3)]
>>> pairing_list([[1,2,3,4],[5,6,7],[8,9]])
[(6, 2), (2, 8), (3, 7), (7, 4), (5, 9), (9, 1), (4, 5), (1, 6), (8, 3)]
>>> pairing_list([[1,2,3,4],[5,6,7],[8,9]])
[(3, 9), (2, 7), (8, 6), (1, 5), (7, 3), (5, 4), (6, 2), (9, 1), (4, 8)]

再帰するときもいちいち return が必要なのがウザい気はするけど、関数の扱いは Ruby よりずっと自然な感じがする。 あと、OCaml に慣れきってるとタプルがあるのが嬉しい。 fold_f 関数の仮引数の書き方とかなんてステキすぎる。

ちなみに、横着して例外に Exception を使ったのは失敗だった。 あらゆる例外が捕捉されてしまって、(このコードだと問答無用にループするので) デバッグに難儀したよ。 泥縄プログラミング、いくないね。 まあ、最終的には except 節を泥縄的に直してごまかしたんだが(の

% [Ruby][Python][Haskell][OCaml] 今回のネタで気がついた Ruby と Python の大きな (?) 違い

百聞は一見にしかず。

% ruby -v
ruby 1.8.4 (2005-12-24) [powerpc-darwin]
% irb
irb(main):001:0> a,b = [1,2,3]
=> [1, 2, 3]
irb(main):002:0> a
=> 1
irb(main):003:0> b
=> 2
irb(main):004:0> a,b = [1]
=> [1]
irb(main):005:0> a
=> 1
irb(main):006:0> b
=> nil
% python2.5
Python 2.5 (r25:51908, Nov 24 2006, 10:19:07) 
[GCC 4.0.1 (Apple Computer, Inc. build 5363)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> a,b = [1,2,3]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack
>>> a,b = [1]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: need more than 1 value to unpack
>>> a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
>>> b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'b' is not defined
>>> a,b = [1,2]
>>> a
1
>>> b
2

一長一短だとは思うけど、Python の方が strict な感じがして好みかも。 少なくともリストはともかくタプルはこうじゃないと困る。

なんかこういう関係って見覚えがあるなあ……と思ったら、あれだ、Haskell の zip と OCaml の combine の関係だ。

% hugs
Hugs> zip [1,2,3] [4,5]
[(1,4),(2,5)]
% ocaml
# List.combine [1;2;3] [4;5];;
Exception: Invalid_argument "List.combine".
# List.combine [1;2] [4;5];;
- : (int * int) list = [(1, 4); (2, 5)]

さすがにこれは、OCaml の方が細かすぎる気がするが……

本日のツッコミ(全2件) [ツッコミを入れる]

% 向井 [Rubyはメソッド内でもdef宣言できるので誤解しがちですが、そのdefはメソッド内メソッドの宣言ではなく「メソッド..]

% jijixi [全然知らなかったです...orz って言うか、この仕様って何かありがたいことあるんですかね? これならいっそ def..]


2006-12-31 [長年日記]

% [Python] 大規模開発には Ruby より Python の方が向いてるのかもしれないと思った (なんとなくだけど)

昨日 Python のコード書いたのがわりと気持ち良かったんで、久々に Python いじりモード突入。 遊ぶなら素の Python より IPython だよねーってことでゴー。

% ipython
Python 2.5 (r25:51908, Nov 24 2006, 10:19:07) 
Type "copyright", "credits" or "license" for more information.

IPython 0.7.2 -- An enhanced Interactive Python.
?       -> Introduction to IPython's features.
%magic  -> Information about IPython's 'magic' % functions.
help    -> Python's own help system.
object? -> Details about 'object'. ?object also works, ?? prints more.
In [1]: def f():
   ...:     class MyException:
   ...:         pass
   ...:     try:
   ...:         raise MyException
   ...:     except MyException, e:
   ...:         print e
   ...:     finally:
   ...:         return True
   ...:     
   ...:     

In [2]: f()
<__main__.MyException instance at 0x12272d8>
Out[2]: True

In [3]: raise MyException
---------------------------------------------------------------------------
<type 'exceptions.NameError'>             Traceback (most recent call last)

/Users/jijixi/<ipython console> in <module>()

<type 'exceptions.NameError'>: name 'MyException' is not defined

関数内ローカルなクラスが定義できる。 これなら周りを気にせず大域脱出のために例外を使えるね。 Exception のサブクラスじゃなくても raise できる辺り、いろいろ無茶できそう。

あとローカルなクラスが定義できると、↓みたいなことが簡単。

In [4]: def factory():
   ...:     class Object:
   ...:         def foo(self):
   ...:             print 'foo'
   ...:     return Object() 
   ...: 

In [5]: o = factory()

In [6]: o.foo()
foo

ちなみに IPython のステキすぎるところ。↓

In [7]: cd /
/

In [8]: ls
Applications/   Library/        automount/      exports@        sbin/
Desktop DB      Network/        bin/            mach@           sw@
Desktop DF      System/         cores/          mach.sym        tmp@
Desktop Folder/ Users/          dev/            mach_kernel     usr/
Developer/      Volumes/        etc@            private/        var@

こういうところは irb なんて目じゃないステキさ。

In [23]: cd /etc/
/private/etc

In [24]: ed -x rc
Editing...

こんなとかね。 ed で EDITOR が立ち上がる。 -x オプションは編集終了後に対象ファイルを読み込まないように。 逆に言うと、

In [27]: ed test.py
Editing... done. Executing edited code...

ここで test.py の内容が、

% cat test.py
def f():
   print 'f'
def g():
   print 'g'

だとすると…

In [28]: f()
f

In [29]: g()
g

普通に実行できたり。 とにかく IPython 楽しすぎ。 その気になればログインシェルにだってできると思う。 一時期本気でこれのために Ruby から Python に乗り換えようかと悩んだことがあるし(笑

% [Python] タプルとリスト

Python では Ruby で言うところの配列をリストと呼ぶ。 ハッシュを辞書と呼ぶ。 まあ、そんなこんなで、関数型脳に毒された人間にとっては Python においてリストと呼ばれるものが、リストと聞いて想像するリストと違ったリストであることが微妙にストレスになったりするのである。 リストリストくどい。

ともかく Python のリストはリストって名前のくせに mutable なのがキモい。 構造はともかく immutable なリストは無いのか〜!! と思って調べてみると、実はタプルもシーケンス型の一つであるのが判明。 OCaml 慣れしてるとびっくりだけど、SML な人ならそれなりに納得かもしれない。

In [1]: tup = (1,2,3,4)

In [2]: tup[0]
Out[2]: 1

In [3]: def succ(x):
   ...:     return x + 1
   ...: 

In [4]: map(succ, tup)
Out[4]: [2, 3, 4, 5]

インデックスでのアクセスとか、map とか reduce の適用とか余裕でできる。 map がリストで返してくるのが微妙に不満だが。

ともあれ、タプルもリストとほとんど同じインターフェイスで操作できてしまうのである。 よって、昨日のコードなんかは、ちょろっといじってやるとそのままタプルを利用した仕様に早変わり。

--- christmas_gift.py	2006-12-30 14:53:45.000000000 +0900
+++ christmas_gift2.py	2006-12-30 18:59:24.000000000 +0900
@@ -1,4 +1,3 @@
-import copy
 import random
 
 def car(lst):
@@ -6,40 +5,40 @@
 def cdr(lst):
    return lst[1:len(lst)]
 def cons(x, xs):
-   return [x] + xs
+   return (x,) + xs
 
 def shuffle(lst):
-   ret = copy.copy(lst)
-   random.shuffle(ret)
-   return ret
+   tmp = list(lst)
+   random.shuffle(tmp)
+   return tuple(tmp)
 
 def flatten(llst):
    def loop(num, l, ret):
-      if l == []:
+      if l == ():
          return ret
 
       x, xs = car(l), cdr(l)
       def loop_(l_, ret_):
-         if l_ == []:
+         if l_ == ():
             return ret_
 
          y, ys = car(l_), cdr(l_)
          return loop_(ys, cons((num, y), ret_))
       return loop(num + 1, xs, loop_(x, ret))
-   return loop(0, llst, [])
+   return loop(0, llst, ())
 
 def pairing_list(llst):
    fl = flatten(llst)
    def loop():
       def fold_f((receivers, result), (id, name)):
          def pair(reserve, recs):
-            if recs == []:
+            if recs == ():
                raise Exception('error')
 
             x, xs = car(recs), cdr(recs)
             rid, rname = x
             def merge(l1, l2):
-               if l1 == []:
+               if l1 == ():
                   return l2
                y, ys = car(l1), cdr(l1)
                return merge(ys, cons(y, l2))
@@ -47,9 +46,9 @@
                return pair(cons(x, reserve), xs)
             else:
                return (merge(reserve, xs), cons((name, rname), result))
-         return pair([], shuffle(receivers))
+         return pair((), shuffle(receivers))
       try:
-         _, ret = reduce(fold_f, shuffle(fl), (shuffle(fl), []))
+         _, ret = reduce(fold_f, shuffle(fl), (shuffle(fl), ()))
          return ret
       except Exception, e:
          if str(e) == "error":
% python -i christmas_gift2.py
>>> pairing_list( ((1,2,3,4),(5,6,7),(8,9)) )
((8, 5), (5, 2), (7, 4), (4, 7), (3, 8), (9, 1), (2, 9), (1, 6), (6, 3))

うーん、ステキ (…か?)。

% [Python] だれか正しいデフォルトエンコーディングの設定方法を教えてください

せっかく Unicode 文字列が用意されてるんだから、それをさくっと使いたいんだけど、なぜかうちの Python は sys.getdefaultencoding() が 'ascii' を返すのである (それが普通なのかもしれないけど)。 まあ、スクリプトとして書く場合はファイルの頭に、

# -*- coding: euc-jp -*-

とか書いとけば良い話なんだけど、対話環境でエンコーディングを指定する方法がわからない。 だからって、いちいち

In [1]: s = unicode('ほげ', 'euc-jp')

だの

In [3]: s = 'ほげ'.decode('euc-jp')

だのやるのはあんまりだ。 色々調べて、sys モジュールに setdefaultencoding という関数があるのに気付く。 ……でも、

In [5]: import sys

In [6]: sys.setdefaultencoding('euc-jp')
---------------------------------------------------------------------------
<type 'exceptions.AttributeError'>        Traceback (most recent call last)

/Users/jijixi/<ipython console> in <module>()

<type 'exceptions.AttributeError'>: 'module' object has no attribute 'setdefaultencoding'

いやん...orz

リファレンスをよく読むと site モジュールってのが読み込まれたときに、この関数は削除されてしまうらしい。 しかたなく site.py を読んで、こんな↓ファイルを作ってみた。

% cat /sw/lib/python2.5/site-packages/sitecustomize.py
import sys
encoding = 'euc_jp'
sys.setdefaultencoding(encoding)

ファイルの場所はそれぞれ環境に応じて。 とりあえずこれで、

% python2.5
Python 2.5 (r25:51908, Nov 24 2006, 10:19:07) 
[GCC 4.0.1 (Apple Computer, Inc. build 5363)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> s = u'ほげ'
>>> s
u'\u307b\u3052'

うまくいくようにはなったみたいだけど、これって正しいお作法なんだろうか? あと、通常の対話環境は良いとして、IPython が

In [1]: s = u'ほげ'

In [2]: s
Out[2]: u'\xa4\xdb\xa4\xb2'

とかなっちゃってショボーンなんで (勝手にエスケープされてる?)、こっちもなんとかしないとなあ。 とりあえず、

In [3]: s = unicode('ほげ')

In [4]: s
Out[4]: u'\u307b\u3052'

In [5]: s = 'ほげ'.decode()

In [6]: s
Out[6]: u'\u307b\u3052'

こういう逃げ方はできるから、何もしなかった時よりはマシなんだけど……

% [Python] なんか書きたいけど相変わらずネタが無いので写経

てことで、昨日のネタ繋がりで鍋谷さんとこのアレをパクる。 リストのメソッドが軒並破壊的で、そのせいで (らしい) self じゃなく None を返すのに慣れなくて、イヤっていうほど

TypeError: 'NoneType' object is not iterable

とかってエラーが出まくるのにぐったりしつつ、なんとか書き上げた。 いちいち Ruby とお作法が違うんで戸惑いつつも、それはそれで楽しかったと言えなくもない。 exchinfo() とか result() 辺りに苦労の跡がうかがえるかも(苦笑

……OCaml の begin..end みたいなローカルなブロックを作る構文が欲しいなあ (あるのかもしれないけど知らない)。

% cat christmas_gift_alt.py
from random import random
from copy   import copy
import sys

class ExchInfo:
   def __init__(self, gr, name, to):
      self.gr   = gr
      self.name = name
      self.to   = to
   def dispname(self):
      if self.gr != self.name:
         return self.gr + ':' + self.name
      else:
         return self.name

class Found:
   def __init__(self, exchinfo):
      self.val = sorted(exchinfo, cmp, lambda o: o.dispname())
      def f(i):
         return "%s -> %s" % (i.dispname(), exchinfo[i.to].dispname())
      self.val = ', '.join(map(f, self.val))
   def __str__(self):
      return self.val

def find_exchange(exchinfo, ix, not_given):
   if not_given == []:
      raise Found, exchinfo
   ng = sorted(not_given, cmp, lambda o: random())
   for to in ng:
      if exchinfo[to].gr == exchinfo[ix].gr:
         continue
      exchinfo[ix] = copy(exchinfo[ix])
      exchinfo[ix].to = to
      next_ng = copy(not_given)
      next_ng.remove(to)
      find_exchange(exchinfo, ix + 1, next_ng)

members = sys.argv[1:len(sys.argv)]

def exchinfo():
   def f(m):
      s = m.split(':')
      if len(s) == 1:
         name = s[0]
      else:
         name = s[1]
      return ExchInfo(s[0], name, None)
   return sorted(map(f, members), cmp, lambda o: random())

def result():
   try:
      find_exchange(exchinfo(), 0, range(0, len(members)))
      return "no solution found."
   except Found, obj:
      return obj

print result()
% python2.5 christmas_gift_alt.py a:1 a:2 a:3 a:4 b:5 b:6 b:7 c:8 c:9
a:1 -> c:8, a:2 -> b:7, a:3 -> c:9, a:4 -> b:5, b:5 -> a:1, b:6 -> a:3, b:7 -> a:2, c:8 -> a:4, c:9 -> b:6
% python2.5 christmas_gift_alt.py a:1 a:2 a:3 a:4 b:5 b:6 b:7 c:8 c:9
a:1 -> c:8, a:2 -> c:9, a:3 -> b:6, a:4 -> b:7, b:5 -> a:3, b:6 -> a:2, b:7 -> a:1, c:8 -> b:5, c:9 -> a:4

% [Python] Python だって結構泥縄指向

さっき気付いたが、こんなことできたんだね。

In [1]: class C:
   ...:     def f(self):
   ...:         print 'hoge'
   ...:         
   ...:         

In [2]: o = C()

In [3]: o.f()
hoge

In [4]: def g(self):
   ...:     print 'fuga'
   ...:     
   ...:     

In [5]: C.f = g

In [6]: p = C()

In [7]: p.f()
fuga

In [8]: o.f()
fuga

さすがに特異メソッドのようなのはツラそうだけど。

% [Python] 特異メソッドっぽいもの

あるにはあるけど、なんか微妙。

In [1]: class C:
   ...:     pass
   ...: 

In [2]: o = C()

In [3]: setattr(o, 'foo', 10)

In [4]: o.foo
Out[4]: 10

インスタンス変数追加。 でも……

In [5]: def bar(self):
   ...:     return 'bar'
   ...: 

In [6]: setattr(o, 'bar', bar)

In [7]: o.bar
Out[7]: <function bar at 0x1221fb0>

In [8]: o.bar()
---------------------------------------------------------------------------
<type 'exceptions.TypeError'>             Traceback (most recent call last)

/Users/jijixi/<ipython console> in <module>()

<type 'exceptions.TypeError'>: bar() takes exactly 1 argument (0 given)

あれ〜〜?

In [9]: o.bar(o)
Out[9]: 'bar'

だからってこれはいただけないな(苦笑


トップ 最新 追記

日記ってのは本来、自分で読み返すためにあるもんだよなあ……
もしくは有名人になったら死後に本になったりとかか?

RSS はこちら

jijixi at azito.com