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'として解釈されるらしい。
足し算を作ってからeval
あとは、これもすごいなと思った。
checkio=lambda d:eval('+'.join(map(str,d)))
[1,2,3] を '1+2+3' という文字列に変えて eval で評価。なるほどねー。
最初はつまらない問題だと思ってたけど、結果的にはすごく勉強になった。