Haskell中的类型

Lin*_*hen 9 haskell types type-inference inferred-type

我是Haskell的新手,我很难理解推断类型和类似的工作方式.

map :: (a -> b) -> [a] -> [b]
(.) :: (a -> b) -> (c -> a) -> c -> b
Run Code Online (Sandbox Code Playgroud)

这到底是什么意思呢?

foldr :: (a -> b -> b) -> b -> [a] -> b
foldl :: (a -> b -> a) -> a -> [b] -> a
foldl1 :: (a -> a -> a) -> [a] -> a
Run Code Online (Sandbox Code Playgroud)

这些有什么区别?

和推断的类型

foldr map是[a] - > [a - > a] - > [a]

但为什么会这样呢?

谢谢!

Luk*_*keN 10

如果你举个例子

map :: (a -> b) -> [a] -> [b]
Run Code Online (Sandbox Code Playgroud)

这意味着map需要2个参数

  1. 从类型a到类型b的函数
  2. 类型列表a

它回来了

  1. b的清单

你可以在这里看到这种模式,但是当我们用'a'和'b'代替它时会更清楚

  • a =字符串
  • b = Int

所以这种类型的定义将是

map :: (String -> Int) -> [String] -> [Int]
Run Code Online (Sandbox Code Playgroud)

所以现在它是一个函数,它接受一个String并返回Int和一个字符串列表并返回一个Ints列表.

假设我们的函数接受String并返回和Int read.read使用你给它的字符串将它转换为不同的东西.因为我们将它放入Int上下文中,所以它会将字符串转换为int

map read ["1", "2", "112", 333"]
Run Code Online (Sandbox Code Playgroud)

这将导致

[1, 2, 112, 333]
Run Code Online (Sandbox Code Playgroud)

因为map你的函数read映射(应用)它到列表的每个元素.你没有必要告诉read它的论点read "1",或者read n,因为map为你照顾它.

这就是它的全部内容.函数的类型仅说明它采用的类型以及返回的类型.当然,也有讨论,但你有意或无意见到!它基本上意味着,一个函数不需要很多参数,而只需要一个参数.说你采取这个功能+.如果你评估1+2,它返回一个函数,它接受另一个被添加的数字1,因为这里有另一个数字2,它将使用它.您可以将其评估为(1+)并将其传递,可能会在一段时间后添加该数字.当你没有中缀语法时,它会更清楚+.它本来可以编写(+) 1 2,所以首先这个语句变成(+) 1了类型(简化了!我不会在这里介绍Num类型类)Int -> Int.因此(+) 1(或者(1+)就此而言)只是可以应用值的另一个函数,此时结果将能够计算为3.

以下是这在实践中的表现:(忽略(Num a) =>这里的部分,如果它让你感到困惑,这是一个你稍后会了解更多的概念.如果你愿意,只需要替换a这里的类型Int,然后(Num a) =>完全忽略这部分.)

 (+)  :: (Num a) => a -> a -> a
 (+2) :: (Num a) => a -> a
(1+)  :: (Num a) => a -> a
(1+2) :: (Num a) => a

Prelude> (+2) 5
7
Prelude> map (+3) [1,2,3]
[4,5,6]
Run Code Online (Sandbox Code Playgroud)

对于您的第二个问题:您没有定义推断类型AT ALL.它们被称为"推断",因为编译器/解释器"推断"(读取:计算)类型本身,而无需您明确命名它们.

关于foldX差异:它们都完全相同:将列表缩减为单个值.功能之间的区别仅仅是内部定义.foldl从左侧折叠列表,并foldr向右折叠.

所以总结一个列表,你可以像这样使用所有这些......

foldl1 (+) [1,2,3] == 6
foldr (+) 0 [1,2,3] == 6
foldl (+) 0 [1,2,3] == 6
Run Code Online (Sandbox Code Playgroud)

你看,除了foldl1,你提供了一个折叠函数,一个起始值(累加器)和一个折叠列表.fold1拥有它自己的累加器,你不给它自己的.

在实践中,你应该更好地使用foldr,因为fold它不适合BIG列表而不会因堆栈溢出而崩溃(tee,hee).这个规则的例外是foldl'(注意"'"!)Data.Map- 它是一个严格的折叠,也适用于大型列表.

如果您想自己为函数提供类型(如果您编写它们),请考虑以下示例:

double :: Int -> Int
double n = 2 * n
Run Code Online (Sandbox Code Playgroud)

或者以一种轻快的方式

double :: Int -> Int
double = (*2)
Run Code Online (Sandbox Code Playgroud)

两个例子(double :: Int -> Int)的第一行是你正在寻找的.这是你可以强制编译器或解释器识别函数的方法 - 你可以省略它,然后编译器/解释器就会找到它们(有时候甚至比你想象的更好)