⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 haskell教程.txt

📁 Haskell教程(by rufi) 传统的Basic,Pascal,C++,C#,Java,Python等都是命令(imperative)编程语言, 程序语句有一定的执行次序. 函数(functi
💻 TXT
📖 第 1 页 / 共 3 页
字号:
                                       
Haskell教程 
by rufi  2003.3.21 -- 2003.4.2 

一.序 

1.什么是Haskell? 

     Haskell是一种函数编程语言. 1980年代以前对函数编程有很多研究, 但不同的研究者使用各自不同的语法记号, 一起交流时造成一些不便. 后来1987年的时候, 在FPCA'87会议上制定了统一的Haskell语言. Haskell吸收了各家的长处, 是一种纯粹的函数编程语言,并根据科学家Haskell B.Curry的名字命名. Haskell经过多年的发展完善, 目前使用的版本是Haskell 98. 
      
2.Haskell有什么特点? 

    相对Haskell来说,传统的Basic,Pascal,C++,C#,Java,Python等都是命令(imperative)编程语言, 程序语句有一定的执行次序. 函数(functional)编程语言则给出执行的内容, 关注于更高层次的"做什么"而不是"怎么做", 这就是二者最明显的一个区别。函数编程语言的语法功能非常强,使编程的效率大幅提高。 
    Haskell是世界上公认的语法最优美最简洁的一种语言。的确,Haskell语言是写给人看的,而不是写给机器看的。另一方面,这也使得的 Haskell的编译技术成为一个难点。从以人为本的角度来看,程序员的时间比机器的时间更宝贵,所以Haskell是明智的选择。 

3.如何获得Haskell? 

     Haskell是一个公共的语言定义, 任何人都可以编写它的实现(implementation), 因而Haskell有很多解释器(比如Hugs)和编译器(比如GHC), 它们都可以在www.haskell.org上得到. 解释器的优点是便于学习和开发,程序写好后不需要编译直接就可以运行,编译器则可以将程序编译可独立执行的文件,便于发布. Haskell既能解释执行, 也能槐槐嘁? 这也是优于其他语言的一个地方. 

附:Hugs使用指南 

    本文中的示例程序都将在Hugs中运行, 在这里简要介绍一下Hugs的使用方法。Hugs可以在http://www.haskell.org/hugs/下载,安装文件只有2.8M, 是学Haskell的必备工具. 

使用方法: 

1.用你自己喜欢的文本编辑器将源程序写好, 保存到文件中, 文件以扩展名 hs 结尾. 

2.运行Hugs, 出现提示符: Prelude> ,表示Prelude库已经装载. 

3.输入:? 可以查看可供使用的一些命令的说明 

4. 先输入:!, 然后就可以输入DOS命令并执行. 比如输入:!dir查看当前的工作目录 

5. 输入:cd directory_name 将工作目录改为你保存源文件的目录 

6. 输入:l file_name 将源程序载入, 提示符变为Main> 

现在就可以在提示符后输入各种表达式以检验所定义的函数的功能, 执行所需的运算. 

注意: 在提示符后不可以定义新的函数, 因为Haskell中各语句不是顺序执行的, 而把整个源文件当作一个整体来处理, 在编辑器中修改源程序并保存后, 只要输入:r就重新载入, 改动就生效了. 


二.语法概要 

1.注释有两种: 一种以"--"开始到行尾结束, 一种以"{-"开始,以"-}"结束,可以延续多行. 

2.表达式和函数都有类型,但类型声明不是必需的,有一套类型推断系统可以推断出所涉及的类型. 

3.函数的定义多使用模式(pattern)匹配的方法, 两个标识符(identifier)邻接就表示函数调用. 

4.函数变量和类型变量的标识符以小写字母开头, 类型构造和模块的标识符以大写字母开头. 

5.语句块以缩进区分, 从同一列开始的语句属于同一个块. 

6.保留字: case class data default deriving do else if import in infix infixl infixr instance let module newtype of then type where _ 

7.运算符可以自定义,由以下符号进行组合: 
       : # $ % & * + - = . / \ < > ? ! @ ^ | 
 预定义的运算符有: 
       +  -  *  /  ^  $  ++  .  &&  ||  ==  /= <=  >=  <  > 
       : //  =  @  ->  =>  ..  ::  <-  !! 
        
8.数字: 123  12.3  1.23e5  0o67  0x3A 

 字符: 'a' 'A' '\n' 
  
 字符串: "character string" 
  
  
三.常数 

    在函数编程语言中, 函数是一个中心概念, 常数也可看作是常函数. 如下语句: 
     
     f = 3 
      
    定义了一个常数. f 被定义为3后就不能重复定义 f=5 ,任何时候调用f, 它都返回3. 这就消除了一些边际效应,减少了出错的可能性. 以下代码在函数编程和命令编程中具有完全不同的含义: 
     
    a = 2 
    b = 4 
    c = a + b 
     
    在函数编程中解释为: 定义常数a为2, 定义b为4, 定义c为a,b之和.   
    在命令编程中解释为: 给变量a赋值2, 给b赋值4, 求a,b之和赋给c. 
     
    定义和赋值的区别在于, 定义不分次序, 赋值有次序, 以上程序在Haskell中完全可以倒过来写: 
     
    c = a + b 
    a = 2 
    b = 4 
     
    另外, 定义并不计算, 比如: 
     
    d = 3*5 
    e = 1/0 
     
    在命令程序中, e=1/0会引发一个错误, 在Haskell中只有当计算e时才引发错误. 
     
    Haskell的思维更像是人脑的思维而不是机器的思维. 
     
    也可以给常数加以类型说明, 比如: 
     
    b :: Int 
     
    如果没有这一类型说明, 系统自动根据 b=4 推断b的类型为: Integer 
     
    Int和Integer区别是Int的取值范围是有限的, Integer的大小是无限的. 因为Integer比Int更普遍,所以在没有明显说明b的类型为Int时, 就自动推断b的类型为: Integer 
     
    再举几个例子,在Haskell标准库Prelude中定义的常数中,有两个是: 
     
    pi = 4 * atan 1 
    otherwise = True 
     
     
四.单变量函数 
     
    如下语句: 
     
    f     (x)=x+2 
    double(x)=2*x 
     
    就定义了两个简单的函数, 函数的类型由自变量的类型和返回值在类型决定, 用运算符"->"连接. 
     
    比如可以用以下语句说明它们的类型: 
     
    f      :: Int -> Int 
    double :: Float -> Float 
     
    表示f是从Int变量到Int变量的映射. 如果没有明显说明, 系统根据函数定义中所涉及到的运算(+),(*)推断这两个函数的类型为:     
     
    Num a => a -> a 
   
    这里a是一个类型变量, 属于一个独立的名字空间, Num是一个类, Num a => 表示类型a属于Num类. Num类中定义了(+)和(*)的运算, 继承此类的类型也支持这两种运算. 这样使用类来限定函数的类型, 使函数具有的普遍性. 把类型归为一些类, 实现了类型的多态, 也简化了编程的任务. 
     
    在Haskell中函数非常频繁地用到, 通常在函数的定义和使用中省去括号, 直接用两个标识符邻接表示函数作用. 所以f和double的定义可写为: 
     
    f x      = x + 2 
    double x = 2 * x 
     
    调用函数的格式和定义函数的格式基本是相同的, 比如定义函数g如下: 
     
    g x = 2 * f x 
     
    函数作用的优先极高于其他所有的运算符, 所以2 * f x等价于2 * (f x), 也等价于2 * (f(x)). 
     
    函数作用的次序是从左向右的, 所以可以等价地定义g为: 
     
    g x = double (f x) 
     
    Prelude有一个运算符$的定义如下: 
    infixr 0  $ 
    ($)            :: (a -> b) -> a -> b 
    f $ x           = f x 
     
    可见, $也是表示函数作用的, 但它的优先级最低, 而且作用次序是从右向左的.所以还可以等价地定义g为: 
     
    g x = double $ f x 
     
    引入$, 避免了括号的使用, 尤其当$后的表达式很长有多行时使用$是很方便的. 
     
     
五. 多变量函数 


  严格说来, 一个函数只能接收一个参数, 返回一个值. 但有两种方法可以实现多变量函数. 
   
  1.Curried函数 
   
  函数接受一个参数后, 返回的也是一个函数, 这个函数对可以接受别的参数. 比如: 
   
  add :: Int -> Int -> Int 
  add x y = x + y 
   
  从add类型可以看出, 有两个"->", 而"->"的结合次序是从右向左, 所以add的类型是: 
   
  Int -> ( Int -> Int ) 
   
  即add接受一个Int参数, 返回一个( Int -> Int )的函数, 这个函数再接受一个Int返回一个Int. 
   
  这样的函数就叫curried函数. 
   
  2.使用数组 
   
  数组就是把几个变量放到一起看成是一个变量, 从而使函数可以输入和输出多个变量. 比如: 
   
  add :: (Int,Int) -> Int 
  add (x,y) = x+y 
   
  数组是Haskell中的一种数据结构, 数组中可以放不同类型的变量, 数目不限但长度固定, 数组的类型就是数组内各元素的类型组合起来构成一种类型. 
   
  这两种函数在使用中各有特色, 而且可以用Prelude中定义的curry和uncurry互相转换. 
   
  curry          :: ((a,b) -> c) -> (a -> b -> c) 
  curry f x y     = f (x,y) 

  uncurry        :: (a -> b -> c) -> ((a,b) -> c) 
  uncurry f p     = f (fst p) (snd p) 
   
  稍作一点解释:类型说明中出现的小写a,b,c等叫类型变量, 表示任意一种类型. f (x,y)表示f接受数组为参数, curry f x y就是(curry f) x y 表示curry f可以接受两个变量为参数. 令 g=curry f, 则 g x y = f (x,y). 可见curry的转换作用, curry的类型表达更清楚和说明了这一点. uncurry也是一样的道理, 其中的fst和snd分别表示取二元数组的第一个和第二个元素.定义如下: 
   
   fst            :: (a,b) -> a 
   fst (x,_)       = x 
    
   snd            :: (a,b) -> b 
   snd (_,y)       = y 
    
   "_"叫匿名变量, 匹配指定类型任意的输入值, 该值在"="后的表达式中不会用到. 
   
     
六. 离散函数 
   
   有些函数的参变量只有有限个取值, 比如Prelude中not的定义如下: 
    
   not         :: Bool -> Bool 
   not True     = False 
   not False    = True 
    
   not对逻辑变量取反. 

   离散的变量可以使用保留字data定义, 比如: 
    
   data Direction = Left|Up|Right|Down 
    
   这就定义了一种Direction类型的变量, 这种变量的取值只有四个值:Left,Up,Right,Down. 
    
   data定义了一个新的类型, type则可以给一个已有的类型取一个别名.比如: 
    
   type Point = (Float, Float) 
    
   (Float, Float)是容纳两个Float变量的数组的类型, 取别名后既简化了书写, 也附加了含义. 
    
   现在看针对离散变量的函数如何定义: 
    
   move :: Direction -> Point -> Point 
   move Left  (x,y) = (x-1,y)         
   move Up    (x,y) = (x,y-1) 
   move Right (x,y) = (x+1,y) 
   move Down  (x,y) = (x,y+1) 
    
   即分别对应离散变量的每一个取值对函数作相应的定义. 以(x,y)表示位置, 给move输入移动的方向和当前的位置, 就输出了移动后的位置. 
    
   再看一个例子: 
    
   data Thing = Paper | Stone | Scissors   deriving Show 

   beat :: Thing -> Thing 
   beat Paper    = Scissors 
   beat Stone    = Paper 
   beat Scissors = Stone 
    
   定义Thing类型有三个取值, deriving Show表示Thing类型继承Show类, 从而拥有的Show的method, Show类的作用是将取值转化为字符串在屏幕上显示. beat定义了三种物品,纸,石头,剪刀之间的输赢关系. 
  

   自然数是离散的也是无限的, 以自然数为变量的函数通常用迭代的方法定义, 即函数自己调用自己.举个例子: 
    
   fac 0 = 1 
   fac n = n * fac (n-1) 
    
   这样计算fac n时, 先去计算fac (n-1),有fac n = n * fac (n-1)=n * (n-1) * fac (n-2),如此类推, 一直算到fac 0, 最后的结果是把从1到n的自然数全部连乘起来. 
    
   再比如:      
   increment,fib :: Int -> Int 
    
   increment x=x+1 
    
   fib 1 = 1 
   fib 2 = 2 
   fib n = fib (n-1) + fib (n-2) 
    
   当计算函数时, 按照输入的参数逐一匹配所给的模式, 如果无法匹配时给出错误中断. 对于自然数模式匹配还允许用如下方式定义函数: 
   foo (n+1) = n-1 
    
   Haskell中预定义的针对字符的函数有: 
   isAscii, isLatin1, isControl, isPrint, isSpace, isUpper, isLower, 
   isAlpha, isDigit, isOctDigit, isHexDigit, isAlphaNum, 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -