热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Haskell学习笔记:探索函数式编程之美

使用教材:《learnyouaHaskell》,中文1-8章戳我准备工作。编译器&调试器:安装HaskellPlatform,

使用教材:《learn you a Haskell》,中文1-8章戳我

准备工作。

编译器&调试器:安装Haskell Platform,也就是著名的GHC;

文本编辑器:

①安装Eclipse(最新的是4.2),安装EclipseFP,根据Extra configuration steps安装一些必要的插件;

②安装GVIM,调教一下_vimrc;

③安装leksah,虽然这是一个IDE,但是仍然需要和GHC配合工作;

④使用其他Editor,比如EmacsYi甚至notepad等;

其实像Haskell这种小众语言,还是用Vim或者Emacs更好一些,这里推荐一个已经调教好的VIM,里面已经修正了对haskell的一些支持,并且加入了补全系统。 不过如果写大的工程文件的话,个人感觉Eclipse更方便一些。

第一章     入门

Haskell的基本思想:高等抽象,不依赖于计算机模型。语法尽量接近数学上的表达式。

例如,在数学上一元函数表达式为,在haskell中对应的函数就是f x = x + x,除了将括号换成空格,其余都一样。二元函数也类似,对应的Haskell函数也就表示为 g x y = x*3 + y*5,我们也可以定义,在Haskell中对应的也就是:h x y = f x + g x y,很明显具有形式上的一致性。

条件函数:

        

条件函数对应Haskell中的if语句,与其他编程语言不通但是与数学上的函数相同的是:每个if必须对应一个else,不可以省略。对于上述函数,这里的表达式显然是:

  1. smallNumber x = if x>100
  2.     then x
  3.         else x*2

    在haskell中,如果一个函数没有参数,那么它是一个"定义",也就是其他语言中的"常量"(在ghci下使用关键字let来定义)。

    为了书写的方便,可以通过使用`操作符将前缀函数变成中缀函数,例如div x y=x/y,在实际调用时可以写成 x `div` y。

    另外需要注意的是,在Haskell中所有函数的首字母都不能大写。

List入门

    list(序列)与其他语言中的array(数组)是相对应的,虽然在STL中list表示的是"链表"。list的特点:单类型,这点和array显然是一致的,不同的是list可以嵌套list,而且这些list的长度可以不同(类型必须相同)。list的表达形式如:[1,2],在方括号中放入元素,以comma分隔。字符(串)字面值的表达方式与C一致。字符串就是字符的list,所有适用于list的操作都可以直接用在字符串上。

    list的特性比较像stack,可以使用 ++ 操作符进行pushback操作,但是haskell需要遍历整个list,效率很低。但是如果使用 : 操作符进行pushfront操作,速度就很快。需要注意的是++操作符的右表达式必须也为list,如果是单个元素,就放入[],而 : 操作符的左表达式必须为元素,如果需要插入多个元素,就按次序分开插入(:表达式的执行顺序是自右向左)。下标运算符!!,下标从0开始计算。list的比较方式同C中strcmp。

    与C语言中array最大的不同之处在于list是haskell原生支持类型,也就是可以直接以"字面值"存在的类型,而C中的array必须以variable形式存在。所以在haskell中可以直接使用函数操作list,常用的几个函数:

    head:返回头部;

    tail:返回去掉头部的其他部分(尾部);

    last:返回最后一个元素;

    init:返回除了最后一个元素的其他部分;

    length:返回长度;

    null:检测是否为空;

    reverse:反转序列;

    take n list:list中取前n个元素组成list,n>=0,如果n=0,得到一个空list;

    maximum/minimum:得到最大/小元素(list中的元素必然可以比较);

    sum:求和;

    elem x list:判断x是否存在于list中;

区间

    list的Range特性仍然是来源于数学,比如常数集{x},x取1到100,一般写作{x}={1,2,3..100},在list中也可以这么干,写出前两个元素确定step,两个点号表示range,最后是上/下界(由于计算机本身的限制,最好不要在浮点数中使用range特性)。如果不标上/下界的话,我们会得到一个无限长的list(显然这在数学上是合法的),haskell为了尽量与数学抽象一致,引入了"惰性求值"的特性,也就是说,haskell不会管这个无限长的list,只在你需要它的一部分时,再处理这个一部分。

生成list的一些函数:

    cycle list:生成重复的无限长序列;

    repeat n:生成仅含有n的无限长序列

    replicate n x:生成含有nx的序列

List comprehension

    list comprehension来源于数学中的集合表达式&#xff0c;如&#xff0c;在haskell中表示为[x*2 | x <- [1,2..10] ].如果有多个条件&#xff0c;就用comma隔开&#xff0c;表示取所有满足条件集合的交集。例如对应于[x*2 | x <- [1,2..10], x*2 >&#61;12].在 | 左边是取值&#xff0c;右边是filter的条件。<-表示"属于"&#xff0c;如果不关心取出的值&#xff0c;可以用_ <- []这种形式&#xff0c;可以省去一个变量名。对于嵌套的list类型&#xff0c;可以写出对应的嵌套list comprehension&#xff0c;用 | 隔开过滤条件。

元组

    Tuple&#xff08;元组&#xff09;与list的特性正好相反&#xff1a;每个tuple可以装不同类型的元素&#xff1b;两个tuple只有元素个数和对应次序的元素类型都相同时&#xff0c;才能视为同一类型。如果用C语言来比较的话&#xff0c;和一个defined的struct类型有行为上的一致性。如果要嵌套list和tuple&#xff0c;注意list要求类型的一致&#xff0c;而tuple类型一致的条件是很苛责的。tuple的表达行式是(x,y)&#xff0c;括号内至少有两个元素&#xff0c;因为有一个元素没有什么意义&#xff08;想一想只有一个元素的struct&#xff09;&#xff0c;空的tuple也就是()也算是一种类型。tuple不能增加或者减少元素&#xff0c;不同类型的tuple也不能相互比较。

    只有两个元素的tuple通常称为pair&#xff08;序对&#xff09;&#xff0c;和STL中的pair一致。pair常用的操作函数&#xff1a;

    fst&#xff1a;返回首项&#xff1b;

    snd&#xff1a;返回次项&#xff1b;

    zip list1 list2&#xff1a;将两个list中的元素进行交叉配对&#xff0c;返回一个由pair组成的list&#xff1b;如果两个list中的元素个数不一致&#xff0c;以较短的那个为界&#xff1b;

第二章 类型与类型类

    Haskell是静态类型语言&#xff0c;但是与C&#43;&#43;或者java不同的是&#xff0c;haskell有类似动态语言中的自动类型推导特性&#xff08;C&#43;&#43;11中引入auto关键字后也有了此特性&#xff09;。在ghci中可以使用:t 来解析表达式或者函数&#xff0c;返回形如 表达式::类型 的结构。其中 :: 符号表示类型约束&#xff0c;在声明函数时也可使用该符号来明确定义域和值域。

    如&#xff1a;addThree :: Int->Int->Int->Int

     addThree x y z&#61; x&#43;y&#43;z

    使用->符号分隔参数&#xff0c;返回值类型在最后&#xff08;显然每个函数只能返回一个值&#xff09;。常见类型包括&#xff1a;Char&#xff0c;Int&#xff0c;Integer&#xff08;前者有界&#xff0c;后者无界但是效率不如前者&#xff09;&#xff0c;Float&#xff0c;Double&#xff0c;Bool。所有类型的首字母大写。

    类型类&#xff0c;类型类的概念类似于java中的"接口"&#xff0c;它规定了满足一系列特性的类的集合。常见的类型类包括&#xff1a;Eq&#xff08;相等性&#xff09;&#xff0c;Ord&#xff08;可比较性&#xff0c;返回LT、GT、EQ等&#xff09;&#xff0c;Show&#xff08;可显示性&#xff0c;对应函数show&#xff09;&#xff0c;Read&#xff08;与Show相反&#xff0c;对应函数read可以将字符串转为需要的类型&#xff0c;形式是read x ::type或者让read根据所在的表达式自己推断需要的类型如 read "3.5"&#43;4&#xff09;&#xff0c;Enum&#xff08;可枚举性&#xff0c;即可以用succ和pred得到后继或者前驱值&#xff09;&#xff0c;Bounded&#xff08;有界性&#xff0c;可以使用minBound和maxBound函数获得上下界&#xff09;&#xff0c;Num&#xff08;数字类型&#xff09;&#xff0c;Integral&#xff08;整数类型&#xff09;&#xff0c;Floating&#xff08;浮点类型&#xff09;。

    使用&#61;>符号来声明类型类的约束&#xff0c;如fromIntegral :: (Integral a, Num b) &#61;> a -> b&#xff08;这个函数用于将整数类转换成更通用的Num类型&#xff09;

第三章 函数的语法

模式匹配

    模式匹配的本质就是一大串swith…case&#xff0c;或者说if…else if…这些&#xff0c;遇到第一个匹配值后就会跳出该函数&#xff08;有些类似C&#43;&#43;中try…catch的匹配过程&#xff09;。这里举著名的斐波那契数作为例子。我我们知道Fibonacci sequence的数学定义为

    那么对应的Haskell代码为

  1.     fibonacci :: ( Integral a) &#61;>a->a
  2.     fibonacci 0&#61;0
  3.     fibonacci 1&#61;1
  4.     fibonacci x&#61;fibonacci (x-1) &#43; fibonacci (x-2)

    显然这段代码与公式(2)具有形式上的一致性。注意这里没有约束x>&#61;0&#xff0c;所以匹配不是健全的。

    对list进行模式匹配&#xff0c;常见的方法是将list分解为(x:xs)&#xff0c;x匹配head&#xff0c;xs匹配tail&#xff0c;但是不能用&#43;&#43;进行分割&#xff0c;原因很简单&#xff0c;因为&#43;&#43;作用于list而不是元素。操作符&#64;匹配全体引用&#xff0c;例如all&#64;(x:xs)&#xff0c;就意味着可以用all来代替(x:xs)&#xff0c;这被称为as模式。

门卫

    门卫&#xff08;guard&#xff09;是与模式匹配配合使用的&#xff0c;它的作用是增加过滤条件。对于区间匹配的条件函数&#xff0c;使用门卫是很简洁而恰当的。Guard的语法&#xff1a;在函数和&#61;之间插入 | guard。

Where

    关键字where用于在函数中进行变量或者函数的声明&#xff0c;一般放置在guard函数之后。在where函数中声明的辅助函数中可以嵌套使用where&#xff0c;这样就可以把一个复杂的表达式通过换元表达为几个符合的简单函数式。

    完善上面的fibonacci函数&#xff0c;将第4行改为

  1.     fibonacci x
  2.     | x<0    &#61; -1
  3.     | otherwise &#61; fibonacci (x-1) &#43; fibonacci (x-2)

    当然这里让x<0的时候返回一个负值并不是很恰当&#xff0c;不过这样不会有异常抛出了。

今天的复习就到这里&#xff1a;&#xff09;

转:https://www.cnblogs.com/livewithnorest/archive/2012/07/15/2592789.html



推荐阅读
  • 数据库内核开发入门 | 搭建研发环境的初步指南
    本课程将带你从零开始,逐步掌握数据库内核开发的基础知识和实践技能,重点介绍如何搭建OceanBase的开发环境。 ... [详细]
  • 解读MySQL查询执行计划的详细指南
    本文旨在帮助开发者和数据库管理员深入了解如何解读MySQL查询执行计划。通过详细的解析,您将掌握优化查询性能的关键技巧,了解各种访问类型和额外信息的含义。 ... [详细]
  • 在 openSUSE Tumbleweed 系统上搭建 51 单片机开发环境并进行编程实践。首先,通过 `sudo zypper in emacs` 命令安装文本编辑器 Emacs。接着,使用 `sudo zypper in sdcc` 安装 SDCC 编译器。最后,利用 `wget` 下载 sdcflash Python 脚本,以便于单片机的烧录和调试。此外,还介绍了如何配置开发环境,确保各组件协同工作,提高开发效率。 ... [详细]
  • Python数据分析入门指南:全面了解Python在数据科学中的应用 ... [详细]
  • 本文详细介绍了如何准备和安装 Eclipse 开发环境及其相关插件,包括 JDK、Tomcat、Struts 等组件的安装步骤及配置方法。 ... [详细]
  • 创建项目:Visual Studio Online 入门指南
    本文介绍如何使用微软的 Visual Studio Online(VSO)创建和管理开发项目。作为一款基于云计算的开发平台,VSO 提供了丰富的工具和服务,简化了项目的配置和部署流程。 ... [详细]
  • Struts与Spring框架的集成指南
    本文详细介绍了如何将Struts和Spring两个流行的Java Web开发框架进行整合,涵盖从环境配置到代码实现的具体步骤。 ... [详细]
  • 在成功安装和测试MySQL及Apache之后,接下来的步骤是安装PHP。为了确保安全性和配置的一致性,建议在安装PHP前先停止MySQL和Apache服务,并将MySQL集成到PHP中。 ... [详细]
  • 本文作者分享了在阿里巴巴获得实习offer的经历,包括五轮面试的详细内容和经验总结。其中四轮为技术面试,一轮为HR面试,涵盖了大量的Java技术和项目实践经验。 ... [详细]
  • 嵌入式开发环境搭建与文件传输指南
    本文详细介绍了如何为嵌入式应用开发搭建必要的软硬件环境,并提供了通过串口和网线两种方式将文件传输到开发板的具体步骤。适合Linux开发初学者参考。 ... [详细]
  • NFS(Network File System)即网络文件系统,是一种分布式文件系统协议,主要用于Unix和类Unix系统之间的文件共享。本文详细介绍NFS的配置文件/etc/exports和相关服务配置,帮助读者理解如何在Linux环境中配置NFS客户端。 ... [详细]
  • Spring Cloud因其强大的功能和灵活性,被誉为开发分布式系统的‘一站式’解决方案。它不仅简化了分布式系统中的常见模式实现,还被广泛应用于企业级生产环境中。本书内容详实,覆盖了从微服务基础到Spring Cloud的高级应用,适合各层次的开发者。 ... [详细]
  • Java EE CDI:解决依赖关系冲突的实例
    在本教程中,我们将探讨如何在Java EE的CDI(上下文和依赖注入)框架中有效解决依赖关系的冲突问题。通过学习如何使用限定符,您将能够为应用程序的不同客户端提供多种接口实现,并确保每个客户端都能正确调用其所需的实现。 ... [详细]
  • 数字图书馆近期展出了一批精选的Linux经典著作,这些书籍虽然部分较为陈旧,但依然具有重要的参考价值。如需转载相关内容,请务必注明来源:小文论坛(http://www.xiaowenbbs.com)。 ... [详细]
  • 七款高效编辑器与笔记工具推荐:KindEditor自动换行功能解析
    本文推荐了七款高效的编辑器与笔记工具,并详细解析了KindEditor的自动换行功能。其中,轻笔记QingBiJi是一款完全免费的记事本软件,用户可以通过其简洁的界面和强大的功能轻松记录和管理日常事务。此外,该软件还支持多平台同步,确保用户在不同设备间无缝切换。 ... [详细]
author-avatar
zhaoyunnidaye_260
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有