用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))

 

posted @ 2013-11-06 18:51  LisPythoniC  阅读(509)  评论(0编辑  收藏  举报