2014-08-28 8 views
8

Diciamo che ho un elenco di record e voglio riassumerlo prendendo la mediana. Più concretamente, dire che hoRiassumi un elenco di record Haskell

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

Ho un elenco di misure, e voglio riassumere in una mediana Location, quindi qualcosa di simile:

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

Questo va bene, ma che cosa succede se io avere qualcosa in più nidificato, come ad esempio:

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

ho una lista di CampusLocation s e voglio una sintesi CampusLocation, in cui la mediana è applicato in modo ricorsivo a tutti campi.

Qual è il modo più pulito per farlo in Haskell? Lenti? UniPlate?

Edit: Bonus:

Che cosa succede se invece di un record che contiene i campi che vogliamo riassumere, avevamo una lista implicita, invece? Per esempio:

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

Come possiamo riassumere un [ComplexCampus] in un ComplexCampus, supponendo che ciascuna delle buildings è la stessa lunghezza?

+0

I a Improvvisamente immaginando questo tipo di cose si adatterebbe un "doppio" traversamento di obiettivi: "applicativi" con tipo 'forall f. Traversable f => (f a -> b) -> (f s -> t) '. Non ho idea se qualcuno ha ancora pensato a quelli. –

+0

@ ØrjanJohansen Non sono sicuro se questo è rilevante qui, ma c'è un "cotraverse" in [distributive] (http://hackage.haskell.org/package/distributive-0.4.4/docs/Data-Distributive. html). –

+0

@ AndrásKovács Questo sembra rilevante e avrei dovuto ricordarlo. Secondo il solito schema di denominazione di Kmett, quello che ho suggerito (eccetto che con 'Functor', che dice è sufficiente) sarebbe un" cotraversal "allora. Per rendere effettivamente i tipi nella domanda 'distributive', dovrebbero cambiare' Double' in un parametro type. –

risposta

4

Ecco un'implementazione di summarize :: [ComplexCampus] -> ComplexCampus che utilizza obiettivi con Uniplate (come indicato) per riepilogare un elenco di ComplexCampus con un singolo 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 

Utilizzando biplate qui è probabile eccessivo, ma a prescindere in averageLoc usiamo biplate sulla lista dei luoghi per ottenere tutti i campi e tutti i xy campi. Se si desidera riepilogare uno Location , è possibile utilizzare biplate per estrarre tutti i valori x e tutti i valori dal livello superiore ComplexBuilding.

Ad esempio:

campusLocs' ^.. biplate . x ci dà tutti i valori x e campusLocs' ^.. biplate . y tutti noi dà y valori

Allo stesso modo, per ottenere tutte le sedi, potremmo semplicemente fare:

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

Or , se volevamo ogni doppio: (campusLocs' ^.. biplate) ::[Double]

+0

Perché non solo 'sumarize = ComplexCampus. mappa mediaLoc. trasporre. mappa _buildings'? Qui non vedo l'uso essenziale per obiettivo/biplate. –

+4

'sum xs/genericLength xs' attraversa xs due volte e prende O (n) spazio. Vuoi usare una piega rigorosa che accumula la somma e conta mentre va in modo da poter attraversare una volta nello spazio costante. Per maggiori informazioni, visita http://www.haskellforall.com/2013/08/composable-streaming-folds.html –

+0

@ReinHenrichs Ad ogni modo, la domanda originale richiede una mediana, non media, che credo fondamentalmente escluda spazio costante. –

Problemi correlati