Skip to content

Instantly share code, notes, and snippets.

@redswallow
Forked from jackywyz/hs.md
Created December 26, 2011 22:35
Show Gist options
  • Save redswallow/1522228 to your computer and use it in GitHub Desktop.
Save redswallow/1522228 to your computer and use it in GitHub Desktop.
Haskell练习

###haskell 基本类型,bool,list,tuple,int,Integer,Double,char ,string,function

  1. 编译运行
  • runhaskell hell.hs
  • ghc --make helloworld and then ./helloworld
  1. let/in 在函数的应用
root s = 
    let p = 2
    in s*p 
  1. List类型
  • s = [1,2,3] ; s!!0 取值
  • 函数 null ,length, head,tail,last,init,sum,product,reverse,take,drop,maximum,elem,
  • a = [1..2],coffescript 也有
  • 没有map,有Map.fromList [(5,'a'), (3,'b')] ! 5
  1. 匿名函数 (\a->a*2) 2

  2. 没有循环,用迭代代替,[a | a <- [1,2,3]]

  3. 门卫

 f a b 
  |a==b = b
  |a > b = a

5.curry

  • f a b = a+b
  • g (a,b) = a+b
  • f = curry g
  • g = uncurry f

6.".lhs" 文件以">"符号开始代码,其他的都是注释。

7.关键字

  • data
  • type
  • newtype 是临时的data定义
  • class
  • instance
  • qualified

8.classes ,继承

class  (Eq a) => Ord a  where
   compare              :: a -> a -> Ordering
   (<), (<=), (>=), (>) :: a -> a -> Bool
   max, min             :: a -> a -> a

##Chapter 9

In / Out

  • unwords :: [String]->String
  • words :: String -> [String]
  • print,putStrLn等函数只能放在main中执行
  • if true then 1 else 2;
  • IO action 中 when 函数 : when True $ do $ something
  • 要认为putStrLn 输入一个串,输出一个IO action,当这个action执行的时候,输出结果到屏幕上。
  • do 语句不能嵌套其他的action。 * 使用let时可不写in。 = 不相等

Files and streams

  • lines :: String->[String] , 对"\n"换行
  • unlines ::[String]->String,对"\n"换行
  • interact:: (String->String)->IO () ,通过喂给一个函数类型参数处理输入得到的串,输出期望的结构到屏幕

####模块的导出

module ModName(模块名) where
data  T1 .....
data  T2 .....
func1 ....
func2 ....`

模块名要与包含模块的文件名(出去扩展名.hs)相同。 可以控制模块被导入后,别人能够使用的内容。格式如下 如果你不想fuc2被使用,则可以这样写:

module ModName (
  T1,
  T2,
  func1)
 where
data Card = Card Suit Face
data Suit = Hearts
 | Spades
 | Diamonds
 | Clubs
data Face = Jack
 | Queen
 | King
 | Ace
 | Number Int

如果在你的模块中有如上的自定义数据类型。如果你不希望别人使用其中的构造器(constructors),你可以这样写:

module ModName ( Card(),
   Suit(),
   Face())
 where

如果你希望别人使用其中的构造器(constructors),可以这样写:

module ModName ( Card(..),
   Suit(..),
   Face(..))
 where

".."代表了所有的构造器(constructors)。

####模块的导入 使用方式: import ModName 当导入模块后,你就可以直接使用模块里面的函数与数据类型了。比如在上一个Mod里面定义的func1,你可以直接用func1 .... 或者 ModName.func1 ....。使用后面的方式可以避免一些不必要的混淆,比如你在现在的模块里又定义了一个func1。 import qualified ModName 强制使用ModName.func1 ....,否则报错。 调用模块中特定的函数或类型 import ModName (func1) 那么在目前的文件中你只能使用ModName的func1,其他的你不能使用。

不调用模块中特定的函数或类型 import ModName hiding (func1) 那么在目前的文件中你不能使用ModName的func1,其他的你都可以使用。 重命名模块: 如果模块的名字太长了,这样做很有用。 import (qualified) ModName as AnotherName 以上介绍的导入方式可以混合使用。

####Hierarchical Imports 分级导入 并非haskell标准规定的。它的意义是一个在haskell调用目录下可以搜索模块的功能。 eg: E:\Haskell是haskell的编译路径。你的模块Mos1,在E:\Haskell\Mos下。那么你可这样导入模块Mos1,而不用担心编译报告找不到模块。 import Mos.Mos1 第一个Mos是文件夹的名字。

{-
Monad是一种规范,它作用于任何类型,只为在类型
的上面添加一个“构造器”用于标示某些状态。这些状态是不可以被“洗掉”的,这样就
很明显地指示出了程序的状态特征,就像是“指示剂”一样地明确,另外,它是一个编译
时的东西,对运行时完全没有效率影响!
-}
newtype Reader e a = Reader { runReader :: (e -> a) }
instance Monad (Reader e) where -- 这里的(Reader e是类型构造器)
return a = Reader $ \e -> a
(Reader r) >>= f = Reader $ \e -> runReader (f (r e)) e
class MonadReader m e | e -> m where
ask :: m e
local :: (e -> e) -> m a -> m a
{-
ghci -XMultiParamTypeClasses -XFunctionalDependencies -X
FlexibleInstances
-}
instance MonadReader (Reader e) e where
ask = Reader id
local f c = Reader $ \e -> runReader c (f e)
reader :: Reader String Int
reader = do
x <- Reader $ \e -> length(e)
return x

###两个冒号 :: 符号 :: 读作 ” 具有类型”, 比如 x :: y 形式的内容 就应该理解成: 表达式x具有类型y 有几种情况, 一种是 函数定义,冒号前是 函数名称, 冒号后 是函数的输入输出的类型定义 另外就是 列表的类型定义, 如 [1,2,3,4,5] :: [Int] 表示 [1,2,3,4,5] 是一个Int类型的列表 ['a', 'a', 'b'] :: String 表示 一个字符列表, 也就是字符串

很多时候都不需要进行类型声明。那是因为 Haskell 可以暗中推断,不必声明之。也就是说,如果省略 :: 和后面的类型,Haskell编译器会推断这个表达式的类型。

###两横 (两个减号) – 单行注释

###大括号和减号 {- -} 括起来的部分都是注释, 前括号 和 后括号 可以放在不同的行

###右箭头 -> 定义函数的输入输出的类型, 也就是定义函数的类型 1)如果只有输入, 没有输出, 就没有箭头 2) 有多个输入参数, 依次用 -> 相连, 最后一个才是 输出

另外,在case表达式中, 连接模式和相应的结果, 如

firstDigit :: String -> Char firstDigit st = case (digits st) of [] -> '\0' (x:_) -> x 一个case 表达式可用于区分不同的分支选择, 在上面这个例子中,是空列表和非空列表

###左箭头 <- 属于, 在list comprehension中, 表示一个元素 属于 哪个列表 (集合) 这个跟数学符号 表示 元素属于哪个集合,是一样的意思

另外一种用法 类似于 赋值 do { n <- readLn ; print (n^2) } 这段代码,就是将从标准输入读取到的数字 赋值给 n, 然后打印出n的平方 但是只限于 IO,Maybe,[] action, 对于普通的函数, 必须用 let = 来赋值

###vertical bar符号 | 竖线符号, 这个符号,在C语言中表示按位与 在Haskell中表示 guide 用来在函数中表示不同的情况, 有点类似与C语言的switch case的意思 比如,求两整数之间的最大值

max :: Int -> Int -> Int max x y --max是函数名, x y是两个形式参数 | x >=y = x --表示如果x>=y 这个条件成立,最大值就等于x | otherwise = y guide是模式匹配的扩展, 可以将竖线读作 ” 当…时”

另外,在list comprehension中, 竖线 分割列表的定义和描述 设列表ex为[2, 4, 7] 则列表概括 [2*n | n <- ex] 表示列表 [4, 8, 14]

第3种情况,就是用data定义数据时用来枚举不同的情况

data Maybe a = Nothing | Just a data Either a b = Left a | Right b

data Color = Red | Orange | Yellow | Green | Blue | Purple | White | Black 它与guide的区别是,第1种情况的前面,不需要 | 符号, 而guide的每种情况都需要

###元组 (,,) 括号里有若干个用逗号分割开的元素 这个概念跟python中的tuple是差不多的

###列表 [,,] 方括号中包含 若干个逗号分割开的元素 跟python中list也是差不多的

++ 列表的连接, 比如 “Hello” ++ “World” 的结果是 “HelloWorld”

=> (Eq a) => a -> a -> Bool 它左边的部分叫做类型约束(type constraint), 有人把 类型约束叫做 context

下划线 _ 通配符, 在模式匹配中, 可以与任何参数都匹配 比如 x:_ 匹配非空列表

###单冒号 : 在列表中,表示连接, 如 3:[] = [3] 以及 2:[3] = [2,3] 冒号称之为cons运算

###反引号 英文中叫做gave accent, 或者backquote , backtick. 键盘中中, 它在 ~ 符号的下面, 也就是按住shift会输入~, 不按shift就会输入

###两个感叹号 !! 取列表的下标元素 如 let x = [abcdefg] x!!6 就为’g’

###关键字 case class data default deriving 这个不常见 do else if import in infix 这3个也不常见 infixl infixr instance let module newtype of then type where

这样说来,关键字是没有多少的

###单引号’

  1. 用单引号将单个字符括起来, 这与C语言中的语法是一致的

  2. 单个单引号是函数名的一部分, 比如 foldl’

作为高阶函数参数的函数通常命名冠以 f、g 等,但有时也像类型变量后门带一些数字进行修饰那样, 函数名后面会带单引号 ‘ 进行修饰 ,例如像 g’ 在以后的例子中,你可以把它读作 “Jee-prime”,并且它被认为是一个与 g 函数有特定关系(a helper or the like)的函数。

比如

foldl — 从左到右遍历它的结构 foldl’ — a fold that is strict in its accumulator, “‘” is used to indicate a strict variant of a function

###点号.

函数复合

(f.g) 3

###美元符号 $

函数应用 function application

在标准库Prelude中, ($)的定义为

f $ x = f x

这个函数的优先级非常低, 这意味着它可以用来代替括号

foo x y = bar y (baz (fluff (ork x)))

可以改写成

foo x y = bar y $ baz $ fluff $ ork x

这基本上类似于 函数复合 点号的语法 ###反斜线
匿名函数,lamda函数 例如 \xs -> length xs > 15 就定义了一个lamda函数,它的参数是xs, 左右就是判断 xs的长度是否大于 15

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment