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

我正在设计一个Haskell模块,用于解决一个可能有各种参数化的数学问题。该模块导出一个函数:

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
实际上是根据transformer
ReaderT
定义的

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