個人檔案Nothing :: Maybe Future部落格清單訪客留言更多 ![]() | 說明 |
|
|
20 January Haskell 的 IDE Haskell 作为 FP 的典型语言,目前尚无成型的 IDE 环境,为了方便学习,于是考虑自己整合一个。因为对 UltraEdit 比较熟悉,于是考虑利用它来扩展一下。
要把 UE32 作为 Haskell 的 IDE 使用,要做几个方面的工作。
第一个是语法高亮。需要修改 UE32 的 wordfile.txt 文件。内容较多,由于现在学习得还没入门,整理得还不够完善,暂不发出来了。以后补上。
第二个是编译和调试。可以利用 UE32 的自定义命令来调用 GHC 进行编译。具体操作如下:
17 October 学习 Haskell想开始认识一下这个据说是最贴近数学语言的计算机语言。
从 haskell.org 上选了 GHC 作为编译器,利用已有的 UltraEdit-32 作为编辑器,Yet Another Haskell Tutorial 作为学习的参考资料,再加上 Refference, Card 和 GHC 的文档,可以开始了。
持续更新中……
#### ## 1月23日 - 普通的数据实体类型 (type) 用 * 表示,种类 (kind) 的类型是 (* -> *) ,如 Maybe 和 List 。 - 定义的类型别名 (type synonym) 可以用来定义其他的类型别名,限制是在定义时必须以完整参数表的形式给出定义式。 - 利用 IO 类型是 Monad 实例的特性,文件操作可以简单地使用类似这样的表达式: readFile "testdata.txt" >>= \s -> putStrLn (show s) * 一个附加问题:Haskell 如何操作二进制文件? 1. writeFile 测试这样的表达式: writeFile "data.bfile" $ map (\a -> toEnum a::Char) [0..255] 其输出文件中,数字 10 被转换为十六进制 0D 0A ,多了一个 0D 。也许是“文本格式”的原因。 2. readFile 测试这样的表达式: readFile "data.bfile" >>= putStrLn . show . map fromEnum 根据输出,0D 0A 被还原为 10 暂且略过。文件读到 1A 就认为是结束了。 经测试,IO 提供的其他判断文件结束的函数也是同样效果。 - Monad 的几个基本方法: class Monad m where return :: a -> m a fail :: String -> m a (>>=) :: m a -> (a -> m b) -> m b (>>) :: m a -> m b -> m b - IO 实现 do 符号的四个规则 (亦即编译器处理 do 子句的方式) : 1. do {e} → e 2. do {e; es} → e >> do {es} 3. do {let decls; es} → let decls in do {es} 4. do {p <- e; es} → let ok p = do {es} ok _ = fail "..." in e >>= ok 这四个规则联合起来实现了 do 子句调用 Monad 的过程。 换言之,可以直接用 Monad 本身的语法写出无 do 子句的 IO 函数来。(虽然这不够直白) - Monad 必须满足的三条法则 (若自定义不满足的,就可能有一些内建运算无法使用?也许可以利用这个特性测试编译器?) : 1. return a >>= f ≡ f a 2. f >>= return ≡ f <两种展开,乘一律> 3. f >>= (\x -> g x >>= h) ≡ (f >>= g) >>= h <结合律> eg. getLine >>= (return . reverse) >>= putStrLn - §9.3 的例子总结。 ㈠ 证明了一个 Monad 的基础函数符合三条法则。 ㈡ 一个典型的 Monad 构造过程: 1. 写出一个常规函数。 2. 比较常规函数和原形函数的差别,把差别抽象成类型。 3. 定义(实现)抽象类型的“接口”(return 和 bind)。 4. 将新类型代入常规函数,写出常规函数的 Monad 版本。 在这个过程中,定义的 Monad 类型不是符合原形中的数据结构类型,而是,对其进行扩展的数据单元类型。 * 应能对此进行扩展,目前没想到。 - #### ## 1月19日 - §8.4.2 里介绍了一个 Computation 类。此类是 Monad 的一个近似型。而对 OO 语言来讲,它是一个计算接口类。提供了四个接口:success 、failure 、augment 和 combine 。 这四种方法把某种计算的结果封装到一起,其中包含了可能的成功和失败。这该是 Monad 的主要思想。 从 Computation 类的定义看: class Computation c where success :: a -> c a failure :: String -> c a augment :: c a -> (a -> c b) -> c b combine :: c a -> c a -> c a success 把一个数据类型 a 封装成匹配 Computation 类的类型 c a ,表示一个成功的计算; failure 接受一个字符串,返回一个 Computation 类的量,表达失败的计算; augment 接受一个计算,一个将这个计算变为另一个计算的变换,从而产生新的计算。 combine 接受两个之前的计算,产生一个新的,作为两者的结合。这个运算不是 Monad 必须的接口,但在 MonadPlus 中可以找到。 在 Monad 类中,success 叫做 return ,failure 叫做 fail 以及 augment 叫做 >>= (bind) 。 * 上面基本属于直译,因为觉得还是这样说起来稍微好接受点。 - ####
## 1月18日 - 两种类型定义:data①、newtype② 和 type③ 。 ①定义的类型的数据集中含“bottom”;②用来定义自己实现的 Int 类型。 ①的定义方式没有特别的局限;②的定义格式是受限制的 MyType a 方式。 ③定义的是一个明确结构的别名。 ⑴三者都可以实现某类的 instance 。 ⑵①和③可以 deriving 某类。 - ####
## 12月18日 - 对数列的处理。 两个函数(家族):zip、unzip 截取函数:take、drop 整合参数:curry、uncurry 数列的另一个定义方式:[ f x | x <- xSet, otherConditions ] - 库中常用数列类型复杂度。 List Array FiniteMap insert O(1)+ O(n) O(log n) update O(n) O(n) O(log n) delete O(n) O(n) O(log n) find O(n) O(1)+ O(log n) map O(n) O(n) O(n log n)x - 可以用 foldx 结合函数运算(“.”等),简化很多 list 的处理表达式。 - #### ## 12月10日 - 实例定义。 instance BaseClass MyDataTypeOrClass where OneCompleteDefinationMethods ... instance BaseClass x => BaseClass (MyDataTypeOrClass x) where OneCompleteDefinationMethods ... 一些常用类的最小完备方法集: Eq : A (==) :: Eq a => a -> a -> Bool B (/=) :: Eq a => a -> a -> Bool Show : A show :: Show a => a -> String B showsPrec :: Show a => Int -> a -> String -> String showList :: Show a => [a] -> String -> String Read : A readsPrec :: Read a => Int -> String -> [(a, String)] readList :: String -> [([a], String)] Ord : A compare :: Ord a => a -> a -> Ordering B (<=) :: Ord a => a -> a -> Bool C (>) :: Ord a => a -> a -> Bool D (>=) :: Ord a => a -> a -> Bool E (<) :: Ord a => a -> a -> Bool F min :: Ord a => a -> a -> a G max :: Ord a => a -> a -> a Enum : pred :: Enum a => a -> a succ :: Enum a => a -> a A toEnum :: Enum a => Int -> a A fromEnum :: Enum a => a -> Int enumFrom :: Enum a => a -> [a] enumFromThen :: Enum a => a -> a -> [a] enumFromTo :: Enum a => a -> a -> [a] enumFromThenTo :: Enum a => a -> a -> a -> [a] Num : A (-) :: Num a => a -> a -> a A (*) :: Num a => a -> a -> a A (+) :: Num a => a -> a -> a A negate :: Num a => a -> a A signum :: Num a => a -> a A abs :: Num a => a -> a A fromInteger :: Num a => Integer -> a - #### ## 12月9日 - 文件操作,多半需要 bracket 作为 wapper 来使用。 - 模块定义: module MyModule module MyModule ( NoInstanceDataType(), PartValidDataType( PublicDataValue ), FullValidDataTypeA( FullValidDataTypeA ), FullValidDataTypeB( .. ), PublicFunction ) 模块引入: import MyModule hiding ( PrivateDefinations ) import qualified MyModule ... -> MyModule.MyFunction ... import qualified MyModule as MyShortName -> MyShortName.MyFunction ... 多级模块(非 Haskell 98 标准,GHC 要 -i 参数): import qualified SubDir.MyModule as MyShortName - 代码的书写,可读性编程(Literate Programming),.lhs 代码 Bird-script(有点仿 BBS 或 Email 的回复引文的味道) 1. 代码行首加“>”字符,其他不加。 2. 代码行与注释行间有空行分隔。 LATEX-style markup 用 \begin{code} 行和 \end{code} 行明确代码行的范围。 - 函数式编程另类指南 http://chn.blogbeta.com/232.html - where 与 let 。同一, where 子句是转换为 let 子句后进行编译的。 - () 转换运算符为函数。可以映射为省略参数的内嵌函数。 `` 转换二元的函数为运算符。 - 多参函数可以理解为多阶函数。接受一个参数后,输出一个函数,接收下一参数,类推。 这种函数可以赋值,可以进行一些函数运算。 - 与 . 运算组合函数类似,$ 运算应用函数。但 $ 运算优先级比较低,可以用以在一些情况下替代括号。 - 通过基于函数的运算,对定义进行纯数学推导。可以优化与分析函数。 - 几个调整参数的函数。 curry - 将第二、三个参数做成 pair 给第一个参函数作为参数。取返回值。 uncurry - 将第二个 pair 参数分解为两个值给第一个参函数作为参数。取返回值。 flip - 将第二、三个参数顺序倒置,给第一个参函数作为参数。取返回值。 - #### ## 11月13日 - CPS, 这种机制的实现,除了函数本身工作所需要的参数之外,需要接受一个“继续”函数作为参数,用来决定完成以后如何继续工作。 * 编译器实现上,用尾递归替代了函数直接返回?是否有额外的系统负担? - #### ## 11月4日 - Just, Nothing, Nil 与 Cons, Left, Right 等,可以理解为定义类型的时候指定的特别的命名常量或命名运算。 相应地,data 标识符可以定义一些常量。 - 对于 Either 这种数据类型,需要更详细的例子帮助理解。 - ####
## 10月21日
这两天忙于其他事情,没继续学习。
今晚 goo 了一下,发现一个数学上的强人用玩笑的语气写的一点关于 haskell 的东西,很精炼,似乎覆盖到了查 Reference 时所见到的绝大多数语法。其中还有几个应用的例子。最重要的是,用中文写的,所以存下来,这是链接。
还有一个,这是链接。
####
## 10月18日 - 函数定义直接使用等号。参数可以是任何类型。 - 由于优先级问题,参数不能写 -x ,必须加括号,封成一维向量。 - 函数中可以使用分支语句。 if conditional then truePart else falsePart - 三个关键字都为必要关键字 case exp of Pattern1 -> action1 Pattern2 -> action2 _ -> else_action 还有参数匹配。(Reference card) Function pattern matching f [ ] = 0 f [x] = 1 f _ = -1 Function conditionals (guards) (没懂这里的guards) f x | x == [] = 1 | length x == 12 = 15 | otherwise = -1 - 函数定义开头可以使用 let var in expr 定义中间变量,也可以定义多个中间变量,多个中间变量必须有相同的缩进。(排版影响解析) - 对于二元运算,加圆括号能使运算符(如加号)作为函数名来使用,反之,加反撇号(`)能使函数名(如 map )作为运算符来使用。 - 注释。“--”表示行注释;“{-”和“-}”含着跨行的注释。跨行注释可嵌套。 + 缩进是语法的一部分,分多行的代码要注意。 - “<-”符号:name <- function ,运行 function ,结果命名为 name 。 - 用 const_value::Type 来明确某常量的类型。 - Haskell 函数可以分为“函数”和“过程”,函数的类型与其返回值类型相同,过程的类型不同于其返回值类型,所以,在过程中,不可以直接将过程名作为其值使用。 - ####
## 10月17日
- 大小写敏感。
- =号表示概念的定义,用时才展开。(“lazy”)
* 类型名以大写字母开头,函数或变量名以小写字母开头。
* 因为是 functional language ,所以对于一个定义明确的函数,haskell 消除了输出反过来影响该函数进程的副作用。
- 直接以向量的形式表达定元一维数组,各维度的值可以是各种类型(允许欠套)。特别地,二维向量(pair)可以使用 fst 和 snd 函数。
也可以说,定元的数组用圆括号表示。
- 传统的 list 用以容纳变元的数组,haskell 用方括号表示,叫变元数组(暂称 list )。可以定义/使用空 list 。list 的各元素类型必须相同(允许欠套)。
冒号用以向 list 添加元素,[] 记法必须在最后,前面可以有任意个元素加入。使用 length 、head 和 tail 函数进行操作。
双加号用以连接两个 list 。
- 字符串就是元素为字符的变元数组,可以直接当作变元数组使用。
+ 定元与变元数组可以互相嵌套。
函数运算优先级高于冒号运算和数学运算。(除括号运算外最高?)
三维或更高维度的向量没法操作?(不能用 fst 函数对,也不能用 length 函数组。)
- 简单列表函数:
映射:map 映射函数 变元数组
过滤:filter 过滤条件函数 变元数组
××:foldr 某种二元运算符 值 变元数组
××:foldl ... ... ...
运算符要加括号,优先级所限。(此函数对可能有十分重要的用处,又或者衍生出特别的用法。)
* [修] 加括号是为了把运算符封装成一元向量,使其可以作为函数参数。(可以用多元向量否?)
* [修] 加括号是为了把运算符封装成函数,使其可以作为函数参数。 如果 foldl 用在无穷元数组,会死锁;而 foldr 可能可以产生返回值。(需要看后面才知道) - |
|
|