很多朋友和同事问我为什么谈论Haskell。在我学习Haskell之前,我总是使用Java,C和C ++等主流语言 - 并且仍然喜欢它们。那么一个命令式的开发人员怎么会变成Haskell粉丝?在本文中,我想解释一下 - 特别是对于函数编程经验较少的开发人员。
控制流
控制流程描述了如何告诉程序要做什么 - 制定算法。有三个基本控制元素:
面向对象编程:
这是一个使用Java的简单示例,它以文本为中心。文本作为字符串数组传递。每一行都是该数组的一个元素:
<b>void</b> alignCenter(String[] text) { <b>int</b> maxLength = 0; <b>for</b> (String line : text) { <b>if</b> (line.length() > maxLength) { maxLength = line.length(); } } <b>for</b> (<b>int</b> i = 0; i < text.length; ++i) { <b>int</b> spaceCount = (maxLength - text[i].length()) / 2; StringBuilder builder = <b>new</b> StringBuilder(); <b>for</b> (<b>int</b> j = 0; j < spaceCount; ++j) { builder.append(' '); } builder.append(text[i]); text[i] = builder.toString(); } }
函数式编程
下面是Haskell中的相同示例,它显示了模式匹配和递归的用法:
alignCenter :: [String] -> [String] alignCenter xs = alignCenter' maxLength xs where maxLength = maximum (map length xs) alignCenter' :: Int -> [String] -> [String] alignCenter' _ [] = [] alignCenter' n (x:xs) = (replicate spaceCount ' ' ++ x) : alignCenter' n xs where spaceCount = div (n - length x) 2
下面是一个简短的版本,通过使用map和lambda函数避免递归:
alignCenter :: [String] -> [String] alignCenter xs = map (/x -> replicate (div (n - length x) 2) ' ' ++ x) xs where n = maximum (map length xs)
函数的第一行是函数签名:
alignCenter :: [String] -> [String]
这告诉我们,我们有一个名为alignCenter的函数,它将一个字符串列表作为输入,并返回一个新的字符串列表作为输出(从左到右理解)。
然后第二行代码中是函数体的内容,第一个函数确定字符串列表中的最长行并调用第二个函数。我们通过一个简单的表达式maximum (map length xs)终止了我们的oop代码的第一个循环。
那么它是怎样工作的?我们来看看所有相关maximum (map length xs)函数的签名:
length :: [a] -> Int map :: (a -> b) -> [a] -> [b] maximum :: [a] -> a
length函数获取任何类型的列表并返回一个Int。所有小写类型的类型签名是类型变量,类似于在List<T>Java中的类型T。
map函数需要获取两个输入参数:第一个是类型a -> b;第二个获取[a]并返回 。
那么它是什么意思“它需要一个函数作为参数”?对,是真的!您可以将函数作为参数传递,既不像C中那样的函数指针也不像Java中那样引用方法引用 - 实际函数作为第一类值。
将函数作为参数使用或将新函数作为结果返回的函数称为高阶函数。那么这个功能有什么作用呢?它将传递[a]中每个元素给a -> b函数,在这个函数中,将a转为b,并且收集collect形成一个新的list
现在让我们解决类型变量map length xs,其中xs的类型为[String]:
map :: (String -> Int) -> [String] -> [Int]
您需要知道这String是一个类型的同义词[Char],表示字符列表。这就是为什么它与length函数兼容的原因。表达式map length ["Hello", "World!"]将解析为[5, 6]。(计算这两个字符串的长度),我们感兴趣的是列表里面最长的字符串的长度,所以我们传递给结果列表maximum返回列表,这是最大的数字6。
更多介绍点击标题见原文。