)
文档教程【免费下载链接】learnxinyminutes-docsCode documentation written as code! How novel and totally my idea!项目地址https://gitcode.com/gh_mirrors/le/learnxinyminutes-docs点击查看免费下载本文以 learnxinyminutes-docs 仓库中的西班牙语教程 es/haskell.md 为骨架结合英文原版 haskell.md 深度展开系统讲解 Haskell 的语法基础、列表与惰性求值、函数定义与高阶技巧、类型系统、控制流、自定义数据类型、类型类、IO 与 Monad 入门以及 GHCi 交互式环境的完整使用方式。读完本文你将掌握用纯函数式思维编写可运行 Haskell 代码的全部核心能力并能在 GHCi 中即时验证每一个示例。背景说明learnxinyminutes-docs 是一个把编程语言教程写成带注释的可执行代码的开源仓库见 README.md同一语言在不同语言目录下均有翻译版本如 es/haskell.md、zh-cn/haskell.md、de/haskell.md 等。本文所有示例均可在 GHCi 中直接逐行运行验证。一、注释、原始数据类型与运算符Haskell 是面向实用场景设计的纯函数式编程语言purely functional programming language以 Monad 与强大的类型系统著称。它的注释语法非常直观-- 单行注释使用两个连字符 {- 多行注释可以像这样 包裹在一个块中 -}数字与算术运算Haskell 中数字直接作为表达式使用算术运算与主流语言一致3 -- 3 1 1 -- 2 8 - 1 -- 7 10 * 2 -- 20 35 / 5 -- 7.0需要特别注意的是Haskell 默认的除法/返回浮点数而非整数这与许多语言中整数相除得整数的行为不同35 / 4 -- 8.75浮点除法若需要整数除法使用反引号包裹的div中缀形式35 div 4 -- 8整数除法布尔值与布尔运算布尔值True/False是原始数据类型支持逻辑否定与比较运算True False not True -- False not False -- True 1 1 -- True 1 / 1 -- False 注意不等于用 / 而非 ! 1 10 -- True从上面可以看出not、、/等都是函数。Haskell 的函数调用不需要括号所有参数依次列在函数名之后通用模式为-- func arg1 arg2 arg3 ...字符串与字符Haskell 严格区分字符串双引号与字符单引号Esto es una cadena. -- 这是一个字符串 a -- 这是一个字符 No puedes utilizar comillas simples para cadenas. -- 错误单引号不能用于字符串字符串拼接使用¡Hola mundo! -- ¡Hola mundo!字符串本质上是字符的列表因此字符列表可以直接写为字符串字面量[H, o, l, a] -- Hola Esto es una cadena !! 0 -- E取第 0 个字符关于!!索引运算英文原版 haskell.md 补充了一个重要事实列表是链表结构!!是O(n) 复杂度的操作与数组语言的 O(1) 随机访问有本质区别。二、列表、元组与惰性求值列表基础列表的所有元素必须是同一类型。下面两种写法完全等价[1, 2, 3, 4, 5] [1..5]区间Range语法非常灵活可以作用于字符[A..F] -- ABCDEF区间支持指定步长step[0,2..10] -- [0, 2, 4, 6, 8, 10] [5..1] -- 不工作Haskell 默认是递增的 [5,4..1] -- [5, 4, 3, 2, 1]显式给出前两个元素指定步长 -1列表从 0 开始索引[0..] !! 5 -- 5无限列表与惰性求值Haskell 支持无限列表这是纯函数式语言的标志性能力[1..] -- 所有自然数的列表无限长无限列表之所以可行是因为 Haskell 采用惰性求值lazy evaluationHaskell 只在需要时才求值。因此你可以直接取第 1000 个元素[1..] !! 999 -- 1000此时 Haskell 只求值了前 1000 个元素这个无限列表的其余部分尚未真正存在——直到你真正需要它们时才被求值。惰性求值是理解 Haskell 一切高级特性的基石它让定义无限数据结构成为日常操作。列表常用操作-- 拼接两个列表 [1..5] [6..10] -- [1,2,3,4,5,6,7,8,9,10] -- 在头部添加元素: 即 cons 操作符 0:[1..5] -- [0, 1, 2, 3, 4, 5] -- 更多列表函数 head [1..5] -- 1取首元素 tail [1..5] -- [2, 3, 4, 5]去掉首元素 init [1..5] -- [1, 2, 3, 4]去掉末元素 last [1..5] -- 5取末元素列表推导List Comprehension列表推导是 Haskell 中构建列表的声明式语法[x*2 | x - [1..5]] -- [2, 4, 6, 8, 10] [x*2 | x - [1..5], x*2 4] -- [6, 8, 10]带条件过滤元组Tuple元组与列表的关键区别元组的元素可以类型不同但长度固定。(haskell, 1) -- 二元组示例访问二元组元素使用fst和sndfst (haskell, 1) -- haskell snd (haskell, 1) -- 1英文原版特别提醒fst/snd仅适用于二元组对三元组等 n 元组使用会报错。若需提取三元组中间元素应使用通配符_结合模式匹配见下一节sndOfTriple (_, y, _) y -- 用 _ 跳过不需要命名的值三、函数定义从基础到模式匹配定义与调用函数定义就是一组方程左侧是函数名加参数右侧是表达式add a b a b注意如果使用 GHCi 解释器定义需要加let前缀let add a b a b见本文 REPL 章节。调用函数直接空格分隔参数add 1 2 -- 3中缀调用用反引号把函数名包起来即可把普通函数当中缀运算符使用1 add 2 -- 3自定义运算符函数名甚至可以不含字母——这让你能定义自己的运算符下面定义一个整数除法运算符//(//) a b a div b 35 // 4 -- 8守卫Guards守卫guard是一种简洁的分支机制用|引导条件otherwise作为兜底分支fib x | x 2 1 | otherwise fib (x - 1) fib (x - 2)模式匹配Pattern Matching模式匹配与守卫类似但按左侧模式进行分派。下面给出fib的三个方程Haskell 会自动选用第一个与参数值匹配的方程fib 1 1 fib 2 2 fib x fib (x - 1) fib (x - 2)元组同样可以模式匹配foo (x, y) (x 1, y 2)列表模式匹配是函数式编程的核心范式x:xs把列表拆成首元素x与剩余部分xs。由此可以手写自己的mapmyMap func [] [] myMap func (x:xs) func x:(myMap func xs)匿名函数Lambda匿名函数用反斜杠\加参数列表定义myMap (\x - x 2) [1..5] -- [3, 4, 5, 6, 7]折叠Foldfoldl1表示从左折叠并把列表第一个值作为累加器初值foldl1 (\acc x - acc x) [1..5] -- 15即 12345四、高阶函数技巧部分应用、函数组合与 $部分应用Partial Application如果只传入部分参数函数会被部分应用返回一个接收剩余参数的新函数add a b a b foo add 10 -- foo 现在是给任意数加 10的函数 foo 5 -- 15等价写法是运算符节operator section直接把二元运算符和一个操作数括起来foo (10) foo 5 -- 15函数组合.运算符把函数链在一起右侧函数的输出作为左侧函数的输入。下面的foo先加 10、再乘 5foo (*5) . (10) -- (5 10) * 5 75 foo 5 -- 75$ 运算符与优先级$是 Haskell 中一个重要的应用运算符。与标准函数应用优先级最高 10、左结合不同$的优先级为 0 且右结合——低优先级意味着其右侧的整个表达式会作为参数传给左侧函数从而省去大量括号-- 之前的写法需要括号 even (fib 7) -- False -- 等价写法$ 免括号 even $ fib 7 -- False -- 结合函数组合 even . fib $ 7 -- False五、类型签名与强类型系统Haskell 拥有非常强大的静态类型系统每个合法表达式都有类型。类型签名用::声明5 :: Integer hola :: String True :: Bool函数同样有类型。not接收一个 Bool 返回一个 Bool-- not :: Bool - Bool接收两个参数的函数类型用箭头链表示-- add :: Integer - Integer - Integer良好实践定义值之前先在其上方写出类型签名double :: Integer - Integer double x x * 2类型签名不仅是文档更是编译器进行静态检查的依据——类型不匹配的代码会在编译期被拒绝。六、控制流if 表达式、case 表达式与递归if 表达式Haskell 的if是表达式有值而非语句。单行写法haskell if 1 1 then awesome else awful -- haskell awesome多行写法中缩进至关重要haskell if 1 1 then awesome else awfulcase 表达式case用于按值分派是解析命令行参数的典型手法case args of help - printHelp start - startProgram _ - putStrLn bad args通配符_匹配所有未列出的情况。Haskell 没有循环用递归与高阶函数替代Haskell没有循环结构代之以递归和高阶函数。map对列表每个元素应用函数map (*2) [1..5] -- [2, 4, 6, 8, 10]可以用map封装出自己的forfor array func map func array -- 使用\i - show i 是匿名函数 for [0..5] $ \i - show i -- 也可以直接传函数show 本身就是函数 for [0..5] showfilter保留满足条件的元素英文原版补充filter even [1..10] -- [2, 4, 8, 10]foldl 与 foldr列表归约foldl/foldr将整个列表归约为一个值签名模式为foldl fn valor inicial lista折叠函数、初值、列表foldl (\x y - 2*x y) 4 [1,2,3] -- 43foldl是左折叠等价于从左侧开始层层嵌套-- 等价展开(2 * (2 * (2 * 4 1) 2) 3)foldr是右折叠从右侧开始foldr (\x y - 2*x y) 4 [1,2,3] -- 16-- 等价展开(2 * 1 (2 * 2 (2 * 3 4)))同一个函数表达式由于折叠方向不同结果从 43 变成 16——这正是理解 fold 左/右结合语义的关键示例。七、自定义数据类型和类型Sum Type与枚举data声明自定义类型左侧是类型构造器右侧用|分隔多个数据构造器构成和类型/联合类型data Color Rojo | Azul | Verde之后即可在函数中模式匹配使用say :: Color - String say Rojo ¡Es Rojo! say Azul ¡Es Azul! say Verde ¡Es Verde!注意类型构造器用于类型签名数据构造器用于函数体二者在使用层面分工明确。带参数的数据构造器英文原版进一步展示了携带字段的构造器例如二维坐标点data Point Point Float Float以及多个带参构造器组合的类型data Name Mononym String | FirstLastName String String | FullName String String String此外还有记录语法record syntax字段自动生成访问器函数并支持函数式更新data Point2D CartesianPoint2D { x :: Float, y :: Float } | PolarPoint2D { r :: Float, theta :: Float } myPoint CartesianPoint2D { x 7.0, y 10.0 } xOfMyPoint x myPoint -- 7.0自动生成的访问器 myPoint myPoint { x 9.0 } -- 记录更新x 变为 9.0参数化类型Maybe自定义类型可以带类型参数最著名的例子是Maybedata Maybe a Nothing | Just a以下值都属于Maybe类型Just hello -- 类型为 Maybe String Just 1 -- 类型为 Maybe Int Nothing -- 类型为 Maybe a对任意 a 都成立Maybe正是 Haskell 表达可能失败/可能缺失的惯用方式是安全处理空值的基石。八、类型类TypeclassesHaskell 的多态机制本节内容来自英文原版 haskell.md 的专题章节是理解 Haskell 标准库组织方式的关键补充。类型类是 Haskell 实现多态的一种方式类似于其他语言的接口interface它定义一组必须对该类中所有类型成立的函数。以Eq可判等类型类为例class Eq a where () :: a - a - Bool (/) :: a - a - Bool x y not (x / y) x / y not (x y)这个声明要求两个函数()和(/)并声明二者可以互相定义——因此你只需实现其中一个另一个会由类型类定义自动补全。使用instance关键字让某个类型成为类型类成员instance Eq TrafficLight where Red Red True Green Green True Yellow Yellow True _ _ False之后即可对TrafficLight使用()与(/)。带类型类约束的函数签名用表达isEqual :: (Eq a) a - a - Bool isEqual x y x y注意x与y必须是同一类型。isEqual Red 2不合法——即便Int和TrafficLight都是Eq的实例类型类也不允许混用不同类型。其他常见类型类包括Ord可排序支持、等、Read可由字符串解析、Show可转为字符串显示、Num/Real/Integral/Fractional可做数学运算、Enum可枚举步进、Bounded有最大最小值。对于简单类型可以用deriving让编译器自动生成Eq、Ord、Read、Show、Enum、Bounded实例免去手写data Point Point Float Float deriving (Eq, Read, Show)英文原版还指出type关键字可创建类型同义词如type String [Char]、type Weight Float同义词不需要构造器但由于同义词最终归约到相同的基础类型语义上不合理的混用也能编译通过使用时应谨慎。九、Haskell IO 与 Monad 入门IO 的完整解释离不开 Monad但掌握足够的入门知识就能立即上手编写真实程序。main 与 putStrLn程序启动时执行main它必须返回IO ()类型的值main :: IO () main putStrLn $ ¡Hola, cielo! (say Blue) -- putStrLn 的类型是 String - IO ()interact字符串到字符串的程序如果能把程序实现为String - String的纯函数IO 会变得非常简单。库函数interact读入全部文本、运行函数、打印输出-- interact :: (String - String) - IO () countLines :: String - String countLines show . length . lines main interact countLines这里countLines是纯函数通过.组合show数值转字符串、length行数、lines按行拆分完美示范了纯核心 薄 IO 壳的架构风格。do 记法串联动作可以把IO ()类型的值理解为计算机要执行的一系列动作类似命令式程序。用do记法串联动作sayHello :: IO () sayHello do putStrLn ¿Cual es tu nombre? name - getLine -- 读取一行输入并绑定到 name putStrLn $ Hola, name练习写一个只读取一行的interact版本。注意sayHello中的代码永远不会自动执行——唯一被执行的入口是main。要运行sayHello注释掉上面的main定义并替换为main sayHello。getLine 与自定义 IO 动作getLine的类型是IO String。可以把IO a类型的值理解为一个执行后会生成a类型值的程序。用-把生成的值绑定到名字以便复用也可以组合自己的IO String动作action :: IO String action do putStrLn Esta es una linea. input1 - getLine input2 - getLine -- do 语句块的类型由其最后一行决定 -- return 不是关键字只是一个普通函数 return (input1 \n input2) -- return :: String - IO String之后action可以像getLine一样使用main do putStrLn ¡Volveré a repetir dos líneas! result - action putStrLn result putStrLn Esto es todo, ¡amigos!IO 即 Monad纯度与并发IO是Monad的一个实例。Haskell 借助 Monad 做 IO从而保持纯函数式本质任何与外部世界交互做 IO的函数都会在类型签名中被打上IO标记。这让我们能清晰区分纯函数不交互、不改状态与不纯的函数。这是一项强大特性纯函数易于并发执行因此 Haskell 的并发编程非常自然——只要函数是纯的并行化就是安全的这正是许多高并发服务选择 Haskell 的深层原因。十、GHCi 交互式环境GHCi 是 Haskell 的交互式解释器REPL。在终端输入ghci启动即可逐行验证本文所有示例。定义新值需要使用letlet foo 5用:t查看任意值或表达式的类型 :t foo foo :: Integer英文原版还演示了:t d显示默认类型、:t (:)检查运算符类型、:i获取名字的详细信息、:?列出全部命令等进阶用法可参见 haskell.md。REPL 中可直接运行任何IO ()类型的动作 sayHello ¿Cual es tu nombre? Amigo Hola, Amigo十一、综合示例QuickSort 与安装指引作为收尾看一个极能体现 Haskell 声明式风格的经典实现——快速排序。整个算法用三行方程表达无需任何循环或变量赋值qsort [] [] qsort (p:xs) qsort lesser [p] qsort greater where lesser filter ( p) xs greater filter ( p) xs把基准p与剩余元素xs分开用filter分出小于与不小于基准的两部分递归排序后拼接——可读性与数学定义几乎一一对应。安装 Haskell最传统的方式是通过 Haskell Platform基于 Cabal较新的方式是 Stack 工具链Stackage 发行版。安装完成后即可用ghci进入交互环境或把代码保存为.hs文件后用runghc/ghc编译运行仓库中 es/haskell.md 的 frontmatter 指定了示例文件名haskell.hs英文原版则为learnhaskell.hs。进一步学习本文所有内容都来自仓库中可直接运行的带注释代码。若希望继续深入可以阅读英文原版 haskell.md 中关于类型类、记录语法与类型同义词的更完整论述也可以对照 zh-cn/haskell.md 等中文翻译版本交叉学习。经典的进阶读物包括《Learn You a Haskell for Great Good!》《Real World Haskell》以及西班牙语社区的《Aprende Haskell por el bien de todos》——掌握类型类与 Monad 这两大核心概念后Haskell 的优雅与乐趣将真正向你展开。赞分享文档教程【免费下载链接】learnxinyminutes-docsCode documentation written as code! How novel and totally my idea!项目地址https://gitcode.com/gh_mirrors/le/learnxinyminutes-docs点击查看免费下载相关推荐Bash 入门与实战指南基于 learnxinyminutes-docs 的 GNU Bash 完整教程Bash 入门与实战指南基于 learnxinyminutes docs 的 GNU Bash 完整教程 本指南以 learnxinyminutes docs文档教程Elm 语言速成指南learnxinyminutes-docs 中的纯函数式 Web 前端编程入门Elm 语言速成指南learnxinyminutes docs 中的纯函数式 Web 前端编程入门 本篇技术指南以 learnxinyminutes docs文档教程G-Helper终极指南华硕笔记本轻量级控制工具完全解析G Helper终极指南华硕笔记本轻量级控制工具完全解析 G Helper是一款专为华硕笔记本电脑设计的开源控制工具作为Armoury Crate的轻量级替桌面应用系统编程上一篇WarcraftHelper终极指南魔兽争霸3完整优化解决方案下一篇tailcat --ssh-authorized-keys 与 GitHub 集成自动拉取 GitHub 用户公钥完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考