Function 在Haskell中,两个相似的函数如何具有不同的多态类型?
我对Haskell很陌生,所以如果我缺少关键概念,请指出 假设我们有两个功能: 事实 |n==0=1 |n>0=n*(事实(n-1))Function 在Haskell中,两个相似的函数如何具有不同的多态类型?,function,haskell,types,polymorphism,fibonacci,Function,Haskell,Types,Polymorphism,Fibonacci,我对Haskell很陌生,所以如果我缺少关键概念,请指出 假设我们有两个功能: 事实 |n==0=1 |n>0=n*(事实(n-1)) fact的多态类型是(Eq t,Num t)=>t->t,因为在if条件中使用了n,n必须是有效类型才能执行=检查。因此t必须是Number并且t可以是类约束内的任何类型Eq t fib n |n==1=1 |n==2=1 |n>2=fib(n-1)+fib(n-2) 那么为什么fib的多态类型是(Eq a,Num a,Num t)=>a->t 我不明白,请
fact
的多态类型是(Eq t,Num t)=>t->t
,因为在if条件中使用了n
,n必须是有效类型才能执行=
检查。因此t
必须是Number
并且t
可以是类约束内的任何类型Eq t
fib n
|n==1=1
|n==2=1
|n>2=fib(n-1)+fib(n-2)
那么为什么fib
的多态类型是(Eq a,Num a,Num t)=>a->t
我不明白,请帮忙。区别在于在
事实中,您直接在算术表达式中使用参数,该表达式构成最终结果:
fact n | ... = n * ...
这样,如果写出展开的算术表达式,n
将出现在其中:
fact 3 ≡ n * (n-1) * (n-2) * 1
这修复了参数必须与结果具有相同类型的问题,因为
(*) :: Num n => n -> n -> n
在fib
中并非如此:此处实际结果仅由文字和子结果组成。瞧,扩展表达式看起来像
fib 3 ≡ (1 + 1) + 1
这里没有n
,所以参数和结果之间不需要统一
当然,在这两种情况下,您还使用了n
来确定这个算术表达式的外观,但为此,您只使用了与文本的相等比较,文本的类型与最终结果无关
请注意,您还可以为fib
提供一个类型保留签名:(Eq a,Num a,Num t)=>a->t
严格来说比(Eq t,Num t)=>t->t
更一般。相反,通过使用转换函数,您可以创建一个不要求输入和输出为同一类型的事实:
fact' :: (Eq a, Integral a, Num t) => a -> t
fact' = fromIntegral . fact
但这没有多大意义,因为Integer
几乎是唯一可以在fact
中可靠使用的类型,但要在上述版本中实现这一点,您需要从Integer
开始。因此,如果有任何问题,您应该执行以下操作:
fact'' :: (Eq t, Integral a, Num t) => a -> t
fact'' = fact . fromIntegral
然后还可以将其用作Int->Integer
,这有点合理
我建议只保留签名(Eq t,Num t)=>t->t
,并且只在实际需要的地方添加转换操作。或者说真的,我建议根本不要使用fact
——这是一个非常昂贵的函数,在实践中几乎没有什么用处;大多数以阶乘结束的应用程序实际上只需要这样的东西,而这些应用程序可以在没有阶乘的情况下更有效地实现。Haskell始终致力于派生最通用的类型签名
现在对于事实
,我们知道输出的类型应该与输入的类型相同:
fact n | n == 0 = 1
| n > 0 = n * (fact (n - 1))
这是由于最后一行。我们使用n*(事实(n-1))
。所以我们使用乘法(*)::a->a->a
。因此,乘法取相同类型的两个成员并返回该类型的一个成员。由于我们与n
相乘,并且输入n
,因此输出与输入的类型相同。由于我们使用n==0
,我们知道(==)::Eq a=>a->a->Bool
,这意味着该输入类型应该有Eq a=>
,而且0::Num a=>a
。因此,结果类型是fact::(numa,Eq a)=>a->a
现在对于fib
,我们看到:
fib n | n == 1 = 1
| n == 2 = 1
| n > 2 = fib (n - 1) + fib (n - 2)
现在我们知道,对于n
,类型约束再次是Eq a,Num a
,因为我们使用n==1
,和(==)::Eq a=>a->a->Bool
和1::Num a=>a
。但是,输入n
从未直接用于输出。实际上,最后一行有fib(n-1)+fib(n-2)
,但这里我们使用n-1
和n-2
作为新呼叫的输入。这意味着我们可以安全地确定输入类型和输出类型是独立工作的。输出类型仍然有一个类型约束:Num t
:这是因为对于前两种情况,我们返回1
,而1::Num t=>t
,并且我们还返回两个输出的加法:fib(n-1)+fib(n-2)
,所以同样(+)::Num t=>t->t->t
因为您没有在输出中使用输入参数。很抱歉,我没有完全理解您所说的@威廉·瓦农森