1

可能重复:
为什么在haskell中不允许这样的函数定义?

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

例如:

let f x1 x2 x3 = x1+ x2 + x3

我想要这种行为

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

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

我试过这个,

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

但它不起作用。我如何实现这一目标?我可以使用数据类型来做到这一点吗?

4

5 回答 5

5

With type families, you can get pretty far—but it surely is not for the faint of heart:

{-# 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

For your example, we get:

> let f x y z = x + y + z
> flist f [2, 3, 5]
10
于 2012-09-06T08:33:11.680 回答
2

你不能flist直接创建你的,因为没有合理的类型可以给它。

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

取决于列表的长度——你只知道flist列表的长度,即不是在编译时,所以你不能编译它。

使用Template Haskell可以编写一个flist你可以使用的“函数”,[flist| (+) [3,4] ]但是 Template 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

但是如果你想对它们做一些统一的事情,比如把它们加起来,你可以使用预先编写的高阶函数,比如sumorproduct来相加或相乘,或者使用foldl. (例如,语言定义sumsum = foldl (+) 0.

于 2012-09-06T06:13:04.757 回答
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列表上,但删除Nothings 并将Just值收集到列表中。如果删除Nothings 不是所需的行为,那么可以使用mapM(对函数进行一些调整)。)

但是,如果exp应该能够采用类型等函数a -> a -> aa -> a -> a -> a那么给出exp一个可用的类型签名将是困难的(在像 Haskell 这样的非依赖类型语言中可能是不可能的),因为 arityf不是固定的。

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

于 2012-09-06T07:41:03.383 回答
0

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

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

但是您应该解释手头的更大任务,因为似乎不需要高级通用技术,并且可以解决您的问题。

于 2012-09-06T08:39:09.963 回答
0
flist f [] = f
flist f (x:xs) = flist (f x) xs

像这样的东西不会起作用,因为你 f 的类型不固定。

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

你在什么地方插入?将取决于列表中元素的数量。
那么像类型类之类的东西可以用来定义这样的函数。

一种方法是为您拥有的所有类型的函数显式定义每个函数,或者您可以使用 little type hackery。

我写了一个凌乱的 hack,只是为了表明可以使用某种类型的技巧。它仍然不是很灵活,因为您需要显式地提供所有参数的类型。添加一些功能依赖可能会解决这个问题。当元素的数量与函数 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  

无论您尝试做什么,都可能不需要此类功能。我唯一的建议是重新审视你的代码并尝试看看是否有一些简单的东西适合。

于 2012-09-06T07:37:11.700 回答