可能重复:
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
但它不起作用。我该如何实现这一目标?我可以使用数据类型来执行此操作吗?
答案 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
但如果您想要与他们做一些统一的事情,比如添加它们,您可以使用预先编写的高级函数(如sum
或product
来添加或乘以,或者使用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 -> a
和a -> 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
但你应该解释一个更大的任务,因为它似乎不需要先进的通用技术,并且可以解决你的问题。