佛山制作手机网站,企业培训机构有哪些,wordpress 切换中文字体,如何自己做淘宝客网站《learn you a Haskell》这书的结构与常见的语言入门教材完全不一样。事实上#xff0c;即使学到第八章#xff0c;你还是写不出正常的程序…因为到现在为止还没告诉你入口点模块怎么写#xff0c;IO部分也留在了最后几章才介绍。最重要的是#xff0c;没有系统的总结数据类…《learn you a Haskell》这书的结构与常见的语言入门教材完全不一样。事实上即使学到第八章你还是写不出正常的程序…因为到现在为止还没告诉你入口点模块怎么写IO部分也留在了最后几章才介绍。最重要的是没有系统的总结数据类型、操作符、语句这些知识被零散的介绍在1-8章的例子中换句话来说这书其实不算是很合格的教材代码大全那种结构才更适合不过它重点强调了FP与其他语言的思想差异对于已经有其他语言基础的人来说读这本书能够很快领悟到FP的妙处对于那些并不用它来做项目估计也很难有公司用这个做项目太难了的人来说这本书很适合作为拓展阅读的小册子。另外一本《Real World Haskell》也相当有名我还没有看这本看完后再着手阅读吧。 第八章 自定义类型和类型类 关键字data用来构建类型格式为 data classname definition 左端是类型的名字右端是该类型的取值又称为构造子。例如 data Shape Circle Float Float Float | Rectangle Float Float Float Float deriving (Show) 这里表示类型Shape可以取值为Circle或Rectangle这两个类型的构造子分别是3个、4个Float。构造子本质是一种函数取几个参数返回一种类型。比如这里的Circle :: Float-Float-Float-Shape。关键字deriving表示派生于某类型类即具有该类型类的性质。这里面有一些类似于面向对象中的封装与继承的概念比如可以认为Shape是基类Circle和Rectangle是派生类。如果某个函数对Shape通用那么在模式匹配中就将两种派生类的实现都写出来否则只匹配部分类即可。注意Circle和Rectangle并不是自定义类型并不能直接使用只有在需要Shape时用构造子进行匹配来使用。如果构造子只有一个可以让data类型与构造子同名这样更加清晰。 如果说第七章的module相当于C中的namespace那么本章的data就相当于class不同的是module封装的是function而Haskell中class就是function所以module、data和function就构成了Haskell中的用户层次。 导出格式 module Shape (Shape(…) ,fun1 ,fun2 ) 其中Shape(…)表示导出全部构造子。 Record Syntax 我们知道C中提倡软件工程中的封装概念尽量不让内置数据类型暴露给使用者一般要写一大堆setxxxconst getxxx函数虽然这些小函数往往只有一句话但是写多了也很烦。Record Syntax是Haskell中一种用来简化此类函数书写的语法结构。在声明data时直接标明其参数名称和对应的数据类型 data ShapeCircle { x::Float,y::Float,radix::Float} | Rectangle { x1::Float,y1::Float,x2::Float,y2::Float} deriving (Show) 这里的语法有点像C中的位域但是它实际的意思其实仍然是前面学过的类型解释符。Haskell会自动生成已经注明名称的参数的函数。另外在调用构造子时也要遵从这种结构不过x::Float换成x1.0即Circle {x1.0,y2.2,radix3.1}。 类型参数 前面学习了值构造子这里介绍类型构造子。值构造子显然需要明确值的类型而类型构造子则宽松的多比如向量我们只需要向量的参数类型一致即可不必明确具体的类型。换句话说类型参数是一种泛型语法支持类似C的模板。 比如 data Vector a Vector a a a deriving (Show)这就是一个三维向量。类型参数a对后面的值构造子产生约束。也就是C中的 template class a class Vector { Vector(a first,a second, a last); } 显然我们在写函数的时候需要对类型参数进行实例化换句话说需要对函数进行类型约束。记住不要在data声明中添加类型约束而是在函数中添加因为具体执行操作的是函数而数据类型需要比函数更加抽象。这里按着C中写模板的思路来就很容易理解什么时候用类型构造子。 派生实例 本节介绍了从类型类从派生类型的方法。前面已经介绍过类型类本质上是一种接口要求它描述了类型的行为一致性。在我们创建类型时可以在值构造子后面加上deriving (Ord,Show,Read,…)来给创建的类添加接口。一旦加上了指定的类型类接口就给予所创建的类对应的行为特性。如果加了Ord就可以直接比较两个类根据值构造子和参数如果加了Show那么就可以显示该类的参数如果使用了syntax record就显示出名称值的格式。 派生实例是很有用的特性在我们设计类的时候需要明确该类支持的行为特性。这看起来有些像C中的重载操作符但是不需要我们自己去实现。Haskell会自动推断应该怎样实现声明的行为。 注意Haskell中True/False与C中意义完全不同【C语言中没有bool只是单纯认为0为false非0为true】可以认为 data Bool False | True deriving (Ord)所以True False是成立的。同理Nothing总是小于Just a。 类型别名 类似typedef的语法在Haskell中格式为 type NewtypeOldType,与typedef相似的是一般用来简化书写或者明晰概念。与typedef不同的是type也支持Haskell的不全调用。 这里举了Either a b作为例子Either a b是用来代替Maybe a的当函数的返回类型可能有多种或者函数需要根据情况返回不同的信息时经常使用Either a b作为返回类型。 data Either a b Left a | Right b deriving (Eq, Ord, Read, Show) Either a b有两个值构造子如果用了Left构造子其返回类型就是a否则是b换句话说Haskell可以返回多种类型的结果而不像C那样只能用结构来封装。 递归数据结构 【我本以为第8章到这就完了呢…结果后面又发现是翻译的大哥翻到一半就终止了第八章后面还有不少这部分是后来添补的】 考虑list如果我们需要自己定义list类型应该如何声明应该这样 data List a Empty | Cons a (List a) deriving (Show,Read,Eq,Ord) 这里Cons相当于运算符 : 显然这里的List定义是递归的——等号的左右两端都存在List。这样我们就可以像使用:一样使用Cons来构造List如 3 Cons 4 Cons Empty。 也可以自定义操作符使用infixr来确定操作符的优先级和左、右结合性注意这里可以定义任意操作符这一点和C中的重载操作符有本质的不同。书中以操作符 :-: 为例介绍了使用方法。 下面介绍了一个二叉搜索树的生成作为例子 data Tree a EmptyTree | Node a (Tree a) (Tree a) deriving (Show, Read, Eq) 一个二叉树是一个空树或者一个由根节点和其左子树与右子树构成的树。下面是一些常用操作函数的实现这里从略。 TypeClass102 本节介绍如何自定义类型类类型类与常见的过程式或者面向对象模型的编程语言并无相似之处不过可以和C中的重载操作符对比参考。 我们使用关键字class来定义类型类使用关键字instance来定义类型类的实例。 class Eq a where () :: a-a-Bool (/) :: a-a-Bool x y not x / y x / y not xy instance Eq TrafficLight where … instance下面就是详细解释该接口的实现。在不想使用默认的从类型类派生得到的行为时必须使用instance来自定义数据对于该接口的行为方式。对于Maybe类型的实例格式是 instance (Eq m) Eq (Maybe m) where 注意这里需要对m添加类型约束。 可以在GHCI中使用:info 得到类型类、类型和类型构造子的详细信息。 A yes-no typeclass 本节讲了一个类型类的实例它用来完成各种类型向Bool的缺省转换如C的非零为True0为False。 class YesNo a where yesno :: a - Bool 实现 instance YesNo Int where yesno 0 False yesno _ True instance YesNo [a] where yesno [] False yesno _ True instance YesNo Bool where yesno id …其他略 注意id是标准库函数。 函数子类型类 函数子类型类(Functor class)指的是可以被被映射的函数满足的接口。 class Functor f where fmap :: (a-b) - f a - f b 注意定义中的f并非类型而是类型构造子。换言之函数子类型类的实例参数必须是类型类构造子而不能是具体的类型那么可见这个类型类是容器的接口如Maybe或[]等拥有1个以上值构造子的类型。如果f有两个以上的参数那么只能用函数的不完全调用格式仅保留一个参数进行处理如Either a 种类和一些类型相关的东西 本节对类型type的知识做了扩展这里将类型本身分为具体类型和不完全类型。 这种类型的类型被称为种类kinds可以在GHCI中使用:k来对类型进行种类的分析。 GHCI使用 * 表示具体的类型如果不是具体类型就是可以通过一个或多个参数得到具体类型的不完全类型。 如果理解了类型构造子本身也是函数这一点本节的内容还是比较容易理解的。 第九章 输入与输出 第九章开始就没有中文的翻译了只能看英文资料英文9-14章戳我 到了第九章我们终于可以写Hello World了一本教材讲到大半才讲输入输出的也算是比较罕见了呵呵。 在Haskell中我们并不能像在命令式编程中一样随意改变非const变量的值。Haskell保持着这样一个特性对于一个函数只要它的调用参数不变那么它返回的值总是不变。Haskell不会试图改变已经确定的变量而是试图返回新的变量。那么这里出现一个问题如果Haskell并不改变任何变量那么它就无法输出——因为输出会改变屏幕。为此Haskell设计了一种机制将非纯函数式编程即与输入输出打交道的部分与函数式编程即前面八章介绍的内容隔离开来这里将这种机制称为side-effects直译为边界效应。 Hello World Hello World总是每种语言必须提到的东西。对于Haskell输出Helloword只需要一行代码嗯不愧是优雅与简洁的典范。 main putStrLn Hello world! main表示主函数所有涉及IO的函数都在main中执行。所以main函数经常被写作main:: IO ()当然()也可以换成其他的返回类型。 putStrLn函数的解释是 putStrLn :: String-IO () 也就是输入一个字符串执行一个IO action返回一个空的tuple。 因为IO行为是非纯函数行为所以Haskell设计了do块来将所有的非纯函数进行封装最后通过-操作符将IO取得的值绑定到一个变量上。do块中除了最后一个IO action 其他的均可绑定到一个变量上不过如putStrLn这种函数的返回值肯定是()所以绑定没什么意义。最后一个IO action会将返回值绑定给main本身。do块这种行为方式类似于verilog中的begin…end语句块。 如果不涉及IO仍然使用前面学过的let … in…来直接绑定变量不过这里in可以省略缺省成为整个do块中有效。 return语句在haskell中return语句只能在IO块中使用它表示一个IO行为输出一个可以通过变量绑定的值。return语句并不能从该段程序中返回。我们只有在需要执行一次什么都不做的IO或者不希望返回最后一个IO action取得的值时使用return语句。 mainmain本身即是一个IO函数所以可以通过在main函数结尾调用它来递归该函数。 其他IO函数 函数原型解释putStr类似putStrLn但尾部不输出换行符putChar/getChar输出/入字符print:: Show a a - IO ()输出一切属于show类型类的数据sequence:: Monad m [m a] - m [a]执行参数1中的I/O动作返回动作的结果mapM :: Monad m (a - m b) - [a] - m [b]map的I/O版相当于sequence . map mapM_ :: Monad m (a - m b) - [a] - m ()同上只是不再返回I/O动作的执行结果注意map一个I/O动作到一个list中并不会真正执行这个list中的动作。想要真正执行必须使用sequence函数当然方便起见可以使用mapM或mapM_。 这一块介绍了Control.Monad内的几个函数when函数取一个布尔值和一个I/O动作作为参数如果bool值为真执行该动作否则返回一个什么都不执行的I/O动作forever永久执行参数中的I/O动作forM类似于mapM只是参数的顺序颠倒。 文件与流 其实I/O这一块Haskell与命令式语言并无不同要注意的只是do块的位置和I/O函数结果的绑定。对于文件I/Ohaskell与C基本一致常用函数如下 函数原型解释getContents :: IO String从标准流读入数据直到EOFCtrlDinteract :: (String - String) - IO ()对输入执行参数1的函数输出结果openFile :: FilePath - IOMode - IO HandleSystem.IO打开文件选择方式返回句柄hGetContents :: Handle - IO String根据句柄返回文件内容hClose :: Handle - IO ()根据句柄关闭文件withFile :: FilePath - IOMode - (Handle - IO r) - IO r整合的一个函数打开文件并使用参数3处理文件完成后关闭文件hGetLine/hPutStr/hputStrLn/hGetChar大体同标准流只是参数中多了文件句柄readFile :: FilePath - IO String读取文件writeFile :: FilePath - String - IO ()写入文件模式为截断appendFile :: FilePath - String - IO ()写入文件模式为追加getTemporaryDirectory :: IO FilePathSystem.Directory读取临时文件夹路径openTempFile :: FilePath - String - IO (FilePath, Handle)System.IO打开临时文件返回一个tupleremoveFile/renameFileSystem.Directory删除、重命名文件参数是路径不是句柄注意这里的读取文件相关函数都是惰性的。以上青色字体来自System.IO库没有注明的来自Precluded库。使用句柄做参数的函数均以h开头。 缓冲控制如果需要修改编译器默认的缓冲机制可以使用函数hSetBuffering来修改使用hFlush来强制刷新缓冲区 这里介绍了Unix下管道操作符 | 的使用。简单来说可以通过管道操作符将上一个动作的输出作为下一个动作的输入。 命令行参数 同C语言程序一样Haskell也是可以接受命令行参数的。C语言将命令行参数作为main函数的参数传递而Haskell主要使用两个函数来取得用户输入的参数import System.Environment getArgs :: IO [String]取出所有参数getProgName :: IO String取得程序名称另外后面给了错误退出的函数类似stdlib中的exit函数errorExit。 随机数发生器 随机数发生器在任何语言中都是标准库自带的函数/类。虽然Haskell要求纯函数的输入一定时输出固定但是实际上几乎所有的语言中随机数发生器生成的都是伪随机数所以Haskell这个特性并不意味着其实现比一般的语言困难。相关函数如下(import System.Random): random :: (RandomGen g, Random a) g - (a, g)参数给出一个随机数种子返回一个随机数和一个新的种子mkStdGen :: Int - StdGen以一个整数为参数生成一个标准的种子randoms :: (RandomGen g, Random a) g - [a]根据种子生成一个无限长的随机序列randomR :: (RandomGen g, Random a) (a, a) - g - (a, g)参数1的pair限制了最后取得随机数的范围randomRs :: (RandomGen g, Random a) (a, a) - g - [a]根据参数1生成规定范围的无限长listgetStdGen :: IO StdGen取得一个全局的随机数发生器种子newStdGen :: IO StdGen刷新全局随机数发生器 注意random函数的返回类型可以是任何类型所以在使用的时候必须在后面加上类型约束::(type1,type2)作为随机数和种子的类型如果使用StdGen的种子则一般返回类型为(Int,StdGen)。 如果不执行newStdGen那么getStdGen总是返回同样的种子。 另外这里还介绍了read加入了错误处理的版本reads后者再不能读取参数时将返回空list。 二进制字符串 不清楚这个翻译是否合适总之Bytestrings主要介绍二进制读写文件的方法。不同于C语言这里有两个版本的二进制读取一个是严格的非惰性版本另外一个是惰性版本分别来自Data.ByteString和Data.ByteString.Lazy. 对于lazy版本这里和前面介绍的文件IO函数的实现也有所不同——它每次最少读取64K字节的东西。大体来讲ByteString的相关函数与Data.List中的函数接口一致不过在类型约束中用ByteString代替了[a]用Word8代替了a。函数pack将Word8打包成ByteStringunpack用于解包fromChunks将严格版非惰性转换为惰性版toChunks则相反cons和cons用来取代list的 操作符注意后者适用于非惰性版还有其他一些函数对应于list中的某些函数这里就不列举了。转载于:https://www.cnblogs.com/livewithnorest/archive/2012/08/02/2620718.html