折叠功能列表?

时间:2014-04-02 23:14:34

标签: haskell

我正在尝试组合列表中的任意数量的数学函数。

我写了一个将它们链接在一起的组合函数:

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]

2 个答案:

答案 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