Haskell/Miranda:找到函数的类型

use*_*210 7 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
Run Code Online (Sandbox Code Playgroud)

注意:

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

我的工作到目前为止

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

length :: [*] -> num.
Run Code Online (Sandbox Code Playgroud)

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

swap :: (* -> ** -> ***) -> ** -> [*] -> ***
Run Code Online (Sandbox Code Playgroud)

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

foldr :: (* -> ** -> **) -> ** -> [*] -> **)
Run Code Online (Sandbox Code Playgroud)

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

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

我的问题

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

Wil*_*ess 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
Run Code Online (Sandbox Code Playgroud)

所以,在米兰达,xxf xs = #xs.我猜它的类型是:: [*] -> numMiranda语法.

Haskell length:: [a] -> Int在这里定义的,:: (Num n) => [a] -> n因为它使用Num的是(+)两个文字,0而且1.

如果你在可视化方面遇到困难foldr,那就简单了

foldr (+) 0 (a:(b:(c:(d:(e:(...:(z:[])...))))))
          == a+(b+(c+(d+(e+(...+(z+ 0)...)))))
          == sum [a,b,c,d,e,...,z]
Run Code Online (Sandbox Code Playgroud)


dfl*_*str 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平它产生[a] -> a -> [a]; 因此,整个表达式具有类型,a -> [a]因为应用了交换函数[].结果函数的作用是它构造给定该项的一个项的列表.

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

singleton :: a -> [a]
singleton = swap (:) []
Run Code Online (Sandbox Code Playgroud)

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

为简单起见,让我们做一个函数:

constPrependOne :: Num n => a -> [n] -> [n]
constPrependOne = (:) . length . singleton
Run Code Online (Sandbox Code Playgroud)

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

listOfOnesWithSameLength :: Num n => [a] -> [n]
listOfOnesWithSameLength = foldr constPrependOne []
Run Code Online (Sandbox Code Playgroud)

如果你有一个清单[2, 4, 7, 2, 5],你会[1, 1, 1, 1, 1]在申请时获得listOfOnesWithSameLength.

然后,该foldr (+) 0功能是另一个右折.它等同sum于Haskell中的函数; 它总结了列表的元素.

那么,让我们做一个函数:

sum :: Num n => [n] -> n
sum = foldr (+) 0
Run Code Online (Sandbox Code Playgroud)

如果你现在组成函数:

func = sum . listOfOnesWithSameLength
Run Code Online (Sandbox Code Playgroud)

...你得到了结果表达式.给定一些列表,它创建一个等长的列表,只包含一个列表,然后对该列表的元素求和.换句话说,它表现得非常像length,只使用更慢的算法.所以,最终的功能是:

inefficientLength :: Num n => [a] -> n
inefficientLength = sum . listOfOnesWithSameLength
Run Code Online (Sandbox Code Playgroud)