用Python递归解决阿拉伯数字转为中文财务数字格式的问题(2)--打开思路的一种方法
几天前自己写了个将阿拉伯数字转为中文财务数字的程序.用的递归,不幸的是它是树形递归.
虽然实际过程中不太可能出现金额数字大到让Python递归栈溢出,但是始终是一块心病,这玩意终究在理论上是受限制的.
我持续地零散地思考过这个问题,今天终于将其一举拿下,并且还是两个版本,一个是函数式(尾递归),一个是命令式.总算是解决一个心病了.
关键在于哪?原来的思路是从左到右转换数字,这种思路用树形递归表示并不难,但是你尝试转化为尾递归时会让你欲仙欲死..反正我是没有弄出来,还浪费了很多时间.
不知怎么的,我突然想到尝试从右到左转换,一下子就豁然开朗了.
我首先写出了个命令式版本,随后轻松翻译为尾递归版本..
这让我想起以前下象棋时,用车纵向将军后,突然发现,如果横向将军直接就赢了啊!这算是一种打开思路的方法了,即尝试从另外一个原始的位置或者方向思考如何解决问题...
是这样吗?是的,字符串从左到右和从右到左够原始吧.
unitDic=dict(zip(range(8),u'拾佰仟万拾佰仟亿')) numDic=dict(zip('0123456789',u'零壹贰叁肆伍陆柒捌玖')) wapDic=[(u'零拾',u'零'),(u'零佰',u'零'),(u'零仟',u'零'), (u'零万',u'万'),(u'零亿',u'亿'),(u'亿万',u'亿'), (u'零零',u'零'),] #函数式 def ChnNumber(s): def wrapper(s,wd=wapDic): def rep(s,k,v): if k in s: return rep(s.replace(k,v),k,v) return s if not wd: return s return wrapper(rep(s,*wd[0]),wd[1:]) def recur(s,acc='',ind=0): if s=='': return acc return recur(s[:-1],numDic[s[-1]]+unitDic[ind%8]+acc,ind+1) def end(s): if s[-1]!='0': return numDic[s[-1]] return '' def result(start,end): if end=='' and start[-1]==u'零': return start[:-1] return start+end return result(wrapper(recur(s[:-1])),end(s)) #命令式 def xChnNumber(s): def wrapper(s): for k,v in wapDic: while k in s: s=s.replace(k,v) return s def cmd(s): start='' for i,char in enumerate(s[::-1]): start=numDic[char]+unitDic[i%8]+start return start def end(s): if s[-1]!='0': return numDic[s[-1]] return '' def result(start,end): if end=='' and start[-1]==u'零': return start[:-1] return start+end return result(wrapper(cmd(s[:-1])),end(s))
测试代码:
for i in range(18): v1='9'+'0'*(i+1) v2='9'+'0'*i+'9' v3='1'*(i+2) print ('%s->%s\n%s->%s\n%s->%s'% (v1,ChnNumber(v1),v2,ChnNumber(v2),v3,ChnNumber(v3))) print ('%s->%s\n%s->%s\n%s->%s'% (v1,xChnNumber(v1),v2,xChnNumber(v2),v3,xChnNumber(v3)))
当然Python的尾递归需要特别手段才不会在超大数字的时候出错(比如10000位数字),针对函数版,有以下办法:
class TailCaller(object) : def __init__(self, f) : self.f = f def __call__(self, *args, **kwargs) : ret = self.f(*args, **kwargs) while type(ret) is TailCall : ret = ret.handle() return ret class TailCall(object) : def __init__(self, call, *args, **kwargs) : self.call = call self.args = args self.kwargs = kwargs def handle(self) : if type(self.call) is TailCaller : return self.call.f(*self.args, **self.kwargs) else : return self.f(*self.args, **self.kwargs) def ChnNumber(s): def wrapper(s,wd=wapDic): @TailCaller def rep(s,k,v): if k in s: return TailCall(rep,s.replace(k,v),k,v) return s if not wd: return s return wrapper(rep(s,*wd[0]),wd[1:]) @TailCaller def recur(s,acc='',ind=0): if s=='': return acc return TailCall(recur,s[:-1],numDic[s[-1]]+unitDic[ind%8]+acc,ind+1) def end(s): if s[-1]!='0': return numDic[s[-1]] return '' def result(start,end): if end=='' and start[-1]==u'零': return start[:-1] return start+end return result(wrapper(recur(s[:-1])),end(s))