haskell学习笔记——从零开始的整理

前言:下周就要半期考试啦,但这门语言的美和深刻的意义我也才在最近感受到,难道这就是压力带给人的buff吗hhh。值此机会,我从头到尾翻了一遍Graham Hutton的 《Programming in Haskell》,并在这里对书中“Basic Concepts”部分中的关键思想整理一下

一个直观的感受是,lambda calculus这个东西确实在愈来愈多地影响着主流语言,比如在c++11后可以定义的函数类型,给程序设计带来了很多便利,确实是源于FP

然后也能感受到的一个点是,作为一门纯粹的FP语言,haskell里的很多高阶的概念确乎难以移植到主流中,例如FP的实质是对函数进行组合来实现复杂的操作,这确实是可行的,但很多时候在理解和实现层面上就显得颇为困难。

但这也不在我一个初学者能考虑的范畴内,目前我还是先感受这种思想的美,并将它落诸纸笔吧。


一些基础的类型定义和函数定义就略过不表

可以参考这篇文章进行入门学习 Link

依我的理解,haskell里的函数其实都是一元函数,传入一个参数,并将它映射到另一个参数上

而多元函数的实际意义,实则是传入一个参数,返回一个函数,然后再对新函数传入下一个参数......

以比较典型的两个函数为例

foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f a [] = a
foldl f a (x : xs) = foldl f (f a x) xs

foldr :: (b -> a -> a) -> a -> [b] -> a
foldr f a [] = a
foldr f a (x : xs) = f x (foldr f a xs)

这里foldl表示传入一个f :: a -> b -> a(依次传入一个a类型和一个b类型返回一个a类型的函数),然后传入一个a类型的值和一个b类型的list,最后返回一个a类型的值作为结果

函数的实际实现就是把[b]这个list里面的数一个一个拿出来依次和a进行函数运算

foldr是同理的,读者自证不难

这个又自然引出currying和high-ordered function

这个非常的关键,是我们实现很多复杂操作的关键

我的理解是,这建立在我们的参数是逐步传入的,这使得我们函数的组合变得非常灵活,不过这是后话了


声明一个类型,可以使用type(相当于define,起别名),data(有点像一个多种类型的mixture,用pattern matching 定义),newtype(只有一种类型)

然后类型的声明里是允许递归的

然后是类型族type classes,我的理解是,这代表满足一些公共性质(实际是拥有公共函数)的一些type构成的集合

我们可以使用instance关键字在类型族里加入我们定义的新类型

通过定义对当前类型定义这个类型族里的公共函数来实现定义

instance class T a where
-- declare functions in T with type a

The countdown problem

一个综合性很强的问题

贴上我的代码

import System.Win32 (COORD(yPos))

data Op
  = Add
  | Sub
  | Mul
  | Div
  | Exp
  deriving Eq

data Expr
  = Val Int
  | App Op Expr Expr
  deriving Eq

ln2 :: Float
ln2 = log 2.0

valid :: Op -> Int -> Int -> Bool
valid Add x y = x <= y && (x + y < 2 ^ 31)
valid Sub x y = x > y
valid Mul x y = x <= y && x /= 1 && y /= 1 && (x * y < 2 ^ 31)
valid Div x y = (x `mod` y == 0) && y /= 1
valid Exp x y = x > 1 && y > 1 && y < 31 && ((fromIntegral y :: Float) * log (fromIntegral x :: Float) < (31 :: Float) * ln2)

-- We note that x ^ y < 2 ^ 31 <=> y * lnx < 31 * ln2
-- We need to prune some possibilities

apply :: Op -> Int -> Int -> Int
apply Add x y = x + y
apply Sub x y = x - y
apply Mul x y = x * y
apply Div x y = x `div` y
apply Exp x y = x ^ y

split :: [a] -> [([a], [a])]
split [] = []
split [n] = []
split (x : xs) = ([x], xs) : [(x : ls, rs) | (ls, rs) <- split xs]

type Result = (Expr, Int)
findResults :: [Int] -> [Result]
findResults [] = []
findResults [n] = [(Val n, n) | n > 0]
findResults ns = [res | (ls, rs) <- split ns
                      , lres <- findResults ls
                      , rres <- findResults rs
                      , res <- fix lres rres]
fix :: Result -> Result -> [Result]
fix (lexpr, lnum) (rexpr, rnum) = [(App op lexpr rexpr, apply op lnum rnum) | op <- [Add, Sub, Mul, Div, Exp], valid op lnum rnum]

insert :: Int -> [Int] -> [[Int]]
insert y [] = [[y]]
insert y xs'@(x : xs) = (y : xs') : [x : ys | ys <- insert y xs]

enumerate :: [Int] -> [[Int]]
enumerate [] = [[]]
enumerate (x : xs) = [xs'' | xs' <- left, xs'' <- insert x xs'] ++ left
              where left = enumerate xs

solutions :: [Int] -> Int -> [Expr]
solutions ns n = [expr | ns' <- enumerate ns, (expr, num) <- findResults ns', num == n]

-- to find nearest solutions

findMinDis :: [Int] -> Int -> Int
findMinDis ns n = minimum [abs (num-n) | ns' <- enumerate ns, (expr, num) <- findResults ns'] 

findNear :: [Int] -> Int -> [Expr]
findNear ns n = [expr | ns' <- enumerate ns, (expr, num) <- findResults ns', abs (num-n) == dis]
              where dis = findMinDis ns n

findSolutions :: [Int] -> Int -> [Expr]
findSolutions ns n = if not (null sol) then sol
                    else findNear ns n
                    where sol = solutions ns n

instance Show Op where
  show Add = "+"
  show Sub = "-"
  show Mul = "*"
  show Div = "/"
  show Exp = "^"

instance Show Expr where
  showsPrec _ (Val n) = shows n
  showsPrec p (App op x y)
    = showParen (p > q)
    $ showsPrec q x . showChar ' ' . shows op
    . showChar ' ' . showsPrec (succ q) y
    where q = case op of
            Add -> 6; Sub -> 6
            Mul -> 7; Div -> 7
            Exp -> 8
posted @ 2022-10-27 17:06  deaf  阅读(89)  评论(0编辑  收藏  举报