2016-11-22 69 views
1

我是Haskell和FP的新手,正在通過LYAH和其他來源工作,但是「通過干涉學習」我試圖編寫一個小程序JSON解析。然而,我已經把自己編碼到了一個角落,並且不能離開。我的代碼是從各種教程中拼湊出來的,我可以感覺到我仍然「在程序上思考」如何很好地將它組合在一起,但是我沒有得到必要的突破來使它工作。將函數映射到新類型定義爲數據結構列表

首先,這裏是一個多級JSON文件的簡化版,它是Weather Underground API的天氣預報,縮短到三個小時。

{ 
    "response": { 
     "version": "0.1", 
     "termsofService": "http://www.wunderground.com/weather/api/d/terms.html", 
     "features": { 
      "hourly": 1 
     } 
    }, 
    "hourly_forecast": [{ 
     "FCTTIME": { 
      "hour": "8", 
      "epoch": "1479736800", 
      "pretty": "8:00 AM CST on November 21, 2016" 
     }, 
     "temp": { 
      "english": "27", 
      "metric": "-3" 
     }, 
     "condition": "Partly Cloudy" 
    }, { 
     "FCTTIME": { 
      "hour": "9", 
      "epoch": "1479740400", 
      "pretty": "9:00 AM CST on November 21, 2016" 
     }, 
     "temp": { 
      "english": "32", 
      "metric": "0" 
     }, 
     "condition": "Partly Cloudy" 
    }, { 
     "FCTTIME": { 
      "hour": "10", 
      "epoch": "1479744000", 
      "pretty": "10:00 AM CST on November 21, 2016" 
     }, 
     "temp": { 
      "english": "35", 
      "metric": "2" 
     }, 
     "condition": "Clear" 
    }] 
} 

接下來,這裏是我的Haskell程序。我是成功解析JSON到newtype稱爲ForecastPointCollection它被定義爲ListWeatherPoint,這是一個data結構的各種東西來自JSON文件。但是,我不知道如何取消[WeatherPoint]列表(請參閱代碼註釋)。作爲列表中「可以做的事情」的測試,我想將Celcius溫度轉換爲Kelvin,並獲得一個新的List,我可以使用它(輸出到JSON,不管),請輸入show

{-# LANGUAGE OverloadedStrings #-} 
-- {-# LANGUAGE RecordWildCards #-} 
{-# LANGUAGE ScopedTypeVariables #-} 

module Main where 

import   Data.Aeson 
import   Data.Aeson.Types 

import   Control.Applicative ((<$>), (<*>)) 
import   Control.Monad  (mzero) 

import qualified Data.ByteString.Lazy as BSL 
import qualified Data.Text   as T 
import qualified Data.Vector   as V 

type MetricTemperature = Int 
type KelvinTemperature = Int 

newtype ForecastPointCollection = ForecastPointCollection 
    {forecastpointcollection :: [WeatherPoint]} deriving Show 

data WeatherPoint = WeatherPoint 
    { epoch  :: T.Text 
    , prettyTime :: T.Text 
    , tempMetric :: MetricTemperature 
    , condition :: T.Text 
    } deriving Show 

instance FromJSON ForecastPointCollection where 
    parseJSON (Object o) = 
    ForecastPointCollection <$> o .: "hourly_forecast" 
    parseJSON _ = mzero 

data ProcessedWeatherPoint = ProcessedWeatherPoint 
    { newEpoch  :: T.Text 
    , newPrettyTime :: T.Text 
    , newTempKelvin :: KelvinTemperature 
    , newCondition :: T.Text 
    } deriving Show 

instance FromJSON WeatherPoint where 
    parseJSON = 
    withObject "Root Object Arbitrary Name" $ \o -> do 
    fctO <- o .: "FCTTIME" 
    epoch <- fctO .: "epoch" -- contained within FCTTIME 
    pretty <- fctO .: "pretty" -- contained within FCTTIME 
    tempO <- o .: "temp" 
    metric <- tempO .: "metric" -- contained within temp 
    condition <- o .: "condition" -- at top level under hourly_forecast 
    return $ WeatherPoint epoch pretty (read metric) condition 
    -- parseJSON _ = mzero 

kelvinizeTemp :: MetricTemperature -> KelvinTemperature 
kelvinizeTemp x = x + 273 -- hey, close enough 

adjustTemp :: Maybe ForecastPointCollection -> [ProcessedWeatherPoint] 
adjustTemp Nothing = [] 
adjustTemp x = [] -- HERE IS WHERE I AM LOSING MY WAY! 
        -- HOW CAN I WALK THROUGH THE LIST INSIDE ForecastPointCollection 
        -- TO map kelvinizeTemp ACROSS THAT LIST AND 
        -- GET A [ProcessedWeatherPoint] LIST BACK TO PLAY WITH? 

getSampleForecast = BSL.readFile "/home/mypath/test/forecastsubmit.json" 

main = do 
    textOfJson <- getSampleForecast 
    let (forecasts2 :: Maybe ForecastPointCollection) = decode textOfJson 
    case forecasts2 of 
    Just (ForecastPointCollection forecasts2) -> do 
     putStrLn ("Success!") 
     putStrLn . show $ forecasts2 
    _ -> putStrLn "Could not parse ForecastPointCollection JSON correctly." 
    -- So far so good, we've extracted data from the JSON and stored it in memory. 
    -- But now, how can we manipulate that data and start doing stuff with it? 
    -- Currently, the "adjustTemp" function returns an empty list no matter what. 
    let (processed2 :: [ProcessedWeatherPoint]) = adjustTemp forecasts2 
    putStrLn ("More success (OK, not really, yet)!") 
    putStrLn . show $ processed2 

任何意見讚賞。我應該不要讓ForecastPointCollection a newtype?我在哪裏成爲慣用語,我在哪裏只是白癡? :-p

根據回答更新:對於後代,這裏是新定義的processWeatherPoint函數的一個可能的(工作)實現。一個data結構的片斷應該被認爲是一個功能!

processWeatherPoint :: WeatherPoint -> ProcessedWeatherPoint 
processWeatherPoint x = ProcessedWeatherPoint 
    (epoch x) 
    (prettyTime x) 
    (kelvinizeTemp (tempMetric x)) 
    (condition x) 

kelvinizeTemp :: MetricTemperature -> KelvinTemperature 
kelvinizeTemp x = x + 273 -- this works OK because both types are type of Int 

回答

3

這應該足以定義一個函數...

processWeatherPoint :: WeatherPoint -> ProcessedWeatherPoint 

...提取與[WeatherPoint]從NEWTYPE領域,在列表上映射功能:

adjustTemp :: Maybe ForecastPointCollection -> [ProcessedWeatherPoint] 
adjustTemp Nothing = [] 
adjustTemp (Just (ForecastPointCollection points)) = processWeatherPoint <$> points 

ForecastPointCollection上的模式匹配的替代方法是使用該字段的記錄訪問器。

adjustTemp :: Maybe ForecastPointCollection -> [ProcessedWeatherPoint] 
adjustTemp Nothing = [] 
adjustTemp (Just forecast) = processWeatherPoint <$> forecastpointcollection forecast 

的書面定義略高於涉及使用maybe功能,而不是在做Maybe明確的案例分析可以說是更方便的方法:

,如果你不打算出口的構造,這將是特別有用的
adjustTemp :: Maybe ForecastPointCollection -> [ProcessedWeatherPoint] 
adjustTemp = maybe [] (fmap processWeatherPoint . forecastpointcollection) 
+0

這個答案非常有幫助(就像後來的「Just」編輯來幫助它編譯一樣),我已經將它標記爲接受的答案。謝謝你Haskellers! –

相關問題