2006-12-08

_ PythonでSICP

何だか気分的に煮詰まってしまったので、 気分転換に SICP の問題でも解いてみる。 C++で解く という強者もいらっしゃるようだが、 私は屁垂れなので、 Python でやってみる。 疲れた時にわざわざ蛇の道を歩むこともなかろう... SICP in other languages にPythonの例もあるみたいだが、 それは見なかったことにする。

Exercise 1.3. 三つ引数を取って、大きい方の二つの数字の自乗の和を求める関数を書け。 これぐらい簡単でないと頭は休まらないよな。

def f(a, b, c):
  l = [a, b, c]
  l.remove(min(l))
  return sum([x * x for x in l])

思わずbrute-forceでやってしまったが、

def f(a, b, c):
  return sum([x * x for x in (max(a, b), max(min(a, b), c))])

これと比べて、速度差なんて出るんだろうか。

Exercise 1.11. n が3未満なら、f(n) = n で、 3以上なら、f(n) = f(n - 1) + 2f(n - 2) + 3f(n - 3) となる関数を、再帰と繰り返しの両方で書け。

再帰だとそのまんまだ。Pythonは簡単だ。

def f(n):
  if n < 3: return n
  return f(n-1) + 2*f(n-2) + 3*f(n-3)

繰り返し。無意味に generator を使ってみる。

def f(n):
  def g():
    a = []
    while 1:
      l = len(a)
      if l < 3:
        a.insert(0, l)
      else:
        a.insert(0, a[0]+2*a[1]+3*a[2])
        a.pop()
      yield a[0]
  i = g()
  for c in xrange(n - 1):
    i.next()
  return i.next()

全然分かりやすくもならないし、速くもならないな。 ベタで書いた方がマシ。

def f(n):
  if n < 3: return n
  a = [2, 1, 0]
  for i in xrange(n-2):
    a.insert(0, a[0]+2*a[1]+3*a[2])
    a.pop()
  return a[0]

Exercise 1.16. 自乗しまくって、対数ステップ数で指数を計算する関数を繰り返しで書け。

def f(b, n):
  a = 1
  while n:
    if n % 2:
      a *= b
      n -= 1
    else:
      b *= b
      n /= 2
  return a

ヒントそのまんまで書けてしまって、ちっとも面白くない。 だからと言って、これより効率的な方法も思い付かないのがくやしい。

Exercise 1.32. sum とか product を一般化するのに、 accumulator を書け。 再帰と繰り返しの両方で。

繰り返し。

def accumulate(combiner, null_value, term, a, next, b):
  r = null_value
  while a <= b:
    r = combiner(r, term(a))
    a = next(a)
  return r

再帰。

def accumulate_r(combiner, null_value, term, a, next, b):
  if a > b:
    return null_value
  return combiner(term(a), accumelate_r(combiner, null_value, term, next(a), next, b))

当然再利用できる。

def sum(term, a, next, b):
  return accumulate(lambda x, y: x + y, 0, lambda x: x, a, next, b)

def product(term, a, next, b):
  return accumulate(lambda x, y: x * y, 1, lambda x: x, a, next, b)

なんか本当にそのまんまだよな。

Exercise 1.37. k-term finite continued fractionを書け。 再帰と繰り返しの両方で。 これって日本語で何て言うんだっけ? k次有限連分数?

繰り返し。ケツから行くだけ。

def cont_frac(n, d, k):
  r = 0.0
  for i in xrange(k, 0, -1):
    r = n(i) / (d(i) + r)
  return r

再帰。 うまい手が思い付かなかったので、 中に関数作って、変数を余分に足してしまった。 もうちょっとマシな方法はある?

def cont_frac_r(n, d, k):
  def f(i):
    if k == i:
      return n(k) / d(k)
    else:
      return n(k) / (d(k) + f(i + 1))
  return f(0)

この辺で飽きてしまった。 はっきり言って、Pythonでやると簡単過ぎて、 何のネタにもならないようである。

SICPを見るのって久しぶりだったけれど、 案外これに載っているネタって、 Lispじゃないと意味を為さない部分が多いんだなあ。

[]

トップ «前の日記(2006-11-30) 最新 次の日記(2007-01-01)»