当前位置:编程学习 > 网站相关 >>

Haskell是个神码语言

Haskell语言是一种函数式编程语言,而且是具有lazy evaluation(懒求值)特性的函数式编程语言。一开始看见函数式编程概念,感觉很高深,很computer science,很有计算机科学范儿,但是,学了一点之后,才发现它偏数学味儿,不是十分贴近computer science,特别对于时间、空间复杂度一开始都是隐藏着的,不作为主要的特性传达给读者的。
 
我用两本Haskell书来入门,但到目前为止,只能算是入了扇大门,能编些简单的函数而已。这两本书分别是Real World Haskell和Learn You A Haskell For Great Good。前面这本书我读完了前3章,读到第4章半当中的时候实在读不下去了。后面这本书则目前看到Recursion这块,刚讲了快排算法。
 
Haskell语言的语法里面,函数调用是比较简单易懂的。比如,div 3 2就是对3作除以2的整数除法。第一个div就是函数,后面跟着的都是参数。参数可以用括号括起来,这样就能表达较为复杂的情况,例如嵌套的函数调用:div (add 1 2) 2。
 
然后复杂的一点的是类型,类型除了对象或数据本身的类型之外,还有type class(类型类属)。比如说,Int类型是整数本身的类型,而Ord(可作大小比较)则是类型类属(type class),类似于C#语言中的接口概念。一个Int一定是一个Ord,一个Ord未必是一个Int。
 
还有一种东西叫类型变量,其实就是把本来用于数据的变量概念用在类型上面。
 
为子把上面讲的三个概念放在一起看,我们看一下下面这个函数(效率并不高,时间复杂度O(n ** 2),只是作个例子):
 
[plain] 
lastSeveral :: Int -> [a] -> [a]  
lastSeveral n [] = []  
lastSeveral n (x:xs)  
    | n < length (x:xs) = lastSeveral n xs  
    | otherwise         = x:(lastSeveral n xs)  
  
> lastSeveral 1 []  
[]  
> lastSeveral 1 "abc"  
"c"  
> lastSeveral 2 "abc"  
"bc"  
 
其中,lastSeveral是个函数,它的输入是一个整数Int和一个列表[a],其元素类型用类型变量a表示,但是我们不限定a到底属于哪些type class,所以任意类型的列表都能支持。如果换成这样:lastSeveral :: (Ord a) => Int -> [a] -> [a],那么a就必须是一个拥有Ord特性的类型。它的输出是另一个列表,其中的类型也还是a,也就是说,输入的如果是个整数列表,输出的也还得是个整数列表。
 
类型声明的下面,我们定义了lastSeveral的实现。具体实现这里谈起来有些长,至于时间复杂度的问题也是因为Haskell列表的实现特性所决定的(是个单链表),所以不详谈。相信看了书以后就能理解它的实现,而做了实验以后就能大致了解它的时间复杂度。
 
有趣的是,Haskell实现某些算法显得特别简单明了:
[plain] 
quicksort :: (Ord a) => [a] -> [a]  
quicksort [] = []  
quicksort (x:xs) =  
    let smallerSorted = quicksort [a | a <- xs, a <= x]  
        biggerSorted = quicksort [a | a <- xs, a > x]  
    in  smallerSorted ++ [x] ++ biggerSorted  
 
一个快速排序能只用数行代码就写出来。但是,反过来,有些在常见的命令式(imperative)编程语言(也就是绝大多数编程语言,包括C#、C/C++、Java、Perl、Python、PHP、Ruby、VB、VB.Net、JavaScript、ABAP等等)中,一些简单的程序用Haskell写出来却很晦涩。例如将一个数组中的元素全部随机排列,或者叫“洗牌”[shuffle],是这样的:
 
[plain] 
import Control.Monad  
import Control.Monad.ST  
import Control.Monad.Random  
import System.Random  
import Data.Array.ST  
import GHC.Arr  
  
shuffle :: RandomGen g => [a] -> Rand g [a]  
shuffle xs = do  
    let l = length xs  
    rands <- take l `fmap` getRandomRs (0, l-1)  
    let ar = runSTArray $ do  
        ar <- thawSTArray $ listArray (0, l-1) xs  
        forM_ (zip [0..(l-1)] rands) $ \(i, j) -> do  
            vi <- readSTArray ar i  
            vj <- readSTArray ar j  
            writeSTArray ar j vi  
            writeSTArray ar i vj  
        return ar  
    return (elems ar)  
  
*Main> evalRandIO (shuffle [1..10])  
[6,5,1,7,10,4,9,2,8,3]  www.zzzyk.com
  利用了一些库中的机制来实现shuffle,包括把xs这个链表转换成数组listArray,然后用thawSTArray、readSTArray和writeSTArray来操作数组中的数据。这个过程看上去类似于命令式编程语言而不像函数式编程。简洁性也不是特别好。
 
总之,感觉并不是每个函数式编程的程序都容易理解,也不是每个都很简洁。我目前认为,函数式编程可能对某些程序来说是可以写得比命令式编程更偷懒,但不是所有程序都行。作为程序员来说,还是在恰当的场合下,把函数式编程的思想用在命令式编程语言中,才会产生较好的效果。需要注意的是Haskell的递归嵌套层数几乎没有限制,但大多数编程语言是有限制的,因此还是不能完全照搬套用。
补充:综合编程 , 其他综合 ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,