# code-haskell **Repository Path**: quanw20/code-haskell ## Basic Information - **Project Name**: code-haskell - **Description**: 使用Haskell 学习函数式 - **Primary Language**: Haskell - **License**: WTFPL - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-06-14 - **Last Updated**: 2022-08-08 ## Categories & Tags **Categories**: Uncategorized **Tags**: Haskell, monad, Functional ## README # CODE_HASKELL ## 0. 简介 Haskell 是一种标准化的、通用纯函数式编程语言,有非限定性语义和强静态类型。 Haskell 语言的最主要的执行环境是 GHC。 Haskell 是一门以表达式为主导(expression-oriented)的语言 ## 1. 安装 VS code Haskell 开发环境 1. 安装 Ghcup To install on Windows run the following in a PowerShell session (as a non-admin user): ```powershell Set-ExecutionPolicy Bypass -Scope Process -Force;[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072;Invoke-Command -ScriptBlock ([ScriptBlock]::Create((Invoke-WebRequest https://www.haskell.org/ghcup/sh/bootstrap-haskell.ps1 -UseBasicParsing))) -ArgumentList $true ``` 1. 安装 VS code 插件 ``` haskell.haskell justusadam.language-haskell hoovercj.haskell-linter ``` 3. 安装 hlint ```powershell cabal install hlint ``` ## 2. 学习路径 ### 2.1 ghci :t 命令处理一个表达式的输出结果为表达式后跟::及其类型 ::读作“它的类型为” 凡是明确的类型,其首字母必为大写 如果你打算给你编写的函数加上个类型声明却拿不准它的类型是啥,只要先不写类型声明,把函数体写出来,再使用:t 命令测一下即可 :info 来查看某个操作符的优先级 ```haskell Prelude> :info (+) type Num :: * -> Constraint class Num a where (+) :: a -> a -> a ... -- Defined in `GHC.Num' infixl 6 + -- 左结合 优先级 6 Prelude> :info (^) (^) :: (Num a, Integral b) => a -> b -> a -- Defined in `GHC.Real' infixr 8 ^ -- 右结合 优先级 8 ``` 优先级和结合性规则的组合通常称之为操作符位(置)(fixity )规则 ### 2.2 数据类型 1. 基本数据类型 ```haskell Int A fixed-precision integer type Integer A unfixed-precision integer type Float Single-precision floating point numbers. Double Double-precision floating point numbers. Bool True or False Char An enumeration whose values represent Unicode ``` 2. 常用容器 **List** 用:把元素与 list、其他元素连接(cons)起来。:是右结合的运算符。[1,2,3] 实际上是 1:2:3:[] 的语法糖。 两个 List 合并通过 ++ 运算子实现。 按照索引取得 List 中的元素,可以使用 !! 运算子,索引的下标为 0。 List 中的 List 可以是不同长度,但必须得是相同的型别。 ['K'..'Z']这样的 Range 方法快捷定义一个 List。[2,4..20]用法给出了 Range 的第一、第二、最后一个元素。 使用 > 和 >= 可以比较 List 的大小。它会先比较第一个元素,若它们的值相等,则比较下一个,以此类推。 **Tuple** 使用( )与逗号分隔符,定义一个 tuple 的实例。其元素可以是不同类型,但个数是固定的。 fst 返回一个序对的首项。 snd 返回序对的尾项 zip 取两个 List 作为参数,然后将它们依次配对,形成一组序对的 List。 3. 类型类(Typeclass) 描述参与运算的数据的类型,对其加以限制 并不是一个类,倒更像一个接口,Eq a 对类型 a 作了限制,表示类型 a 必须得是 Eq 的一个实例(instance)才行。 而成为 Eq 的实例, 需要实现一些函数,这些函数告诉了==满足什么样的条件说明这两个元素是相等的。 所以,并不是所有类型的元素都可以做==的参数的,必须得是 Eq 实例的类型才可以。 ```haskell Eq 可判断相等性的type 它的实例,可以用==比较这个类型的两个元素是不是相等 Ord 可比较大小的type 结果是三种类型之一:GT,LT,EQ Show 可表示为字符串的type 它的实例,ghci就知道如何来显示它 Read 可从字符串转换出值的type Enum 连续的,也就是可枚举的type。通过`succ`函数得到后继,`pred`函数得到前置 Bounded 有上限和下限。例如:maxBound :: Char Num 实数和整数。而Intgral仅包含整数 Integral 整数 Floating 浮点数 Float和Double ``` 4. 定义一个新一数据类型(使用 data 关键字来定义) ```haskell data Bool = False | True ``` 要想显示:使用`derivin`g成为`Show`的实例 ```haskell data Shape = Circle Float Float Float | Rectangle Float Float Float Float deriving (Show) ``` 1. 类别名(Type Synonyms) 可以通过下面的方式为`[Char]`起一个别名 String,这样可以使类型的名字有较好的自我描述能力。 ```haskell type String = [Char] ``` 递归定义+类型变元(Type variable) 下面我们来定义一棵树,树的定义是递归的,我们可以这样定义 ```haskell data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show) ``` ### 2.3 运算符 ```haskell + - * / -- 加、減、乘、除、 ^ ** -- 整数指數 浮点数指数 mod -- 取餘數 $ -- 也是表示函數作用的, 但它的優先級最低, 而且作用次序是從右向左的 ++ -- 兩個List的連接 . -- 函數的複合 && || == /= -- 與、或、等於、不等於 <= >= < > -- 小於等於、大於等於、小於、大於 : -- 增加一个元素到列表的头部 它读成“cons”(即“construct”的简称) // = @ -- 一個元素連接List、 -> -- 函數類型描述,運算符左邊為參數類型,右邊為結果類型。為右结合。例如:addThree :: Int -> Int -> Int -> Int => -- 運算符的左邊表示一個類型變量(通常為單個小寫字母)屬於一個類型類(Typeclass),相當於C++語言的模板參數類型 .. -- List的Range限定 :: -- 函數/表達式的類型特徵,左側為函數/表達式,右側為類型 <- -- List comprehension 的條件限定 !! -- 取List的第某個元素,下標從0開始 ``` 中缀运算符可以加括号变为单参数函数。 如 (\*3) 5 的值为 15。 但(-5)表示负值,所以单参数函数需要写为(subtract 5) 中缀运算符 $,可用于改变函数的调用次序,使其右边的表达式先计算出来。 例如 `f (g (z x)) <==> f $ g $ z x` 等价。其定义是: ```haskell ($) :: (a -> b) -> a -> b f $ x = f x ``` 结合性 associativity 对于运算符 R 有 ```haskell X1 R X2 R X3 R ...=(...((X1 R X2)R X3)...) => 左结合 X1 R X2 R X3 R ...=(...(X1 R(X2 R X3))...) => 右结合 ``` ### 2.4 表达式 >let 表达式: 在 ghci 下,可以使用 `let` 关键字来定义一个常量。在 ghci 下执行 `let a=1` 与在脚本中编写 `a=1` 是等价的。 格式为: ```haskell let [bindings] in [expressions] ``` >if then else 表达式: ```haskell if then else ``` >case 表达式 ```haskell case expression of pattern -> result pattern -> result pattern -> result ``` ### 2.5 函数 1. 函数 函数调用有最高运算顺序,例如 succ 9*10 表示(succ 9)*10。 函数的调用使用空格符而不是括号。 函数的复合调用是左结合 首字母大写的函数是不允许的 两个参数的函数的调用可以写成中缀形式: param1 `funcName` param2 运算符可以用括号围起来,作为前缀形式:(+) 2 3 的结果为 5 2. 模式匹配 模式匹配通过检查数据的特定结构来检查其是否匹配,并按模式从中取得数据 ```haskell fact :: Integer -> Integer fact 0 = 1 fact n = n * fact(n-1) ``` 3. Guards 在模式匹配中,通常匹配一个或多个表达式,但是使用 Guards 来测试表达式的某些属性 ```haskell fact :: Integer -> Integer fact n | n == 0 = 1 | n /= 0 = n * fact (n-1) ``` 4. Where 子句 可在运行时用于生成所需的输出。 如果输入是具有多个参数的复杂表达式。在这种情况下,可以使用 where 子句将整个表达式分解成小部分 ```haskell -- 计算 x ^ 2-8x + 6 = 0 的根 roots :: (Float, Float, Float) -> (Float, Float) roots (a, b, c) = (x1, x2) where x1 = e + sqrt d / (2 * a) x2 = e - sqrt d / (2 * a) d = b * b -4 * a * c e = - b / (2 * a) ``` ### 3. Pure 副作用指的是,函数的行为受系统的全局状态所影响。 举个命令式语言的例子:假设有某个函数,它读取并返回某个全局变量,如果程序中的其他代码可以修改这个全局变量的话,那么这个函数的返回值就取决于这个全局变量在某一时刻的值。我们说这个函数带有副作用,尽管它并不亲自修改全局变量。 副作用本质上是函数的一种不可见的(invisible)输入或输出。Haskell 的函数在默认情况下都是无副作用的:函数的结果只取决于显式传入的参数。 从类型签名可以看出一个 Haskell 函数是否带有副作用 —— 不纯函数的类型签名都以 IO 开头