Haskell / Miranda:找到函数的类型

时间:2012-04-29 16:23:51

标签: haskell types functional-programming type-inference miranda

简要说明:这是Miranda考试的过去考试问题,但语法与Haskell非常相似。

问题:以下表达式的类型是什么?它有什么作用? (定义 函数长度和交换如下所示。

(foldr (+) 0) . (foldr ((:) . length . (swap (:) [] )) [])

length [] = 0

length (x:xs) = 1 + length xs

swap f x y = f y x

注意:

请随时回复haskell语法 - 抱歉使用星形作为多边形但我不想将它错误地翻译成haskell。基本上,如果一个变量的类型为*而另一个变量具有*,则表示它们可以是任何类型,但它们必须是同一类型。如果有**,则意味着它可以但不需要与*具有相同的类型。我认为它对应于haskell usuage中的a,b,c等。

到目前为止我的工作

从长度的定义中你可以看到它找到了任何列表的长度,所以这给出了

length :: [*] -> num.

从定义我认为swap接受一个函数和两个参数并产生交换过的两个参数的函数,所以这给出了

swap :: (* -> ** -> ***) -> ** -> [*] -> ***

foldr将二进制函数(如加号)作为起始值和列表,并使用该函数从右向左折叠列表。这给了

foldr :: (* -> ** -> **) -> ** -> [*] -> **)

我知道在函数组合中它是正确的关联,所以例如第一个点(。)右边的所有内容都需要生成一个列表,因为它将作为第一个折叠器的参数给出。

foldr函数输出一个值(折叠列表的结果),所以我知道返回类型将是某种多义类型而不是多类型列表。

我的问题

我不确定从哪里开始真的。我可以看到swap需要接受另一个参数,所以这个部分应用程序是否意味着整个事情是一个函数?我很困惑!

2 个答案:

答案 0 :(得分:9)

你已经得到了答案,我会一步一步地写下推导,这样就可以很容易地看到所有这些:

xxf xs = foldr (+) 0 . foldr ((:) . length . flip (:) []) [] $ xs
       == sum $ foldr ((:) . length . (: [])) [] xs
       == sum $ foldr (\x -> (:) (length [x])) [] xs
       == sum $ foldr (\x r -> length [x]:r) [] xs
       == sum $ map   (\x -> length [x]) xs
       == sum [length [x] | x <- xs]  
       == sum [1 | x <- xs]
    -- == length xs
xxf :: (Num n) => [a] -> n

那么,在米兰达,xxf xs = #xs。我想它的类型是米兰达语法中的:: [*] -> num

Haskell的length:: [a] -> Int,但此处定义为:: (Num n) => [a] -> n,因为它使用Num的{​​{1}}和两个文字,(+)0

如果您在查看1时遇到问题,则只需

foldr

答案 1 :(得分:8)

让我们逐步完成。

length函数显然具有您描述的类型;在哈斯克尔,Num n => [a] -> n。等效的Haskell函数是length(它使用Int而不是任何Num n。)

swap函数接受一个函数来调用和反转它的前两个参数。你没有得到正确的签名;它是(a -> b -> c) -> b -> a -> c。等效的Haskell函数是flip

foldr函数具有您描述的类型;即(a -> b -> b) -> b -> [a] -> b。等效的Haskell函数是foldr

现在,让我们看一下主表达式中每个子表达式的含义。

表达式swap (:) []接受(:)函数并交换其参数。 (:)函数的类型为a -> [a] -> [a],因此swap ping它会产生[a] -> a -> [a];因此,整个表达式具有类型a -> [a],因为交换的函数应用于[]。结果函数的作用是它构造给定该项的一个项目的列表。

为简单起见,让我们将该部分提取到一个函数中:

singleton :: a -> [a]
singleton = swap (:) []

现在,下一个表达式为(:) . length . singleton(:)函数仍具有类型a -> [a] -> [a]; (.)函数的作用是它组成函数,因此如果你有一个函数foo :: a -> ...和一个函数bar :: b -> afoo . bar将具有类型b -> ...。表达式(:) . length因此具有类型Num n => [a] -> [n] -> [n](请记住,length返回Num),而表达式(:) . length . singleton的类型为Num => a -> [n] -> [n]。结果表达式的作用有点奇怪:给定a类型的任何值和一些列表,它将忽略a并将数字1添加到该列表中。

为简单起见,让我们从中做出一个功能:

constPrependOne :: Num n => a -> [n] -> [n]
constPrependOne = (:) . length . singleton

您应该已经熟悉foldr了。它使用函数在列表上执行右侧折叠。在这种情况下,它会在每个元素上调用constPrependOne,因此表达式foldr constPrependOne []只构造一个与输入列表长度相等的列表。因此,让我们做一个功能:

listOfOnesWithSameLength :: Num n => [a] -> [n]
listOfOnesWithSameLength = foldr constPrependOne []

如果您有一个列表[2, 4, 7, 2, 5],那么在应用[1, 1, 1, 1, 1]时您将获得listOfOnesWithSameLength

然后,foldr (+) 0功能是另一个右侧折叠。它相当于Haskell中的sum函数;它总结了列表的元素。

所以,让我们发挥作用:

sum :: Num n => [n] -> n
sum = foldr (+) 0

如果您现在撰写功能:

func = sum . listOfOnesWithSameLength

...你得到了结果表达式。给定一些列表,它创建一个等长的列表,只包含一个列表,然后对该列表的元素求和。换句话说,它的行为与length完全相同,只使用速度慢得多的算法。所以,最终的功能是:

inefficientLength :: Num n => [a] -> n
inefficientLength = sum . listOfOnesWithSameLength