python – 将一个“增长”表的命令式算法转换为纯函数
我的程序是用
Python 3编写的,它有很多地方以一个(非常大的)类似数字的数据结构开始,并按照某种算法为它添加列. (每个地方的算法都不同.)
我试图将其转换为纯函数方法,因为我遇到了命令式方法的问题(难以重用,难以回忆临时步骤,难以实现“懒惰”计算,由于依赖于状态而容易出错等) . Table类实现为字典字典:外部字典包含行,由row_id索引; inner包含一行中的值,由column_title索引.该表的方法非常简单: # return the value at the specified row_id,column_title get_value(self,row_id,column_title) # return the inner dictionary representing row given by row_id get_row(self,row_id) # add a column new_column_title,defined by func # func signature must be: take a row and return a value add_column(self,new_column_title,func) 到目前为止,我只是在原始表中添加了列,每个函数都将整个表作为参数.当我转向纯函数时,我必须使所有参数都是不可变的.因此,初始表变得不可变.任何其他列都将作为独立列创建,并仅传递给需要它们的那些函数.典型的函数将采用初始表和已创建的几列,并返回一个新列. 我遇到的问题是如何实现独立列(列)? 我可以把它们都变成字典,但看起来很贵.实际上,如果我需要在每个逻辑行中的10个字段上执行操作,我将需要进行10次字典查找.最重要的是,每列将包含键和值,使其大小加倍. 我可以使Column成为一个简单的列表,并在其中存储对从row_id到数组索引的映射的引用.好处是这个映射可以在对应于同一个初始表的所有列之间共享,并且一次查找一次,它适用于所有列.但是这会产生任何其他问题吗? 如果我这样做,我可以更进一步,并实际将映射存储在初始表本身中吗?我可以将Column对象的引用放回到创建它们的初始表吗?它似乎与我想象的功能性工作方式有很大不同,但我看不出它会导致什么问题,因为一切都是不可改变的. 通常,函数方法是否会将返回值中的引用保留为其中一个参数?它似乎不会破坏任何东西(如优化或懒惰评估),因为无论如何已经知道了这个论点.但也许我错过了一些东西. 解决方法
我将如何做到这一点:
>从frozenset中派生您的表类. 现在你无法修改表格 – >不变,太棒了!下一步 f T -> T' 这应该被解读为应用表T上的函数f来产生 add(T,A) -> T' 这里最棒的是添加可以减去而不是给你 下面是一个如何实现和处理表的示例 class Table(frozenset): def __new__(cls,names,rows): return frozenset.__new__(cls,rows) def __init__(self,rows): frozenset.__init__(self,rows) self.names = names def add_column(rows,func): return [row + (func(row,idx),) for (idx,row) in enumerate(rows)] def table_process(t,(name,func)): return Table( t.names + (name,),add_column(t,lambda row,idx: func(row)) ) def table_filter(t,func)): names = t.names idx = names.index(name) return Table( names,[row for row in t if func(row[idx])] ) def table_rank(t,name): names = t.names idx = names.index(name) rows = sorted(t,key = lambda row: row[idx]) return Table( names + ('rank',add_column(rows,idx: idx) ) def table_print(t): format_row = lambda r: ' '.join('%15s' % c for c in r) print format_row(t.names) print 'n'.join(format_row(row) for row in t) if __name__ == '__main__': from random import randint cols = ('c1','c2','c3') T = Table( cols,[tuple(randint(0,9) for x in cols) for x in range(10)] ) table_print(T) # Columns to add to the table,this is a perfect fit for a # reduce. I'd honestly use a boring for loop instead,but reduce # is a perfect example for how in FP data and code "becomes one." # In fact,this whole program could have been written as just one # big reduce. actions = [ ('max',max),('min',min),('sum',sum),('avg',lambda r: sum(r) / float(len(r))) ] T = reduce(table_process,actions,T) table_print(T) # Ranking is different because it requires an ordering,which a # table does not have. T2 = table_rank(T,'sum') table_print(T2) # Simple where filter: select * from T2 where c2 < 5. T3 = table_filter(T2,('c2',lambda c: c < 5)) table_print(T3) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |