热门标签 | 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



推荐阅读
  • 深入探讨前端代码优化策略
    本文深入讨论了前端开发中代码优化的关键技术,包括JavaScript、HTML和CSS的优化方法,旨在提升网页加载速度和用户体验。 ... [详细]
  • spring boot使用jetty无法启动 ... [详细]
  • 在Linux系统中,许多应用程序以源代码的形式提供,这给安装带来了挑战。本文旨在介绍一种简化源码软件安装流程的方法,帮助用户更加轻松地完成安装。 ... [详细]
  • 本文档详细介绍了软通动力Java开发工程师职位的笔试题目,涵盖了Java基础、集合框架、JDBC、JSP等内容,并提供了详细的答案解析。 ... [详细]
  • 本文探讨了在Windows系统中运行Apache服务器时频繁出现崩溃的问题,并提供了多种可能的解决方案和建议。错误日志显示多个子进程因达到最大请求限制而退出。 ... [详细]
  • c语言二元插值,二维线性插值c语言
    c语言二元插值,二维线性插值c语言 ... [详细]
  • flea,frame,db,使用,之 ... [详细]
  • 本文整理了一份基础的嵌入式Linux工程师笔试题,涵盖填空题、编程题和简答题,旨在帮助考生更好地准备考试。 ... [详细]
  • 构建一个可扩展的插件系统是软件开发中的重要任务。本文将探讨实现这一目标的关键步骤和最佳实践,包括模块化设计、接口标准化、动态加载机制以及安全性考量,旨在帮助开发者打造高效、灵活且安全的插件架构。 ... [详细]
  • 如何在Linux环境中高效地创建、编译与运行C语言代码
    本文面向第一次在linux尝试写C语言程序的新人。有经验的请绕行。C语言用法不是本文介绍的关键,本文只是引入如何第一次在linux系统下编写、编译和执行一个简单的c程序。学会后请自行深入 ... [详细]
  • CentOS下ProFTPD的安装与配置指南
    本文详细介绍在CentOS操作系统上安装和配置ProFTPD服务的方法,包括基本配置、安全设置及高级功能的启用。 ... [详细]
  • 小编给大家分享一下Vue3中如何提高开发效率,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获, ... [详细]
  • 本文将详细介绍如何在Android Studio中导入和编译OSChina Android 2.4版本的源码。包括所需软件、下载地址以及一些注意事项。 ... [详细]
  • 本文介绍了Java编程语言的基础知识,包括其历史背景、主要特性以及如何安装和配置JDK。此外,还详细讲解了如何编写和运行第一个Java程序,并简要介绍了Eclipse集成开发环境的安装和使用。 ... [详细]
  • 在开发过程中,我最初也依赖于功能全面但操作繁琐的集成开发环境(IDE),如Borland Delphi 和 Microsoft Visual Studio。然而,随着对高效开发的追求,我逐渐转向了更加轻量级和灵活的工具组合。通过 CLIfe,我构建了一个高度定制化的开发环境,不仅提高了代码编写效率,还简化了项目管理流程。这一配置结合了多种强大的命令行工具和插件,使我在日常开发中能够更加得心应手。 ... [详细]
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社区 版权所有