在终端输入 g h c i ghci ghci开始运行haskell程序。
创建文件 b a b y . h s baby.hs baby.hs,包含以下函数:
doubleMe x = x + x
doubleUs x y = x*2 + y*2
doubleSmallNumber x = if x > 100
then x
else x*2
列表,用[ , , ,]表示。
lostNumbers = [4,8,15,16,23,42]
++
和:
可以作为列表的连接符
++
连接两个列表,而:
连接一个列表和一个字符
ghci> [1,2,3,4] ++ [9,10,11,12]
[1,2,3,4,9,10,11,12]
ghci> "hello" ++ " " ++ "world"
"hello world"
ghci> ['w','o'] ++ ['o','t']
"woot"
如果要按索引从列表中获取元素,使用!!
ghci> [1,2,3,4,5] !! 1
2
head
接受一个列表,并返回头部
ghci> head [5,4,3,2,1]
5
tail
接受一个列表,并返回除去第一个元素的列表
ghci> tail [5,4,3,2,1]
[4,3,2,1]
last
接受一个列表,并返回最后一个元素
ghci> last [5,4,3,2,1]
1
init
接受一个列表,并返回除去最后一个元素的列表
ghci> init [5,4,3,2,1]
[5,4,3,2]
length
接受一个列表,并返回它的长度
ghci> length [5,4,3,2,1]
5
null
接受一个列表,如果为空,则返回True,否则返回False
ghci> null [1,2,3]
False
ghci> null []
True
reverse
接受一个列表,返回翻转后的列表
ghci> reverse [5,4,3,2,1]
[1,2,3,4,5]
take
接受一个数x以及一个列表,返回该列表前x个数
ghci> take 3 [5,4,3,2,1]
[5,4,3]
ghci> take 1 [3,9,3]
[3]
ghci> take 5 [1,2]
[1,2]
ghci> take 0 [6,6,6]
[]
drop
接受一个数x以及一个列表,返回去除前x个数的列表
ghci> drop 3 [8,4,2,1,5,6]
[1,5,6]
ghci> drop 0 [1,2,3,4]
[1,2,3,4]
ghci> drop 100 [1,2,3,4]
[]
maximun
和minimum
接受一个列表,返回最大值/最小值
ghci> minimum [8,4,2,1,5,6]
1
ghci> maximum [1,9,2,3,4]
9
sum
接受一个数字列表并返回它们的总和。
elem
接受一个事物和一个事物列表,并告诉我们该事物是否是列表中的一个元素。
ghci> 4 `elem` [3,4,5,6]
True
ghci> 10 `elem` [3,4,5,6]
False
样例:
ghci> [1..20]
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
ghci> ['a'..'z']
"abcdefghijklmnopqrstuvwxyz"
ghci> ['K'..'Z']
"KLMNOPQRSTUVWXYZ"
等差数列Range
ghci> [2,4..20]
[2,4,6,8,10,12,14,16,18,20]
ghci> [3,6..20]
[3,6,9,12,15,18]
cycle
接受一个列表并返回一个无限循环列表
ghci> take 10 (cycle [1,2,3])
[1,2,3,1,2,3,1,2,3,1]
ghci> take 12 (cycle "LOL ")
"LOL LOL LOL "
如果我们想要得到集合 S = { 2 ⋅ x ∣ x ∈ N , x ≤ 10 } S=\{2 \cdot x|x\in N,x\le 10\} S={2⋅x∣x∈N,x≤10},我们可以用haskell这样表示:
ghci> [x*2 | x <- [1..10]]
[2,4,6,8,10,12,14,16,18,20]
一个简易的计算列表长度的函数:
length' xs = sum[1 | _ <- xs]
与列表不同,元组可以包含多种类型的组合。元组用 ( ) () ()表示,它的组成部分用逗号分隔。
fst
接受一个pair(双元素元组)并返回其第一个元素。
ghci> fst (8,11)
8
ghci> fst ("Wow", False)
"Wow"
snd
接受一个pair并返回它的第二个元素。
ghci> snd (8,11)
11
ghci> snd ("Wow", False)
False
zip
需要两个列表,然后通过将匹配的元素成对连接,将它们压缩到一个列表中。
ghci> zip [1,2,3,4,5] [5,5,5,5,5]
[(1,5),(2,5),(3,5),(4,5),(5,5)]
ghci> zip [1 .. 5] ["one", "two", "three", "four", "five"]
[(1,"one"),(2,"two"),(3,"three"),(4,"four"),(5,"five")]
##1.6 一个简单的例子
我们想要找出边长为整数,周长为24的直角三角形。
ghci> let rightTriangles' = [ (a,b,c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2, a+b+c == 24]
ghci> rightTriangles'
[(6,8,10)]
使用:t
可以检查元素的类型。
ghci> :t 'a'
'a' :: Char
ghci> :t True
True :: Bool
ghci> :t "HELLO!"
"HELLO!" :: [Char]
ghci> :t (True, 'a')
(True, 'a') :: (Bool, Char)
ghci> :t 4 == 5
4 == 5 :: Bool
函数也有类型。在编写我们自己的函数时,我们可以选择给它们一个显式类型声明。
removeNonUppercase :: [ Char ] -> [ Char ]
removeNonUppercase st = [ c | c <- st, c `elem` [ 'A' .. 'Z' ]]
removeNonUppercase
函数获取Char列表,并除去非大写字母。
addThree :: Int -> Int -> Int -> Int
addThree x y z = x + y + z
addThree
接收三个Int
元素,并返回一个Int
元素。
注意:
Int
和Integer
都表示整数,Integer
的范围更大。此外还有Float
、Double
、Bool
、Char
等原始数据。
:t
获取函数的类型ghci> :t (==)
(==) :: (Eq a) => a -> a -> Bool
这里的==
是一个函数。
=>
符号之前的所有内容都称为类约束。
Eq
用于支持相等测试的类型
Ord
用于具有排序的类型。
Show
把元素转换成字符串。
这些都是函数类。
ghci> show 3
"3"
ghci> show 5.334
"5.334"
ghci> show True
"True"
Read
把字符串转换为元素。
ghci> read "True" || False
True
ghci> read "8.2" + 3.8
12.0
ghci> read "5" - 2
3
ghci> read "[1,2,3,4]" ++ [3]
[1,2,3,4,3]
sayMe :: (Integral a) => a -> String
sayMe 1 = "One!"
sayMe 2 = "Two!"
sayMe 3 = "Three!"
sayMe 4 = "Four!"
sayMe 5 = "Five!"
sayMe x = "Not between 1 and 5"
当我们输入sayMe 1
的时候,就会输出One!
。当我们输入sayMe 1
的时候,就会输出Two!
。以此类推。当我们输出的数不是1,2,3,4,5时,就会输出Not between 1 and 5
。
再来看下一个例子:
head' :: [a] -> a
head' [] = error "Can't call head on an empty list, dummy!"
head' (x:_) = x
head'
方法返回一个列表的首元素。_
是通配符,表示匹配任意个任意元素。
length' :: (Num b) => [a] -> b
length' [] = 0
length' (_:xs) = 1 + length' xs
该函数使用递归的写法,返回列表的长度。
sum' :: (Num a) => [a] -> a
sum' [] = 0
sum' (x:xs) = x + sum' xs
该函数使用递归的写法,返回列表元素的和。
使用Guard
可以代替if-else
语句。
luckySum :: (Integral a) => a -> a -> a -> String
luckySum x y z
| x+y+z == 21 = "lucky total 21"
| x+y+z > 10 && x+y+z < 21 = "getting closer"
| otherwise = "not that lucky"
--等同于if-elseif-else
用Guard
实现比较函数
myCompare :: (Ord a) => a -> a -> Ordering
myCompare a b
| a > b = GT
| a == b = EQ
| otherwise = LT
ghci> 2 `myCompare` 3
LT
将函数用反引号``括起来可以使用中缀表达式。
使用case同样可以创造分支语句。
head' :: [a] -> a
head' [] = error "No head for empty lists!"
head' (x:_) = x
-- 相同
head' :: [a] -> a
head' xs = case xs of [] -> error "No head for empty lists!"
(x:_) -> x
case
的表达式长这样:
case expression of pattern -> result
pattern -> result
pattern -> result
...
case
不同于Guard
,可以局部使用。
describeList :: [a] -> String
describeList xs = "The list is " ++ case xs of [] -> "empty."
[x] -> "a singleton list."
xs -> "a longer list."
使用where
可以定义变量并给变量赋值。
luckySum :: Int -> Int -> Int -> String
luckySum x y z
| sum == best = "lucky total " ++ show best
| sum > lowLimit && sum < best = "getting closer"
| otherwise = "not that lucky"
where sum = x+y+z
best = 21
lowLimit = 10
使用let
也可以定义变量,但是有作用范围。
cylinder :: (RealFloat a) => a -> a -> a
cylinder r h =
let sideArea = 2 * pi * r * h
topArea = pi * r ^2
in sideArea + 2 * topArea
let
的使用方式是let <bindings> in <expression>
。
let
还可以使用类似c语言的宏定义,where
也可。
ghci> [let square x = x * x in (square 5, square 3, square 2)]
[(25,9,4)]
一个用递归实现的查找列表最大值函数。
maximum' :: (Ord a) => [a] -> a
maximum' [] = error "maximum of empty list"
maximum' [x] = x
maximum' (x:xs) = max x (maximum' xs)
快速排序的haskell实现。
quicksort :: (Ord a) => [a] -> [a]
quicksort [] = []
quicksort (x:xs) =
let smallerSorted = quicksort [a | a <- xs, a <= x]
biggerSorted = quicksort [a | a <- xs, a > x]
in smallerSorted ++ [x] ++ biggerSorted
是不是非常简洁呢~
使用递归函数时,先定义终止条件,再定义子结构。
由于递归在之前学习算法的时候被用到过很多次,所以这里就不再赘述了。
让我们首先看一个例子。
ghci> max 4 5
5
ghci> (max 4) 5
5
这里,max a b
返回两者中的最大值。我们也可以把(max 4)
看做一个函数,它接受一个数,并返回该数和4的最大值。
函数max :: (Ord a) => a -> a -> a
可以看做一个函数,它接受一个数,并返回一个函数f
。这个函数f
接受一个数,并返回一个数。所以这个表达式意义和max :: (Ord a) => a -> (a -> a)
相同。
某些中缀函数可以写成部分形式。
divideByTen :: (Floating a) => a -> a
divideByTen = (/10)
isUpperAlphanum :: Char -> Bool
isUpperAlphanum = (`elem` ['A'..'Z'])
函数可以将函数作为参数,也可以返回函数
applyTwice :: (a -> a) -> a -> a
applyTwice f x = f (f x)
该函数第一个参数是一个函数,第二个参数是相同的a。它能将函数f
应用两次。
ghci> applyTwice (+3) 10
16
ghci> applyTwice (++ " HAHA") "HEY"
"HEY HAHA HAHA"
ghci> applyTwice (3:) [1]
[3,3,1]
我们将实现另一个已经在标准库中的函数,称为flip
。flip
简单地接受一个函数并返回一个函数。
flip' :: (a -> b -> c) -> (b -> a -> c)
flip' f = g
where g x y = f y x
-- (a -> b -> c) -> (b -> a -> c)等价于 (a -> b -> c) -> (b -> a -> c),所以这样写也可以:
flip' :: (a -> b -> c) -> b -> a -> c
flip' f y x = f x y
ghci> flip' zip [1,2,3,4,5] "hello"
[('h',1),('e',2),('l',3),('l',4),('o',5)]
map
接受一个函数和一个列表,并将该函数应用于列表中的每个元素,生成一个新列表。
map :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x:xs) = f x : map f xs
filter
接受一个返回bool的函数和一个列表,并将该函数应用于列表中每个元素,当值为False则被筛出。
filter :: (a -> Bool) -> [a] -> [a]
filter _ [] = []
filter p (x:xs)
| p x = x : filter p xs
| otherwise = filter p xs
ghci> filter (>3) [1,5,3,2,1,6,4,3,2,1]
[5,6,4]
ghci> filter (==3) [1,2,3,4,5]
[3]
利用filter
可以重写quicksort
函数。
quicksort :: (Ord a) => [a] -> [a]
quicksort [] = []
quicksort (x:xs) =
let smallerSorted = quicksort (filter (<=x) xs)
biggerSorted = quicksort (filter (>x) xs)
in smallerSorted ++ [x] ++ biggerSorted
利用filter
找到1~100000的整数中被3829整除的最大的数。
largestDivisible :: (Integral a) => a
largestDivisible = head (filter p [100000,99999..])
where p x = x `mod` 3829 == 0
takeWhile
函数接受一个bool函数以及一个列表。takeWhile
对列表中每个元素进行函数操作,当返回True时加入列表,当返回False时停止。
eg:计算小于10000的奇数的平方和
ghci> sum (takewhile (<10000) (filter odd (map(^2) [1..])))
用集合语言表示,即为
ghci> sum( takeWhile (<10000) [n^2 | n <- [1..], odd (n^2)])
我们使用\
表示匿名函数,后面些参数,用空格分隔。之后是->
和函数体。
一个简单的Lambda函数例子:
ghci> map (\(a,b) -> a + b) [(1,2),(3,5),(6,3),(2,6),(2,5)]
[3,8,9,8,7]
这里,(\(a,b) -> a + b)
就是一个匿名函数。它把一个Pair的两个值相加,然后返回。
使用匿名函数同样可以实现flip
。
flip' :: (a -> b -> c) -> b -> a -> c
flip' f = \x y -> f y x
这里的\x y -> f y x
是一个Lambda函数。它接受两个参数,并返回一个函数。
foldl
函数接受一个[二元函数]、一个[初始值]、一个[要折叠的列表]。[二元函数]本身有两个参数。使用[累加器]和第一个(或最后一个)元素调用[二元函数],并生成一个新的[累加器]。然后,使用新的[累加器]和现在新的第一个(或最后一个)元素再次调用[二元函数],依此类推。一旦我们遍历了整个列表,就只剩下[累加器]了,这就是我们将列表简化为的内容。
sum' :: ( Num a) => [a] -> a
sum' xs = foldl(\acc x -> acc + x) 0 xs
ghci> sum' [3, 5, 2, 1]
11
foldr
函数和foldl
函数相似。只不过是从列表的最右边开始,并且累加器放在右边。
map' :: (a -> b) -> [a] -> [b]
map' f xs = foldr (\x acc -> f x: acc) [] xs
这里空列表[]
作为初始值。每次将得到的x
连接到累加器acc
左边。
符号$
具有[右结合性],相当于括号。这个符号优先级很低,能够减少工作量。
sqrt (3 + 4 + 9)
--equals
sqrt $ 3 + 4 + 9
sum( filter (>10) (map (*2) [2..10]))
--equals
sum $ filter (>10) $ map (*2) [2..10]
读这类式子时,只需要记住$的优先级最低,最后读即可。
复合函数
符号.
相当于复合函数的连接符。
f(g(z x))
相当于(f.g.z) x
。
Haskell中的模组就是java
中的包,c++
中的头文件。我们用import <module name>
导包。
import Data.List
numUniques :: (Eq a) => [a] -> Int
numUniques = length . nub
我们可以通过在包后面加括号()
实现导入部分功能。
import Data.List (nub, sort)
我们可以利用hiding
选择不导入某些功能。
import Data.List hiding (nub)
--不导入nub
当我们使用一个有[命名冲突]的函数时(比如我们导入了Data.Map
包,然后想使用filter
函数。由于有多个filter
函数,故产生了冲突。)这个时候我们可以用qualified
来制定我们用哪个包。
import qualified Data.Map as M
as
可以给包起别名,从而简化操作
本词条省略。书上有。懒得写了/
module Geometry
( sphereVolume
, sphereArea
, cubeVolume
, cubeArea
, cuboidArea
, cuboidVolume
) where
sphereVolume :: Float -> Float
sphereVolume radius = (4.0 / 3.0) * pi * (radius ^ 3)
sphereArea :: Float -> Float
sphereArea radius = 4 * pi * (radius ^ 2)
cubeVolume :: Float -> Float
cubeVolume side = cuboidVolume side side side
cubeArea :: Float -> Float
cubeArea side = cuboidArea side side side
cuboidVolume :: Float -> Float -> Float -> Float
cuboidVolume a b c = rectangleArea a b * c
cuboidArea :: Float -> Float -> Float -> Float
cuboidArea a b c = rectangleArea a b * 2 + rectangleArea a c * 2 + rectangleArea c b * 2
rectangleArea :: Float -> Float -> Float
rectangleArea a b = a * b
构造一个集合模组
在程序中通过import Geometry
,以及再控制台中通过:m Geometry
导入。
通过data
关键字,我们可以自定义数据类。
data Bool = False | True
data Int = -2147483648 | -2147483647 | ... | -1 | 0 | 1 | 2 | ... | 2147483647 -
|
代表或。用于声明数据的范围,或者枚举类型。
data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
data Shape = Circle Float Float Float | Rectangle Float Float Float Float
这种方式声明了两个类:Circle
和Rectangle
。它们的类型如下:
ghci> :t Circle
Circle :: Float -> Float -> Float -> Shape
ghci> :t Rectangle
Rectangle :: Float -> Float -> Float -> Float -> Shape
利用Shape
类及其子类,我们可以写出计算其面积的函数。
surface :: Shape -> Float
surface (Circle _ _ r) = pi * r ^ 2
surface (Rectangle x1 y1 x2 y2) = (abs $ x2 - x1) * (abs $ y2 - y1)
(Circle _ _ r)
是Circle
的构造函数。
ghci> let circle1 = Circle 1 1 3
ghci> let rec1 = Rectangle 1 1 3 3
ghci> surface circle1
28.274334
我们可以在类定义后面加上deriving (XXX)
,表示作为XXX
的子类。
data Shape = Circle Float Float Float | Rectangle Float Float Float Float deriving (Show)
我们可以定义Point
类,来简化Shape
类。
data Point = Point Float Float deriving (Show)
data Shape = Circle Point Float | Rectangle Point Point deriving (Show)
这时,它们的构造函数应该这样写:
ghci> (Circle (Point 1 1) 1)
我们可以在模组中加入自定义数据类型。
module Shapes
(Point(..)
,Shape(..)
,surface
)where
--
(..)
表示其为一个数据类型。换句话说,就是结构体。
data Person = Person String String Int Float String String deriving (Show)
--equals
data Person = Person { firstName :: String
, lastName :: String
, age :: Int
, height :: Float
, phoneNumber :: String
, flavor :: String
} deriving (Show)
这样,我们就能通过成员变量名访问成员变量的值。
ghci> let guy = Person "Buddy" "Finklestein" 43 184.2 "526-2928" "Chocolate"
ghci> firstName guy
"Buddy"
ghci> height guy
184.2
ghci> flavor guy
"Chocolate"
并且通过成员变量名赋值。
data Car = Car {company :: String, model :: String, year :: Int} deriving (Show)
ghci> Car {company="Ford", model="Mustang", year=1967}
Car {company = "Ford", model = "Mustang", year = 1967}
我们可以使用deriving(a,b,c,...)
继承多个类。
data Person = Person { firstName :: String
, lastName :: String
, age :: Int
} deriving (Eq, Show, Read)
这样,我们便能对Person
实例进行read
和show
操作。注意,进行read
操作时需要指明转换类型。通过::
表示。
ghci> let mikeD = Person {firstName = "Michael", lastName = "Diamond", age = 43}
ghci> mikeD
Person {firstName = "Michael", lastName = "Diamond", age = 43}
ghci> "mikeD is: " ++ show mikeD
"mikeD is: Person {firstName = \"Michael\", lastName = \"Diamond\", age = 43}"
ghci> read "Person {firstName =\"Michael\", lastName =\"Diamond\", age = 43}" :: Person
Person {firstName = "Michael", lastName = "Diamond", age = 43}
我们可以通过type
为类型起别名,就像c++
中的typedef
。
type AssocList k v = [(k,v)]
type PhoneBook = [(String,String)]
type PhoneNumber = String
type Name = String
type PhoneBook = [(Name,PhoneNumber)]
简单二叉搜索树递归定义与插入、查找:
-- 定义
data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show, Read, Eq)
-- 创建实例
singleton :: a -> Tree a
singleton x = Node x EmptyTree EmptyTree
-- 插入
treeInsert :: (Ord a) => a -> Tree a -> Tree a
treeInsert x EmptyTree = singleton x
treeInsert x (Node a left right)
| x == a = Node x left right
| x < a = Node a (treeInsert x left) right
| x > a = Node a left (treeInsert x right)
-- 查找
treeElem :: (Ord a) => a -> Tree a -> Bool
treeElem x EmptyTree = False
treeElem x (Node a left right)
| x == a = True
| x < a = treeElem x left
| x > a = treeElem x right
EmptyTree
是一个Tree
类。它只有一个名字,没有任何成员函数和成员变量。
ghci> :t EmptyTree
Tree a
我们可以通过instance
创造一个类的实例。这个实例是一个函数。
data TrafficLight = Red | Yellow | Green
instance Eq TrafficLight where
Red == Red = True
Green == Green = True
Yellow == Yellow = True
_ == _ = False
上述代码相当于重写TrafficLight
中的equals
。
类似的,我们能够重写toString
(即Show
)
instance Show TrafficLight where
show Red = "Red light"
show Yellow = "Yellow light"
show Green = "Green light"
ghci> Red == Red
True
ghci> Red == Yellow
False
ghci> Red `elem` [Red, Yellow, Green]
True
如果要重写该数据的==
,我们可以这样写:
data BicycleType = ElectriBicycle | NormalBicycle deriving(Show, Eq, Read)
type BicycleNumber = Integer
type BicycleName = String
data Bicycle = Bicycle {
bicycleName :: BicycleName,
bicycleNumber :: BicycleNumber,
bicycleType :: BicycleType
} deriving(Show)
instance Eq Bicycle where
x == y = ((bicycleName x) == (bicycleName y) && (bicycleType x) == (bicycleType y))
对于Bicycle
类,两个实例相等只需要bicycleName
和bicycleType
相等即可。
我们利用class
创造一个函数类,然后用instance
实现实例函数。
-- 将一个元素转换为bool
class YesNo a where
yesno :: a -> Bool
-- 数值
instance YesNo Int where
yesno 0 = False
yesno _ = True
-- 列表
instance YesNo [a] where
yesno [] = False
yesno _ = True
class
的语法为class <classname> <parameter> where ...
Functor
类可以对map
进行改进,实现多态。它的抽象类接受一个函数,并把它应用于元素。
class Functor f where
fmap :: (a -> b) -> f a -> f b
instance Functor Tree where
fmap f EmptyTree = EmptyTree
fmap f (Node x leftsub rightsub) = Node (f x) (fmap f leftsub) (fmap f rightsub)
这里的[]
和Tree
是构造函数。
ghci> fmap (*2) EmptyTree
EmptyTree
ghci> fmap (*4) (foldr treeInsert EmptyTree [5,7,3,2,1,7])
Node 28 (Node 4 EmptyTree (Node 8 EmptyTree (Node 12 EmptyTree (Node 20 EmptyTree EmptyTree))))
如果直接对EmptyTree
使用map (*2)
,将会报错。而fmap
将不会。
对于初学者来说,最简单的程序应该是"Hello, world"。
main = putStrLn "hello, world"
putStrLn
用于在控制台输出字符串。
main = do
putStrLn "Hello, what's your name?"
name <- getLine
putStrLn ("Hey " ++ name ++ ", you rock!")
getLine
用于在控制台读取一行字符串。其值被复制到name
中。由于getLine
是一个IO操作,故要用<-
赋值。
在有d多个操作的地方(这些操作应包括IO操作),应该用do
加以修饰。
在haskell中,main
函数可以递归调用。
下述为重复读取一行字符串,直到字符串为空,返回一个空元组()
。
main = do
line <- getLine
if null line
then return ()
else do
putStrLn $ reverseWords line
main
reverseWords :: String -> String
reverseWords = unwords . map reverse . words
在haskell中,return
并不会中断整个程序,而是继续运行下去。
类似c语言,haskell也有print
和getChar
。一个用于输出到终端,一个用于从终端读入。.
通过getContents
可以读取文件中所有内容。
建立一个文本文件,并把它输出。
建立文本文件haiku.txt
I'm a lil' teapot
What's with that airplane food, huh?
It's so small, tasteless
建立读取文件capslocker.hs
import Control.Monad
import Data.Char
main = forever $ do
putStr "Give me some input: "
l <- getLine
putStrLn $ map toUpper l
在控制台编译并输出
$ ghc --make capslocker
[1 of 1] Compiling Main ( capslocker.hs, capslocker.o )
Linking capslocker ...
$ cat haiku.txt
I'm a lil' teapot
What's with that airplane food, huh?
It's so small, tasteless
$ cat haiku.txt | ./capslocker
I'M A LIL' TEAPOT
WHAT'S WITH THAT AIRPLANE FOOD, HUH?
IT'S SO SMALL, TASTELESS
capslocker <stdin>: hGetLine: end of file
通过管道运算符|
可以就应用该应用程序。
通过调用openFile
函数可以直接在应用程序中打开文件。运用这个函数需要导入System.IO包。
import System.IO
main = do
handle <- openFile "girlfriend.txt" ReadMode
contents <- hGetContents handle
putStr contents
hClose handle
openFile
的函数签名:openFile :: FilePath -> IOMode -> IO Handle
IOMode是一个枚举类型,它包含ReadMode | WriteMode | AppendMode | ReadWriteMode
。
利用函数hGetContents
,我们可以从openFile
中得到的IO handle来获取文件的内容。它返回一个字符串。
类似的,还有hGetLine
、hPutStr
、hPutStrLn
、hGetChar
等操作。
readFile
也可以用来读取文件。
import System.IO
main = do
contents <- readFile "girlfriend.txt"
putStr contents
haskell提供了writeFile :: FilePath -> String -> IO ()
来写入文件。
import System.IO
import Data.Char
main = do
contents <- readFile "girlfriend.txt"
writeFile "girlfriendcaps.txt" (map toUpper contents)