Q1
type Predicate a = a -> Bool
testP :: Bool
testP = (isPass [] == False) &&
(isPass [2, 5, 6, 7] == False) &&
(isPass [12, 15, 16, 17] == True) &&
(isPass [5, 8, 12, 15] == True)
isPass :: Predicate [Integer]
写一个函数isPass,如果列表中的和大于40就返回True,否则返回False
isPass = (>=40) . sum
-- 或
isPass ns = sum ns >= 40
这里可以直接用sum实现
>>> sum [1..10]
55
sum函数会返回一个列表的和
Q2
testAl :: Bool
testAl = (isAlphabet "" == True ) &&
(isAlphabet "hello!" == False) &&
(isAlphabet "hello" == True) &&
(isAlphabet "Hello" == True) &&
(isAlphabet "Wow3" == False) &&
(isAlphabet "Software" == True)
isAlphabet:: Predicate String
写一个函数isAlphabet,判断是否仅为为英文字母
isAlphabet = all (`elem` (['A'..'Z']++['a'..'z']))
使用all和`elem`,看一下all的解释
>>> all (> 3) [1,2]
False
all后面会跟一个函数和一个列表,如果列表中所有元素都符合函数的条件,就会返回True,否则返回False
>>> 3 `elem` [1,2,3,4,5]
True
elem则是会判断前面输入的值是否存在于后面的列表中
Q3
testcmbList :: Bool
testcmbList =
(cmbList ['s'] "comp" == "cs") &&
(cmbList "otae""sfwr" == "software") &&
(cmbList [1, 3, 5] [0, 2, 4, 6] == [0,1,2,3,4,5]) &&
(cmbList ["1", "2", "3"]["THE", "WORLD", "HEY"] ==
["THE","1","WORLD","2","HEY","3"])
cmbList :: [a] -> [a] -> [a]
写一个函数cmbList,将两个列表合并成一个列表,并且返回的列表中奇数位置的元素为第一个列表中的值,偶数位置的元素为第二个列表中的值(起始位置为0)
cmbList fs ss = concatMap tup2lst $ zip ss fs
where
tup2lst (x,y) = [x,y]
先把两个列表zip起来,zip的功能如下
>>> zip [1, 2] ['a', 'b']
[(1,'a'),(2,'b')]
会把两个列表中对应位置的元素合并成一个元组,再把所有元组放在一个列表中
tup2lst就是一个自己写的函数,会把元组转换成列表
concatMap会使用tup2lst这个函数,把所有元组先变成列表,然后再整合成一个列表
>>> concatMap (take 3) [[1..], [10..], [100..], [1000..]]
[1,2,3,10,11,12,100,101,102,1000,1001,1002]
以上面zip的例子为例:
[(1,'a'),(2,'b')]
-- tup2lst之后变成
[[1,'a'],[2,'b']]
-- 再concatMap变成
[1,'a',2,'b']
Q4
testcmbProd :: Bool
testcmbProd =
(cmbProd [] [5, 6] == []) &&
(cmbProd [2, 3, 4] [5, 6] == [10,18]) &&
(cmbProd [0.23, 3.4, 7.88, 9.21] [3.4, 5] == [0.782,17.0]) &&
(cmbProd [0.23, 3.4, 7.88, 2*0.3] [3.4, 1.3, 2.1, 2] ==
[0.782,4.42,16.548000000000002,1.2])
cmbProd :: Num x => [x] -> [x] -> [x]
实现函数cmbProd,将两个列表的乘积返回成一个列表,返回列表的长度应等于最短列表长度
cmbProd = zipWith (*)
可以直接用zipWith实现
>>> zipWith (+) [1, 2, 3] [4, 5, 6]
[5,7,9]
zipWith就是把函数应用到后面两个列表中,并返回一个列表
Q5
testsqDiff :: Bool
testsqDiff =
(sqDiff [] == []) &&
(sqDiff [4, 6] == []) &&
(sqDiff [6, 4] == [4]) &&
(sqDiff [4, 6, 3, 1, 8] == [9, 4])
sqDiff :: [Int] -> [Int]
实现一个函数sqDiff,返回列表中两个连续数字差的平方
必须满足一个条件:前一个数字必须大于后面的数字才会返回
sqDiff [] = []
sqDiff (x:xs) = [ (a - b) * (a - b) | (a,b) <- zip (x:xs) xs, a > b ]
第二行表达式的意思是在满足a>b的条件下,使用zip遍历整个列表
最后返回(a - b) * (a - b)
>>> zip [1, 2] ['a', 'b']
[(1,'a'),(2,'b')]
(a,b) <- zip (x:xs) xs,(x:xs)就是从列表的第一个元素开始遍历,对应a
xs从第二个元素开始,对应b
Q6
testM2int :: Bool
testM2int =
(maybe2int [] == 0) &&
(maybe2int [Just 23] == 23) &&
(maybe2int [Nothing] == 0) &&
(maybe2int [Just 2, Nothing, Just 3, Just 16, Nothing] == 21)
maybe2int :: [Maybe Int] -> Int
实现一个函数maybe2int,返回列表的和并且为Int型,Nothing不会影响结果
maybe2int xs = sum [x | (Just x) <- xs]
Q7
testcmb :: Bool
testcmb = (cmb "Hello" [] [] == []) &&
(cmb "Prod" [2, 3] [] == []) &&
(cmb "List" [] [2, 3] == []) &&
(cmb "Prod" [2, 3, 4] [5, 6] == [10,18]) &&
(cmb "List" [2, 3, 4] [5, 6] == [5,2,6,3]) &&
(cmb "Haskell" [2, 3, 4] [5, 6] == []) &&
(cmb "Prod" [2, 3, 4] [5, 6, 7] == [10,18,28]) &&
(cmb "List" [2, 3, 4] [5, 6, 7] == [5,2,6,3,7,4])
cmb :: Num a => String -> [a] -> [a] -> [a]
实现一个函数cmb
如果输入Prod就会返回两个列表的乘积
如果输入List就会合并两个列表,第一个列表在奇数位置,第二个列表在偶数位置
输入其他则返回空列表
cmb cmd fs ss = concatMap (f cmd) (zip ss fs)
where
f "List" (x, y) = [x, y]
f "Prod" (x, y) = [x * y]
f _ _ = []
三个元素,cmd fs ss
cmd代表输入的指令,fs和ss则代表两个列表
zip ss fs,把位置调一下,因为列表下标从0开始,所以应该第二个列表在前
f cmd,f是自己实现的函数,如果为List就[x,y],Prod就[x*y]其他就[]
并且把zip中的元组[(),()]改成了列表[[],[]]
concatMap实现完f cmd函数之后,再把[[],[]]大列表中的所有小列表整合成一个列表
Q8
data year1 = Student {name :: String, Chinese, Math, Eng :: Int}
s1Db :: [year1]
s1Db = [Student {name = "Beth", Chinese = 65, Math = 58, Eng = 79},
Student {name = "Adam", Chinese = 55, Math = 68, Eng = 61},
Student {name = "Lisa", Chinese = 60, Math = 72, Eng = 65},
Student {name = "Will", Chinese = 71, Math = 52, Eng = 49},
Student {name = "Mark", Chinese = 67, Math = 78, Eng = 50}]
8a)
test1MK :: Bool
test1MK = c1Mk s1Db == [("Beth",65),("Adam",55),("Lisa",60),
("Will",71),("Mark",67)]
c1Mk :: [year1] -> [(String, Int)]
实现一个函数c1MK,返回数据中所有学生的名字和语文成绩
the1Mk ss = [(name s, Chinese s) | s <- ss]
8b)
testMath:: Bool
testMath= tMath s1Db == [("Lisa",65,60),("Mark",50,67)]
tMath :: [year1] -> [(String, Int, Int)]
实现函数tMath,如果学生的Math成绩大于70,返回名字和Eng和Chinese的成绩,Eng在前
tMath ss = [(name s, Eng s, Chinese s) | s <- ss, Math s > 70]
8c)
testavg:: Bool
testavg = avgENG s1Db == 60.8
avgENG :: [year1] -> Float
返回ENG的平均分
avgENG ss = fromIntegral (sum gList) / fromIntegral (length gList)
where
gList = [ENG s | s <- ss]
fromIntegral :: (Integral a, Num b) => a -> b
将任意 Integral 值转为任意数字类型
gList就是对ENG进行遍历,再应用sum函数,得到列表的和
这里使用fromIntegral是因为要把Int值转换为Float