Haskell 将列表的内容用作单个多参数函数的位置参数
是否有一个标准的Haskell函数(或模式)来提取列表的内容并将其作为函数的有序位置参数提供Haskell 将列表的内容用作单个多参数函数的位置参数,haskell,arguments,Haskell,Arguments,是否有一个标准的Haskell函数(或模式)来提取列表的内容并将其作为函数的有序位置参数提供 例如,考虑函数(/)>代码>,当给定两个位置参数时,将从它们中生成一个二元组: (,) 3 4 --> (3,4) 相反,假设我有一些无法更改的外部函数调用提供给我的参数,表示为列表[3,4] 是否有一个“内容”操作,使其能够工作: (,) $ contents_of [3, 4] 因此,的contents\u的行为就好像这些项被放置在源代码中,它们之间有空格作为函数应用程序一样 例如,[1]
例如,考虑函数<代码>(/)>代码>,当给定两个位置参数时,将从它们中生成一个二元组:
(,) 3 4 --> (3,4)
相反,假设我有一些无法更改的外部函数调用提供给我的参数,表示为列表[3,4]
是否有一个“内容”操作,使其能够工作:
(,) $ contents_of [3, 4]
因此,的contents\u的行为就好像这些项被放置在源代码中,它们之间有空格作为函数应用程序一样
例如,[1]
的(,)$contents_应该是当前的函数(,)1)
,然后再使用一个参数来完成元组的创建
我的一个想法是尝试将函数折叠到列表上,折叠函数表示currying:
foldr (\x y -> y x) (,) [3, 4]
但是看看foldr的类型签名:
foldr :: (a -> b -> b) -> b -> [a] -> b
这似乎很难做到<这里的code>b
需要是函数类型本身,但是当它被应用到参数时,它将不再是与b
具有相同类型签名的函数,从而导致折叠中的类型问题
这在精神上类似于Python的*args
构造
我不关心这可能意味着什么严格的属性——只关心在标准Haskell中是否可能出现类似的情况。我通常使用如下匹配模式
let (a:b:c:_) = args
in func a b c
或内联
(\(a:b:c:_) -> func a b c) args
如果您真的想这样做,您可以创建一个操作符来将“inyect”元素列表添加到任何函数中
showAdd2 <<< args
但您必须为每种情况选择适当的“inyector”
showAdd2 `i2` [4..5]
(在Haskell中,代数数据类型、列表和类的使用恰当地取代了对多变量函数的需求。正如@user5402所说,您应该提供一些明确的问题)可以这样表示N元函数:
data FunN r a = FunN Int (a -> FunN r a) | FNil r
然后将普通函数转换为FunN
:
f2FunN :: (FunN (a->b) a) -> FunN b a
f2FunN (FNil g) = FunN 1 (FNil . g)
f2FunN (FunN n g) = FunN (n+1) (f2FunN . g)
然后应用参数列表:
a :: FunN b a -> [a] -> b
a (FNil r) [] = r
a (FunN _ f) (x:t) = a (f x) t
a _ _ = error "wrong arity"
例如:
Prelude> a (f2FunN $ f2FunN $ FNil (+)) [1,2]
3
Prelude> a (f2FunN $ FNil (+)) [1] 2
3
Prelude> a (f2FunN $ f2FunN $ FNil (+)) [1,2,3]
*** Exception: wrong arity
Prelude> a (f2FunN $ f2FunN $ FNil (+)) [1]
*** Exception: wrong arity
但当然,您需要在编译时知道函数的算术性,这样您就可以知道可以用
f2FunN
包装函数多少次,正如我在上面的注释中所解释的,我认为这在标准列表中是不可能的。因此,让我们引入一个新的列表,其中列表中的每个元素都可以是不同的类型,并在列表的类型中进行编码:
{-# LANGUAGE GADTs, MultiParamTypeClasses, FlexibleInstances #-}
data Nil
data TList a b where
TEmpty :: TList Nil Nil
(:.) :: c -> TList d e -> TList c (TList d e)
infixr 4 :.
tenty
这里类似于[]
,:.
到:
。所以我们可以有一个Int
的列表,和几个Bool
s:31:。对:。错误:。诱惑
。此列表的类型为tlistint(tlistbool(tlistbool(tlistnil-Nil))
现在,我们可以引入一个typeclass,它提供了一个函数,在类型匹配的情况下,该函数可以按照您提出的方式将任意函数应用于列表
class TApplies f h t r where
tApply :: f -> TList h t -> r
instance TApplies a Nil Nil a where
tApply a _ = a
instance TApplies f h t r => TApplies (a -> f) a (TList h t) r where
tApply f (e :. l) = tApply (f e) l
我们现在可以使用tApply
来做您想要做的事情。请注意,类似以下内容将无法编译:
tApply (+) $ 1 :. (2 :: Int) :. TEmpty
我们必须清楚地标注所有内容:
tApply ((+) :: Int -> Int -> Int) $ (1 :: Int) :. (2 :: Int) :. TEmpty :: Int
正如我们所期望的那样,这将提供3
。我不知道如何避开这种需要;我希望巧妙地使用功能依赖性可以达到目的。为什么列表本身不起作用
从狭义上讲
是否有一个标准的Haskell函数(或模式)来提取列表的内容并将其作为函数的有序位置参数提供
这有一个非常简单的答案“不”。这种函数不存在的原因是类型签名不容易理解。您要求的类型签名是:
applyToList :: (a -> c) -> [a] -> d
其中,c
要么具有形式a->c'
(在这种情况下,我们递归定义),要么具有类型d
。前奏曲函数都没有古怪的类型签名
如果您主动忽略此项并尝试,则会出现以下错误:
Prelude> let applyToList f list = case list of [] -> f; x : xs -> applyToList (f x) xs
<interactive>:9:71:
Occurs check: cannot construct the infinite type: t1 ~ t -> t1
Relevant bindings include
xs :: [t] (bound at <interactive>:9:52)
x :: t (bound at <interactive>:9:48)
list :: [t] (bound at <interactive>:9:19)
f :: t -> t1 (bound at <interactive>:9:17)
applyToList :: (t -> t1) -> [t] -> t -> t1
(bound at <interactive>:9:5)
In the first argument of ‘applyToList’, namely ‘(f x)’
In the expression: applyToList (f x) xs
然后,您可以编写如下内容:
wrap3 (\x y z -> (x + y) * z) `applyAll` [9, 11, 2]
它将正确地构造40
正如您所知,这涉及到很多麻烦,但是有必要以完全通用的方式告诉编译器“嘿,这个函数将有三个参数,因此长度为3的列表非常适合它”
当然,编写applyAll(wrap3\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。但是,如果您试图构建具有任意算术运算的函数库,您可能可以使用一些额外的函数来为您管理这些算术运算
您可能还想用Z
,sz
等来注释列表的长度——在这种情况下,我想您可以得到一个applyAll
,它可以进行转换。另外,正如另一个答案所指出的,如果有多个Wrap
构造函数,可以将递归移到数据类型本身,这可能会让你有一个很好的距离——可能会删除一些讨厌的语言扩展。如果没有依赖类型或大量扩展,就不可能编写真正的arity泛型函数,但你可以欺骗一点:
z :: b -> [a] -> b
z x _ = x
s :: (b -> [a] -> c) -> (a -> b) -> [a] -> c
s h f (x:xs) = h (f x) xs
s _ _ _ = error "function called on too few arguments"
sum3 a b c = a + b + c
tup4 a b c d = (a, b, c, d)
main = do
print $ z 0 [1..4] -- 0
print $ s (s (s z)) sum3 [1..4] -- 6
print $ s (s (s (s z))) tup4 [1..4] -- (1, 2, 3, 4)
-- print $ s (s (s (s z))) tup4 [1..3] -- runtime error
或者,如果要抛出错误,如果列表中的元素太多,则将z
的定义更改为
z :: b -> [a] -> b
z x [] = x
z _ _ = error "function called on too many arguments"
foldr
无法工作,因为它无法停止;我想函数的签名会变成编译时错误。是的,我提到过这一点。其他褶皱都有类似的问题。他们希望累加器的最终值的类型与其起始值相同,但中间货币函数将具有不同的类型。所以问题是,如果列表的长度超过了一个functi的参数数量,那么你不也会遇到类型问题吗
wrap3 (\x y z -> (x + y) * z) `applyAll` [9, 11, 2]
z :: b -> [a] -> b
z x _ = x
s :: (b -> [a] -> c) -> (a -> b) -> [a] -> c
s h f (x:xs) = h (f x) xs
s _ _ _ = error "function called on too few arguments"
sum3 a b c = a + b + c
tup4 a b c d = (a, b, c, d)
main = do
print $ z 0 [1..4] -- 0
print $ s (s (s z)) sum3 [1..4] -- 6
print $ s (s (s (s z))) tup4 [1..4] -- (1, 2, 3, 4)
-- print $ s (s (s (s z))) tup4 [1..3] -- runtime error
z :: b -> [a] -> b
z x [] = x
z _ _ = error "function called on too many arguments"