Haskell 部分应用一组函数
我正在设计一个Haskell模块,用于解决一个可能有各种参数化的数学问题。该模块导出一个函数:Haskell 部分应用一组函数,haskell,Haskell,我正在设计一个Haskell模块,用于解决一个可能有各种参数化的数学问题。该模块导出一个函数: run_and_output_parameterization :: ProblemParams -> String -> IO () 其中,思想是在一些“控制器”中生成ProblemParams对象,并按如下方式调用: map (\(pp, name) -> run_and_output_parameterization pp name) (zip pp_list names_l
run_and_output_parameterization :: ProblemParams -> String -> IO ()
其中,思想是在一些“控制器”中生成ProblemParams
对象,并按如下方式调用:
map (\(pp, name) -> run_and_output_parameterization pp name) (zip pp_list names_list)
我的问题是,在模块中,有一些函数,如索引函数,我想部分应用于特定的参数化。比如说,
evenly_spaced_point_approx :: Int -> Int -> Int -> Double -> Double -> Int
evenly_spaced_point_approx xmin xmax xstep_i xstep_d target = pt
where
pt = max (min (round (target/xstep_d) * xstep_i) xmax) xmin
evenly_spaced_si_approx target = evenly_spaced_point_approx (_pp_si_min pp) (_pp_si_max pp) (_pp_nstep_s pp) (_pp_nstep_sd pp) target
evenly_spaced_wi_approx target = evenly_spaced_point_approx (_pp_wi_min pp) (_pp_wi_max pp) (_pp_nstep_w pp) (_pp_nstep_wd pp) target
我想使用模块中的函数等间距近似值
和等间距近似值
,用于特定的ProblemParameter数据结构(称为pp
)
有没有办法告诉Haskell部分应用所有依赖函数,或者这是我必须手工完成的?另外,我对函数式编程术语的不精确表示歉意。如果您有许多函数需要相同的参数,并且这是它们使用的唯一(或最后一个)参数,那么您可以利用
(>)r
的Monad
实例。或者,您可以将所有内容包装在阅读器
monad中,monad的定义基本上是
newtype Reader r a = Reader { runReader :: r -> a }
instance Monad (Reader r) where
return a = Reader $ \_ -> a
m >>= f = Reader $ \r -> runReader (f (runReader m r)) r
与(>)r
的Monad
实例相比:
instance Monad ((->) r) where
return a = const a
m >>= f = \r -> f (m r) r
你怎么能用这个?例如,如果您有一个参数pp::ProblemParams
,那么您可以将函数编写为
-- Some declarations
smallFunc1 :: ProblemParams -> Double
smallFunc2 :: ProblemParams -> Double
smallFunc3 :: Int -> ProblemParams -> Double
doStuff :: ProblemParams -> Double -- Just a random return type
doStuff = do -- Keep the parameter implicit
result1 <- smallFunc1 -- The ProblemParams are automatically passed
result2 <- smallFunc2
result3 <- smallFunc3 10
return $ result1 + result2 + result3
我们必须创建一个reader
函数,将原语提升到reader
monad的原因是reader
实际上是根据transformerReaderT
定义的
type Reader r a = ReaderT r Identity a
围绕着标识
单子
你决定用哪个取决于你。我想大多数人会更熟悉阅读器
版本,如果你决定以后再叠加一些变压器,那就很简单了。Reader
monad基本上有助于使函数签名看起来是一元的,因为ProblemParams->Double
看起来不像普通的monad签名。它将使用更多的代码,但它可能会帮助您对程序进行推理
注意:我没有运行任何这段代码,所以请注意可能存在小错误。如果有人发现问题,就告诉我,我会解决的
带有
Par
monad和ReaderT
的示例:
type App a = ReaderT ProblemParams Par a
runApp :: ProblemParams -> App a -> a
runApp pp app = runPar $ runReaderT app pp
然后,您可以简单地使用lift
将Par
操作提升到App
操作:
parReader :: (ProblemParams -> Par a) -> App a
parReader f = do
r <- ask
lift $ f r
-- parReader f = ask >>= lift . f
doStuff :: ProblemParams -> Double
doStuff pp = runApp pp $ do
result1 <- parReader parAction1
result2 <- parReader parAction2
result3 <- parReader (parAction3 10)
return $ result1 + result2 + result3
<代码>收件人:(问题PARAM-> PAR A)-APP A
parReader f=do
r>=提升。F
doStuff::ProblemParams->Double
doStuff pp=runApp pp$do
结果1依赖函数是什么意思?你的意思是自动传递某个论点吗?根据您想要使用的函数的定义,您可以利用无点表示法,但这并不总是可行的方法。我的想法是,我的
运行和输出参数化函数是DAG函数的根,所有这些函数都需要ProblemParameterization对象。我想看看是否可以避免到处传播。听起来你需要阅读器
单子。我将提供一个例子作为答案。只是想:map(\(a,b)->fab)(zip-xs-ys)
可以简化为map(uncurry-f)(zip-xs-ys)
,然后是zipWith f-xs-ys
。哦,是的,zipWith
。谢谢你的提醒!非常感谢。这很有帮助。@stevejb这是完全解决了您的问题,还是您还有其他问题?我想这就涵盖了目前的问题。在实际程序中的doStuff
函数中,我计划使用Control.Monad.Par
中的parMap来执行并行映射。这不会是个问题吧?@stevejb您可以在Par
monad上使用ReaderT
转换器,我将用一个示例编辑我的答案(我将首先确保有效),这是非常有用的。谢谢你,贝克利尔!
parReader :: (ProblemParams -> Par a) -> App a
parReader f = do
r <- ask
lift $ f r
-- parReader f = ask >>= lift . f
doStuff :: ProblemParams -> Double
doStuff pp = runApp pp $ do
result1 <- parReader parAction1
result2 <- parReader parAction2
result3 <- parReader (parAction3 10)
return $ result1 + result2 + result3