summer_tree_home

Check iOでPython3をマスターするぜっ

Restricted sum (Electronic Station) - sum()を使わずに合計を求めよ

どんな問題?

Restricted sum
http://www.checkio.org/mission/restricted-sum/

リストの合計を求めよ。ただし、コード内で以下の用語は使ってはならない。(禁止ワード)
sum, import, for, while, reduce

sum(data) で済むことを、あえて他のやり方で書かなくてはいけないという変な問題。
ゲームの縛りプレイみたいなものか。(例えばメタルマックスを戦車なしでプレイするとか)

どうやって解く?

最初にあれこれ実験してみた。コード中に禁止ワードが含まれていると、

FAIL
I found 'sum' in you code.
Fail: checkio([1,2,3])

となってしまう。禁止ワードはコメントの中でもダメみたい。

だったら、sum()と書かずにsum()を呼び出せばいいじゃないか。getattr()を使えば、文字列から関数を呼び出すことができたはず。sumはbuiltinsというオブジェクトのメンバだから、

  f = getattr(builtins, 'sum')
  return f(data)

とすればいい。もちろん、これだとsumが含まれるので、

import builtins

def checkio(data):
    f = getattr(builtins, 's' + 'u' + 'm')
    return f(data)

sとuとmは言いましたが、sumとは言っておりません。一休さんのとんちみたいだが、問題ないはず。

FAIL
ImportError: builtins, , 1
Fail: checkio(undefined)

あれ?builtinsはインポートできないのか?いや、まて、builtinsはダメでも、mathならimportできるはず。なら、mathにはfsumがあったよな。

import math

def checkio(data):
    f = getattr(math, 'fs' + 'um')
    return int(f(data))

どやっ。

I found 'import' in you code.
Fail: checkio([1,2,3])

あー、そうか、そもそも import が禁止ワードだった。なんか無駄な時間を費やしてしまった・・・。
(importと書かずにimportすることは、さすがに無理なのかな?)

あきらめて真面目に解いてみる

ようするに、for とか while とかを使わずにループしろっていう問題なのだろう。
そうなると、再帰呼び出しぐらいしか思いつかない。

「dataを1つ削除してtotalに追加する」という関数を作って、dataが無くなるまで再帰呼び出しすればいいか。totalはグローバル変数にしとこう。

total = 0

def add(data):
    global total

    if len(data) == 0:
        return
    total += data.pop()
    add(data)

def checkio(data):
    global total

    total = 0
    add(data)
    return total

うんうん、テストはクリア。
でも、やっぱり global が美しくないので、totalを関数内に入れてみた。

def goukei(data, total=0):
    if len(data) == 0:
        return total
    return goukei(data, total + data.pop())

def checkio(data):
    return goukei(data)

これでOK

他の人の答え

evalを使う

checkio=eval("su"+"m")

http://www.checkio.org/mission/restricted-sum/publications/veky/python-3/using/
eval なんてあったのか。最初のアイデアで間違ってなかっただけに悔しい。

globals()を使う

def checkio(data):
    return globals()['__builtins__']['su'+'m'](data)

http://www.checkio.org/mission/restricted-sum/publications/oduvan/python-27/sorry/
globals()も知らなかった。モジュール一覧をdictで返すらしい。

ちなみに、自分のPC(Python3.3.3 + Windows7)では、このコードでは「TypeError: 'module' object is not subscriptable」というエラーが出た。
globals()['__builtins__'] がリストではなくmoduleオブジェクトになっているようだ。(Pythonのバージョンによる違い?)
まあ、moduleオブジェクトが得られるなら、getattrを使えばいい。

def checkio(data):
    module = globals()['__builtins__']
    f = getattr(module, 'su' 'm')
    return f(data)

ちなみに、'su'+'m'と書かなくても'su' 'm'だけで'sum'として解釈されるらしい。

再帰呼び出し

やはり再帰呼び出しを使っている人が多かったけど、こんな風にシンプルに書いていた。

def checkio(data):
    return data[0] + checkio(data[1:]) if data else 0

足し算を作ってからeval

あとは、これもすごいなと思った。

checkio=lambda d:eval('+'.join(map(str,d)))

[1,2,3] を '1+2+3' という文字列に変えて eval で評価。なるほどねー。


最初はつまらない問題だと思ってたけど、結果的にはすごく勉強になった。