我正在尝试组合列表中的任意数量的数学函数。
我写了一个将它们链接在一起的组合函数:
chain :: (c -> d) -> (a -> b -> c) -> a -> b -> d
g `chain` f = \a b -> g (f a b)
这样做
let b = (*) `chain` (+) `chain` (-)
创建一个带 abcd 的函数,然后执行(( a - b )+ c )× d
> b 2 3 4 5
> 15
我的问题是,我希望能够根据任意列表将一组这些链接在一起:
[(*),(+),(-)]
会产生(*) `chain` (+) `chain` (-)
[(*),(+),(-),(*)]
会产生(*) `chain` (+) `chain` (-) `chain` (*)
这可能吗?我尝试过使用折叠,但由于每次应用元素后累加器的类型都会发生变化,所以无法使用折叠。
例如,链接在一起的三个函数的类型是:
b :: Integer -> Integer -> Integer -> Integer -> Integer
但是四是:
b :: Integer -> Integer -> Integer -> Integer -> Integer -> Integer
如果有人能指出我正确的方向或知道解决方案,那就太棒了!谢谢。
编辑:
我的最终目标是能够做到这样的事情:
getZipList $ pure (function composing * and +) <*> ZipList [1,2,3] <*> ZipList [4,5,6]
<*> ZipList [7,8,9]
会导致:
[(1+4)*7, (2+5)*8, (3+6)*9]
答案 0 :(得分:9)
好吧,正如你自己注意到它实际上是不可能的,因为不同的列表长度(对于类型系统是不可见的)会导致不同的类型。唯一“可靠”的方法就是使用某种编译时列表;有各种各样的,但没有一个真正得到稳定支持和易于使用。
最简单的替代方法是将参数作为列表。即,
ifxChain :: [a->a->a] -> [a] -> a
可以很容易地实现:它基本上是一个zip,为每个函数提供第二个参数,省略第一个参数。然后将第一个参数折叠到单个参数函数的结果列表中:
ifxChain fs (p0:ps) = foldl' (flip ($)) p0 $ zipWith flip fs ps
您可能希望添加处理不匹配长度的列表,应该可以合理地安全。
如果你坚持实际可变数量的函数参数(我不认为这很好!)那么你需要一些类型类hackery。这些可变参数函数最为人所知。printf
。
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
class ChainedFunc f t where
chainIfxs :: [t->t->t] -> t -> f
chainIfxs fs i = chainIfxPre i id $ reverse fs
chainIfxPre :: t -> (t->t) -> [t->t->t] -> f
instance ChainedFunc t t where
chainIfxPre i f [] = f i
instance (ChainedFunc f t) => ChainedFunc (t->f) t where
chainIfxPre i fin (f:fs) x0 = chainIfxPre i (flip f x0 . fin) fs
显然,这在很多方面都不好看。但是,好吧,它有效......啊......
主&GT; chainIfxs [(*),(+),( - )](2 :: Int)(3 :: Int)(4 :: Int)(5 :: Int):: Int
15
答案 1 :(得分:2)
这个想法可以帮助你发展一般功能吗?
Prelude> :m +Data.List
Prelude Data.List> let fs = [(-),(+),(*)]
Prelude Data.List> foldl' (\accum (i,f) -> accum `f` i) 2 (zip [3..5] fs)
15