2014-08-28 98 views
8

假设我有一个记录列表,我想通过取中位数来对它进行总结。更具体地讲,说我有总结Haskell记录列表

data Location = Location { x :: Double, y :: Double } 

我的测量结果列表,我想将它总结成一个中间Location,所以像:

Location (median (map x measurements)) (median (map y measurements)) 

这很好,但如果我有一些更多的嵌套,如:

data CampusLocation = CampusLocation { firstBuilding :: Location 
             ,secondBuilding :: Location } 

我有CampusLocation个清单,我想总结CampusLocation,其中递归地应用到所有的中位数领域。

在Haskell中做到这一点最干净的方法是什么?镜头? Uniplate中?

编辑:奖金:

如果不是包含我们要总结领域的记录,我们有什么,而不是一个隐含的名单?例如:

data ComplexCampus = ComplexCampus { buildings :: [Location] } 

我们怎样才能总结出[ComplexCampus]ComplexCampus,假设每个buildings的是相同的长度?

+0

I a米突然想象这种事情将适合镜头遍历的“双重”:“应用程序”与“forall f”类型。可遍历的f =>(f a→b)→(f s→t)'。不知道有没有人想过这些。 – 2014-08-28 04:13:37

+0

@ØrjanJohansen我不确定这是否与此有关,但[分配]中有一个“cotraverse”(http://hackage.haskell.org/package/distributive-0.4.4/docs/Data-Distributive。 HTML)。 – 2014-08-28 07:01:00

+0

@AndrásKovács看起来很相关,我应该记住这一点。按照Kmett通常的命名方案,我所建议的(除了用'Functor',他认为就足够了)将是一个“cotraversal”。为了使问题中的类型实际上是“分布式”的,他们需要将'Double'变成一个类型参数。 – 2014-08-28 15:49:45

回答

4

这是一个summarize :: [ComplexCampus] -> ComplexCampus的实现,它使用带有Uniplate的透镜(如你所提到的)来汇总ComplexCampus的一个单一ComplexCampus列表。

{-# Language TemplateHaskell,DeriveDataTypeable #-} 
import Control.Lens 
import Data.Data.Lens 
import Data.Typeable 
import Data.Data 
import Data.List(transpose,genericLength) 
data Location = Location { _x :: Double, _y :: Double } deriving(Show,Typeable,Data) 


data CampusLocation = CampusLocation { _firstBuilding :: Location, _firsecondBuilding :: Location }deriving(Show,Typeable,Data) 
data ComplexCampus = ComplexCampus { _buildings :: [Location] } deriving(Show,Typeable,Data) 


makeLenses ''Location 
makeLenses ''CampusLocation 
makeLenses ''ComplexCampus 

l1 = Location 1 10 
l2 = Location 2 20 
l3 = Location 3 30 


c1 = CampusLocation l1 l2 
c2 = CampusLocation l2 l3 
c3 = CampusLocation l1 l3 
campusLocs = [c1,c2,c3] 


c1' = ComplexCampus [l1, l2] 
c2' = ComplexCampus [l2, l3] 
c3' = ComplexCampus [l1, l3] 
campusLocs' = [c1',c2',c3'] 


average l = (sum l)/(genericLength l) 

-- returns average location for a list of locations 
averageLoc locs = Location { 
      _x = average $ locs ^.. biplate . x, 
      _y = average $ locs ^.. biplate . y 
      } 


summarize :: [ComplexCampus] -> ComplexCampus 
summarize ccs = ComplexCampus $ ccs ^.. biplate . buildings ^.. folding transpose . to averageLoc 

使用双片在这里很可能矫枉过正,但无论在averageLoc我们使用biplate地区的清单上获得所有x领域和所有y领域。如果您想将ComplexCampus总结为一个Location,我们可以使用biplate来提取顶级ComplexBuilding的所有值和所有y值。

例如:

campusLocs' ^.. biplate . x给了我们所有的x值和campusLocs' ^.. biplate . y给了我们所有的y值

同样地,把所有的位置,我们可能只是做:

(campusLocs' ^.. biplate) ::[Location]

或者,如果我们想每个Double: (campusLocs' ^.. biplate) ::[Double]

+0

为什么不只是'summarize = ComplexCampus。地图averageLoc。转置。地图_buildings'?这里我没有看到镜头/双板的必要用途。 – 2014-08-28 07:34:51

+4

'sum xs/genericLength xs'遍历两次xs并占用O(n)空间。你想要使用一个严格的折叠,它可以累积总和并计数,所以你可以在恒定空间中遍历一次。欲了解更多信息,请查看http://www.haskellforall.com/2013/08/composable-streaming-folds.html – 2014-08-28 15:39:03

+0

@ReinHenrichs无论如何,最初的问题是要求中位数,而不是平均数,我认为这基本上排除了恒定的空间。 – 2014-08-28 15:42:18