[Haskell] CIS 194: Homework 1-4

Validating Credit Card Numbers

• Double the value of every second digit beginning from the right.
That is, the last digit is unchanged; the second-to-last digit is doubled; the third-to-last digit is unchanged; and so on. For example,
[1,3,8,6] becomes [2,3,16,6].
• Add the digits of the doubled values and the undoubled digits from the original number. For example, [2,3,16,6] becomes
2+3+1+6+6 = 18.
• Calculate the remainder when the sum is divided by 10. For the
above example, the remainder would be 8.
If the result equals 0, then the number is valid.

doubleList    :: [Integer] -> [Integer]
doubleList  []          =   []
doubleList  (x:[])      =   [x]
doubleList  (x:(y:zs))  =   (2*x):y:doubleList zs

addDigits   ::  [Integer]   ->  Integer
addDigits   []      =   0
addDigits   (x:zs)  =   getDigits x+addDigits zs

getDigits   ::  Integer     ->  Integer
getDigits   n
    |   n < 0     =     0
    |   n < 10    =     n
    |   otherwise    =     mod n 10 + getDigits (div n 10)

isValid     ::  Integer     -> Bool
isValid     n   =   (mod n 10)==0


main = do
    print(doubleList [1,3,8,6])
    print(addDigits [2,3,16,6])
    print(isValid  18)

HomeWork 1

Exercise 1

Exercise 1 We need to first find the digits of a number. Define the
functions
toDigits :: Integer -> [Integer]
toDigitsRev :: Integer -> [Integer]
toDigits should convert positive Integers to a list of digits. (For 0 or
negative inputs, toDigits should return the empty list.) toDigitsRev
should do the same, but with the digits reversed.
Example: toDigits 1234 == [1,2,3,4]
Example: toDigitsRev 1234 == [4,3,2,1]
Example: toDigits 0 == []
Example: toDigits (-17) == []

toDigits    ::  Integer   ->  [Integer]
toDigitsRev ::  Integer   ->  [Integer]

toDigits   n
    |   n <  10 && n > 0   =     [n]
    |   n >= 10            =     toDigits (div n 10) ++ (mod n 10 : [])
    |   otherwise          =     []

toDigitsRev   n
    |   n <  10 && n > 0   =     [n]
    |   n >= 10            =     mod n 10 : toDigitsRev (div n 10)
    |   otherwise          =     []


main    =   do
            print(toDigits 1234)
            print(toDigitsRev 1234)

Exercise 2

Once we have the digits in the proper order, we need to
double every other one. Define a function
doubleEveryOther :: [Integer] -> [Integer]
Remember that doubleEveryOther should double every other number beginning from the right, that is, the second-to-last, fourth-to-last,
. . . numbers are doubled.
Example: doubleEveryOther [8,7,6,5] == [16,7,12,5]
Example: doubleEveryOther [1,2,3] == [1,4,3]

doubleList    :: [Integer] -> [Integer]
doubleList  []          =   []
doubleList  (x:[])      =   [x]
doubleList  (x:(y:zs))  =   (x):(2*y):doubleList zs

doubleEveryOther :: [Integer] -> [Integer]
doubleEveryOther n
           = reverse (doubleList  (reverse n))

main    =   do
            print(doubleEveryOther [8,7,6,5])

Exercise 3

Exercise 3 The output of doubleEveryOther has a mix of one-digit
and two-digit numbers. Define the function
sumDigits :: [Integer] -> Integer
to calculate the sum of all digits.
Example: sumDigits [16,7,12,5] = 1 + 6 + 7 + 1 + 2 + 5 = 22

sumDigits   ::  [Integer]   ->  Integer
sumDigits   []      =   0
sumDigits   (x:zs)  =   getDigits x+sumDigits zs

getDigits   ::  Integer     ->  Integer
getDigits   n
    |   n < 0     =     0
    |   n < 10    =     n
    |   otherwise    =     mod n 10 + getDigits (div n 10)

main    =   do
            print(sumDigits [16,7,12,5])

Exercise 4

Exercise 4 Define the function
validate :: Integer -> Bool
that indicates whether an Integer could be a valid credit card number. This will use all functions defined in the previous exercises.
Example: validate 4012888888881881 = True
Example: validate 4012888888881882 = False

sumDigits   ::  [Integer]   ->  Integer
sumDigits   []      =   0
sumDigits   (x:zs)  =   getDigits x+sumDigits zs

getDigits   ::  Integer     ->  Integer
getDigits   n
    |   n <= 0     =     0
    |   n < 10    =     n
    |   otherwise    =     mod n 10 + getDigits (div n 10)

isValid     ::  Integer     -> Bool
isValid     n   =   (mod (getDigits n) 10)==0

main    =   do
            print(isValid 4012888888881881)

Exercise 5

简单的汉诺塔问题

type Peg = String
type Move = (Peg,Peg)
hanoi :: Integer -> Peg -> Peg -> Peg -> [Move]
hanoi   n a b c
    | n==1  =   [(a,b)]
    | otherwise = (getHanoiMiddleResult (n-1) a b c) ++ [(a,b)] ++ (getHanoiMiddleResult (n-1) c a b)

getHanoiMiddleResult :: Integer -> Peg -> Peg -> Peg -> [Move]
getHanoiMiddleResult   n a b c
    |n>1       = (a,c) : getHanoiMiddleResult (n-1) a b c
    |otherwise = [(a,c)]

main    =   do
            print(hanoi 2 "a" "b" "c")

HomeWork 2

Exercise 1

不会做了就看看课件最后一段(;´д`)ゞ⊙﹏⊙∥

Exercise 1 The first step is figuring out how to parse an individual
message. Define a function
parseMessage :: String -> LogMessage
which parses an individual line from the log file. For example,
parseMessage “E 2 562 help help”
== LogMessage (Error 2) 562 “help help”
cis 194: homework 2 3
parseMessage “I 29 la la la”
== LogMessage Info 29 “la la la”
parseMessage “This is not in the right format”
== Unknown “This is not in the right format”
Once we can parse one log message, we can parse a whole log file.
Define a function
parse :: String -> [LogMessage]
which parses an entire log file at once and returns its contents as a
list of LogMessages.
To test your function, use the testParse function provided in the
Log module, giving it as arguments your parse function, the number
of lines to parse, and the log file to parse from (which should also be
in the same folder as your assignment). For example, after loading
your assignment into GHCi, type something like this at the prompt:
testParse parse 10 “error.log”
Don’t reinvent the wheel! (That’s so last week.) Use Prelude functions to make your solution as concise, high-level, and functional as
possible. For example, to convert a String like “562” into an Int, you
can use the read function. Other functions which may (or may not)
be useful to you include lines, words, unwords, take, drop, and (.).

{-# OPTIONS_GHC -Wall #-}
module LogAnalysis where
import Log

parseMessage :: String -> LogMessage
parseMessage str =
    let tokens = words str
    in 
        case tokens of
        ("E":level:time:rest) -> 
            LogMessage (Error (read level) (read time) (unwords rest))
        ("I":time:rest) ->
            LogMessage Info (read time) (unwords rest)
        ("W":time:rest) ->
            LogMessage Warning (read time) (unwords rest)
        _ -> Unknown (unwords tokens)

parse :: String -> [LogMessage]
parse input = 
    let line = lines input
    in
        map parseMessage line

Exercise 2

Define a function
insert :: LogMessage -> MessageTree -> MessageTree
which inserts a new LogMessage into an existing MessageTree, producing a new MessageTree. insert may assume that it is given a
sorted MessageTree, and must produce a new sorted MessageTree
containing the new LogMessage in addition to the contents of the
original MessageTree.
However, note that if insert is given a LogMessage which is
Unknown, it should return the MessageTree unchanged.

这里有个值得注意的地方:|guard没有注释掉的部分才是pattern match,guard得到的是Bool值,参数不能使用

insert :: LogMessage -> MessageTree -> MessageTree
insert (Unknown _) tree = tree
insert message Leaf     = Node Leaf message Leaf
insert message (Node left nodeMessage right) =
  let messageTime = getMessageTime message
      nodeMessageTime = getMessageTime nodeMessage
  in
    if messageTime < nodeMessageTime
    then Node (insert message left) nodeMessage right
    else Node left nodeMessage (insert message right)

-- insert message tree = 
--     case message tree of
--         (Unknown _) tree -> tree
--         LogMessage MessageType TimeStamp String Leaf ->
--             (Node Leaf message Leaf)
--         LogMessage (Node left nodeMessage right) -> 
--             let
--                 messageTime = getMessageTime nowMessage
--                 nodeTime = getMessageTime nodeMessage
--             in
--                 if messageTime < nodeTime
--                 then Node (insert message left) nodeMessage right
--                 else Node left nodeMessage (insert message right) 

-- insert message tree
--     |   Unknown tree = tree
--     |   LogMessage Leaf =
--             (Node Leaf message Leaf)
--     |   LogMessage (Node left nodeMessage right) = 
--             let
--                 messageTime = getMessageTime LogMessage
--                 nodeTime = getMessageTime nodeMessage
--             in
--                 if messageTime < nodeTime
--                 then Node (insert message left) nodeMessage right
--                 else Node left nodeMessage (insert message right) 

Exercise 3

Once we can insert a single LogMessage into a MessageTree,
we can build a complete MessageTree from a list of messages. Specifically, define a function
build :: [LogMessage] -> MessageTree
which builds up a MessageTree containing the messages in the list,
by successively inserting the messages into a MessageTree (beginning
with a Leaf).

build :: [LogMessage] -> MessageTree
build [] = Leaf
build (x:xs) = insert x (build xs)

Exercise 4

Finally, define the function
inOrder :: MessageTree -> [LogMessage]
which takes a sorted MessageTree and produces a list of all the
LogMessages it contains, sorted by timestamp from smallest to biggest.
(This is known as an in-order traversal of the MessageTree.)
With these functions, we can now remove Unknown messages and
sort the well-formed messages using an expression such as:
inOrder (build tree)
[Note: there are much better ways to sort a list; this is just an exercise to get you working with recursive data structures!]
Log file postmortem


inOrder :: MessageTree -> [LogMessage]
inOrder tree = 
    case tree of
        Leaf -> []
        (Node left node right) -> (inOrder left)++[node]++(inOrder right)

Exercise 5

Now that we can sort the log messages, the only thing
left to do is extract the relevant information. We have decided that
“relevant” means “errors with a severity of at least 50”.
Write a function
cis 194: homework 2 5
whatWentWrong :: [LogMessage] -> [String]
which takes an unsorted list of LogMessages, and returns a list of the
messages corresponding to any errors with a severity of 50 or greater,
sorted by timestamp. (Of course, you can use your functions from the
previous exercises to do the sorting.)
For example, suppose our log file looked like this:
I 6 Completed armadillo processing
I 1 Nothing to report
E 99 10 Flange failed!
I 4 Everything normal
I 11 Initiating self-destruct sequence
E 70 3 Way too many pickles
E 65 8 Bad pickle-flange interaction detected
W 5 Flange is due for a check-up
I 7 Out for lunch, back in two time steps
E 20 2 Too many pickles
I 9 Back from lunch
This file is provided as sample.log. There are four errors, three of
which have a severity of greater than 50. The output of whatWentWrong
on sample.log ought to be
[ “Way too many pickles”
, “Bad pickle-flange interaction detected”
, “Flange failed!”
]
You can test your whatWentWrong function with testWhatWentWrong,
which is also provided by the Log module. You should provide
testWhatWentWrong with your parse function, your whatWentWrong
function, and the name of the log file to parse.

whatWentWrong :: [LogMessage] -> [String]
whatWentWrong message= 
    let 
        sortedMessage = inOrder (build  message)
    in
        whatWentWrongHelper sortedMessage

getErrorLevel :: LogMessage -> TimeStamp
getErrorLevel (LogMessage _ time _) = time

getErrorString :: LogMessage -> String
getErrorString (LogMessage _ _ s) = s

whatWentWrongHelper :: [LogMessage] -> [String]
whatWentWrongHelper []      = []
whatWentWrongHelper (x:zs)  = if (getErrorLevel x) >= 50
                            then [(getErrorString x)] ++ whatWentWrongHelper zs
                            else whatWentWrongHelper zs

过年这几天翻译了一篇将语言静态动态特性的plt文章,感觉翻译对我这种懒得写代码的人很合适…
就是排版又累又折磨orz
And最近帮人做了一下ECS 150: Project #2 - User-level thread library,本来以为很简单,没想到又是一个“实现raft”级别的实验…为了避免掉头发所以github借鉴了一下orz

Recursion patterns, polymorphism, and the Prelude

这一节首先介绍了一些Recursion patterns
包括:
mapIntList
filter

fold
不过后两者没有提供具体的api
然后介绍了polymorphism
包括多态的函数和类型
然后介绍Prelude模块和Maybe类型
Partial function可以被full fuction或者Maybe替代,尽量少的使用partial是一种好的范式

作业使用了一些课件之外的技巧:
使用wherelocal scope
使用[运算|迭代]做计算,比如line xs n = [if i >= n then '*' else ' ' | i <- xs]迭代整个列表
使用[a a+n.. b]做迭代,效果类似于for语句
使用lst !! n运算符,作用是从列表中取第n个值
使用\n,作为lambda表达式的开头声明
使用$用于省略()$后面的语句优先级会大于前面的语句

HomeWork 3

module Golf where

skips :: [a] -> [[a]]
skips lst = [each i lst | i <- [1..length lst]]

-- each不是partial functions,因为它对于任何输入,都是安全的
each :: Int -> [a] -> [a]
each n lst = [lst !! i | i <- [n-1,n-1+n..length lst-1]]

localMaxima :: [Integer] -> [Integer]
localMaxima [] = []
localMaxima lst = concat [maximaHelper i lst | i <- [1..length lst-2]]

maximaHelper :: Int -> [Integer] -> [Integer]
maximaHelper n lst 
    | (lst !! n)>(lst !! (n-1)) && (lst !! n)>(lst !! (n+1)) = [(lst !! n)]
    | otherwise = []
-- 更简单的写法是这样的:
-- localMaxima :: [Integer] -> [Integer]
-- localMaxima (x:y:z:zs)
--   | x < y && y > z = y : localMaxima (y:z:zs)
--   | otherwise      = localMaxima (y:z:zs)
-- localMaxima _ = []


histogram :: [Integer] -> String
histogram xs = unlines (map (line c) [m+1,m..1]) ++ "==========\n0123456789\n"
  where c = count xs
        m = maximum c

-- 迭代整个列表,一次输出一行的结果
line :: [Int] -> Int -> String
line xs n = [if i >= n then '*' else ' ' | i <- xs]

-- 获取每个数字出现的次数
count :: [Integer] -> [Int]
count xs = map (\n -> length $ filter (== n) xs) [0..9]

main    =   do
            print(count [1,4,5,4,6,6,3,4,2,4,9])

higher-order

关于dependent type…

Type constructor(λω):允许 Type -> Type 的项
Polymorphism(λ2):允许 Type -> Term 的项
Dependent Type(λP):允许 Term -> Type 的项(Term也是Type的一部分)
三个都加上的话(这系统叫做 λC)你会发现:Term 和 Type 变成一回事儿了,于是很多「普通」函数式语言里面要开编译器扩展才能搞的高大上的特性变成了稀松平常的东西……
作者:Belleve
链接:https://www.zhihu.com/question/29706455/answer/138665873
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

介绍了很多重要的概念,首先是Anonymous functionsoperator section

Prelude> (\x y z -> [x,2*y,3*z]) 5 6 3
[5,12,9]
Prelude> (>100) 102
True

然后是Function composition,用.来表示运算优先级并且省略参数:
myTest xs = even (length (greaterThan100 xs))
等价于
myTest' = even . length . greaterThan100
这种风格被称为point-free style (也就是链式调用 和$相同

再之后是sml里面已经见过的Currying and partial application
curry就是将多参数表示为单参数,接下来参数作为返回值的用法
partial就是将一个包含多个参数的函数应用于它的一些参数,然后得到一个包含其余参数的函数。

HomeWork 4

import Data.List

fun1 :: [Integer] -> Integer
fun1 [] = 1
fun1 (x:xs)
  | even x    = (x - 2) * fun1 xs
  | otherwise = fun1 xs

-- product 乘积
-- even 偶数返回true

fun1' :: [Integer] -> Integer
fun1' =  product . map (\x -> x - 2) . filter even

fun1'' :: [Integer] -> Integer
fun1'' =  foldl (*) 1 . map (\x -> x - 2) . filter even

-- take 取无限列表的前n个
-- iterate 创建一个infinite list 以递归的方式
-- takeWhile 取列表符合条件的部分

fun2 :: Integer -> Integer
fun2 1 = 0
fun2 n 
    | even n = n + fun2 (n `div` 2)
    | otherwise = fun2 (3 * n + 1)

-- (/= 1)意为不等于1

fun2' :: Integer -> Integer
fun2' =  sum
        . filter even
        . takeWhile (/= 1)
        .iterate (\n -> if even n then n `div` 2 else 3*n+1)


data Tree a = Leaf
            | Node Integer (Tree a) a (Tree a)
  deriving (Show, Eq)

-- should produce a balanced Tree using @foldr@
-- 对length取2的对数之后向下取整
foldTree :: Eq a => [a] -> Tree a
foldTree xs = balancedInsert start xs
  where start = floor (logBase 2 $ fromIntegral(length xs)::Double)

-- a -> Tree a -> Tree a

balancedInsert :: Integer -> [a] -> Tree a
-- balancedInsert _ _ _ = Leaf
balancedInsert start xs = 
  Node start (Leaf) (last xs) (Leaf)

-- balancedInsert _ x (Node n left y right)
--   | right == Leaf = Node n left y (Node (n-1) Leaf x Leaf)
--   | otherwise = Node n (Node (n-1) Leaf x Leaf) y right


xor :: [Bool] -> Bool 
xor = not . even . length . filter (\n -> if n==True then True else False)

map' :: (a -> b) -> [a] -> [b]
map' f = foldr (\x y -> f x : y) []

sieveSundaram :: Integer -> [Integer]
sieveSundaram n = map ((+1) . (*2)) $ [1..n] \\ sieve
  where sieve = map (\(i, j) -> i + j + 2*i*j)
                . filter (\(i, j) -> i + j + 2*i*j <= n)
                $ cartProd [1..n] [1..n]

cartProd :: [a] -> [b] -> [(a, b)]
cartProd xs ys = [(x,y) | x <- xs, y <- ys]

main    =   do
            print(xor [False, True, False])

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值