Haskell:创建一个在列表上行动的功能

时间:2012-09-06 05:52:16

标签: haskell higher-order-functions

  

可能重复:
  Why is such a function definition not allowed in haskell?

我想创建一个函数flist,它将函数f作为参数,并返回另一个函数,其参数将是一个列表,但行为与f完全相同。

例如:

let f x1 x2 x3 = x1+ x2 + x3

我想要这种行为

(flist f) [x1,x2,x3] = x1+x2+x3

当列表长度不是3时,它可能以任何方式运行。 flist应该处理任何函数(不仅是带有3个参数的函数,即g x1 x2 x3 x4 = x1+x2+x3*x4,然后是(flist g) [x1,x2,x3,x4] = x1+x2+x3*x4)。

我试过了,

flist f [] = f
flist f (x:xs) = flist (f x) xs

但它不起作用。我该如何实现这一目标?我可以使用数据类型来执行此操作吗?

5 个答案:

答案 0 :(得分:5)

对于类型系列,你可以走得很远 - 但它肯定不适合胆小的人:

{-# LANGUAGE FlexibleContexts  #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies      #-}

class FList a where
  type Point a
  flist :: a -> [Point a] -> Point a

instance FList (a -> a) where
  type Point (a -> a) = a
  flist f [x] = f x

instance (FList (a -> b), a ~ Point (a -> b)) => FList (a -> (a -> b)) where
  type Point (a -> (a -> b)) = Point (a -> b)
  flist f (x : xs) = flist (f x) xs

对于您的示例,我们得到:

> let f x y z = x + y + z
> flist f [2, 3, 5]
10

答案 1 :(得分:2)

您无法直接创建flist,因为没有明智的类型可以提供。

flist :: (a -> a -> a -> ..... -> a) -> [a] -> a

取决于列表的长度 - 一旦你知道列表有多长,你只知道flist的类型,即不是在编译时,所以你不能编译它。

可以Template Haskell编写一个flist“函数”,你可以使用[flist| (+) [3,4] ],但模板Haskell是非常先进的东西,我认为你现在应该避免,以及语法甚至比你想要的语法更丑,这已经比(+) 3 4更难。

如果你知道你将拥有多少个参数,你可以使用以下功能之一:

flist1 f [x] = f x
flist2 f [x,y] = f x y
flist3 f [x,y,z] = f x y z
flist4 f [a,b,c,d] = f a b c d
flist5 f [a,b,c,d,e] = f a b c d e

但如果您想要与他们做一些统一的事情,比如添加它们,您可以使用预先编写的高级函数(如sumproduct来添加或乘以,或者使用foldl滚动自己。 (例如,sum的语言定义为sum = foldl (+) 0

答案 2 :(得分:2)

这对于固定数量的参数来说并不难,例如

flist3 f [a,b,c] = f a b c
flist3 _ _       = 0

(我注意到您在数字上下文中使用该函数,因此默认为0完全没问题。)

在更一般的上下文中,可以通过返回Maybe值来表示匹配成功或失败,例如

flist3 f [a,b,c] = Just $ f a b c
flist3 _ _       = Nothing

然后可以使用它:

import Data.Maybe

exp f n = (sum . mapMaybe (flist3 f) $ booleanCube n) / 2^n

mapMaybe将一个函数a -> Maybe b映射到列表中,但会删除Nothing并将Just值收集到列表中。如果删除Nothing } s不是理想的行为,那么可以使用mapM代替(对函数进行一些调整)。)

但是,如果exp应该能够使用a -> a -> aa -> a -> a -> a等类型的函数,那么给exp一个可用的类型签名将很难(可能因为f的arity不固定,所以在Haskell之类的非依赖类型语言中是不可能的。

(正如@Mystic演示的那样,可以使用类型类在Haskell中创建可变参数函数,但这与您想要的行为略有不同。)

答案 3 :(得分:0)

flist f [] = f
flist f (x:xs) = flist (f x) xs

这样的东西不起作用,因为你的f类型没有修复。

f :: (? -> b) -> [a] -> b 

你插入什么?将取决于列表中元素的数量 类似类的类可以用来定义这样的函数。

一种方法是明确定义所有类型函数的每个函数,或者使用小型hackery。

我写了一个混乱的黑客,只是为了表明它可能使用某种类型的技巧。它仍然不是很灵活,因为您需要明确提供所有参数的类型。添加一些功能依赖可能会解决这个问题。当元素数量与函数f的顺序不匹配时,它会引发异常。

{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}

class FList a b c where 
    flist :: a -> b -> c

instance FList a [s] a where 
    flist x _ = x


instance (FList a [s] b) => FList (s -> a) [s] b where 
    flist f (x:xs) =  flist (f x) xs


f:: Int -> Int -> Int -> Int 
f a b c = a + b * c

test :: [Int]
test = [1,2,3]

foo :: Int 
foo = (flist f) test  

无论你想做什么,都可能不需要这样的功能。我唯一的建议是重新审视你的代码并试着看看是否适合。

答案 4 :(得分:0)

关于arity-generic编程的论文很多,例如。

http://www.seas.upenn.edu/~ccasin/papers/aritygen.pdf

但你应该解释一个更大的任务,因为它似乎不需要先进的通用技术,并且可以解决你的问题。