[英]map function to newtype defined as a List of Data structures
I'm new to Haskell and FP and am working my way through LYAH and other sources, but to "learn by doing" I'm trying to write a small program involving JSON parsing. 我是Haskell和FP的新手,我正在通过LYAH和其他来源工作,但要“边做边学”,我正在尝试编写一个涉及JSON解析的小程序。 However, I've coded myself into a corner and can't get out. 但是,我已将自己编入角落,无法离开。 My code is cobbled together from various tutorials and I can sense I'm still "thinking procedurally" about how to put it together nicely, but I haven't gotten to the needed breakthrough to make it work. 我的代码是从各种教程中拼凑而成的,我可以感觉到我仍然“在程序上思考”如何将它整合在一起,但我还没有达到必要的突破才能使它工作。
First, here is a chopped-down version of the multi-level JSON file, it's a weather forecast from the Weather Underground API, reduced to three hours. 首先,这是一个多级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"
}]
}
Next, here is my Haskell program. 接下来,这是我的Haskell程序。 I'm successfully parsing the JSON into a newtype
called ForecastPointCollection
which is defined as a List
of WeatherPoint
, which is a data
structure of various things that came from the JSON file. 我成功地解析JSON成newtype
称为ForecastPointCollection
其定义为一个List
的WeatherPoint
,这是一个data
的,从JSON文件来各种事物的结构。 But then, I can't figure out how to get the [WeatherPoint]
list back out (see code comments). 但是,我无法弄清楚如何将[WeatherPoint]
列表退出(参见代码注释)。 As a test of "something to do" with the list, I want to convert the Celcius temperature to Kelvin and get a new List
that I can work with (output to JSON, do a show
on, whatever). 作为对列表中“要做的事情”的测试,我想将Celcius温度转换为开尔文并获得一个我可以使用的新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
Any advice appreciated. 任何建议表示赞赏 Should I not make ForecastPointCollection
a newtype
? 我不应该让ForecastPointCollection
一个newtype
? Where am I being idiomatic and where am I just idiotic? 我在哪里惯用,我在哪里愚蠢? :-p :-P
Update based on answer: For posterity, here is a possible (working) implementation of the newly-defined processWeatherPoint function. 基于答案更新:对于后代,这里是新定义的processWeatherPoint函数的可能(工作)实现。 The pieces of a data
structure should be thought of as a function! 应该将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
It should be enough to define a function... 它应该足以定义一个函数......
processWeatherPoint :: WeatherPoint -> ProcessedWeatherPoint
... extract the field with the [WeatherPoint]
from the newtype and map the function over the list: ...使用[WeatherPoint]
的[WeatherPoint]
提取字段,并将该函数映射到列表上:
adjustTemp :: Maybe ForecastPointCollection -> [ProcessedWeatherPoint]
adjustTemp Nothing = []
adjustTemp (Just (ForecastPointCollection points)) = processWeatherPoint <$> points
An alternative to pattern matching on the ForecastPointCollection
is using the record accessor for the field. ForecastPointCollection
上的模式匹配的替代方法是使用该字段的记录访问器。 That would be specially useful if you didn't intend to export the constructor: 如果您不打算导出构造函数,那将特别有用:
adjustTemp :: Maybe ForecastPointCollection -> [ProcessedWeatherPoint]
adjustTemp Nothing = []
adjustTemp (Just forecast) = processWeatherPoint <$> forecastpointcollection forecast
An arguably more convenient way of writing the definition just above involves using the maybe
function instead of doing explicit case analysis on Maybe
: 一种可以说是更方便的编写上面定义的方法涉及使用maybe
函数而不是对Maybe
进行明确的案例分析:
adjustTemp :: Maybe ForecastPointCollection -> [ProcessedWeatherPoint]
adjustTemp = maybe [] (fmap processWeatherPoint . forecastpointcollection)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.