[TOC]

0, Why

我们用几分钟来想象一下自己是一名伐木工人,手里有林场里最好的斧子,因此你是工作效率最高的。突然有一天场里来了个推销的,他把一种新的砍树工具一一链锯给夸到了天上去。这人很有说服力,所以你也买了一把,不过你不懂得怎么用。你估摸着按照自己原来擅长的砍树方法,把链锯大力地挥向树干一一你不知道要先发动它。“链锯不过是时髦的样子货罢了”,没砍几下你就得出了这样的结论,于是把它丢到一边重新捡起用惯了的斧子。就在这个时候,有人在你面前把链锯给发动了…

学习一种全新的编程范式,困难并不在于掌握新的语言。

毕竟学到函数式编程语言的程序员,学过的编程语言少说也有一箩筐一一语法不过是些小细节了。

真正考验人的,是怎么学会用另一种方式去思考。

​ ——《Functional Thinking》 Neal Ford

1,编程语言的类型

如图所示,常见编程语言的分类:

2,Haskell是什么

2.1,什么是函数式编程语言:

函数式编程关心数据的映射,命令式编程关心解决问题的步骤。

这里的映射就是数学上「函数」的概念——一种东西和另一种东西之间的对应关系。

在纯粹函数式编程语言中, 不是像命令式语言那样命令电脑「要做什么」,而是通过用函数来描述出问题「是什么」。

举个例子,二叉树的翻转:

## 命令式编程思维的实现,描述了从旧树到新树应该怎么做来实现翻转:
def invertTree(root):
	if root is None:
		return None
	root.left, root.right = invertTree(root.right), invertTree(root.left)
  

## 函数式编程思维的实现,描述了一个旧树到新树的映射,犹如一个数学公式:
def intert(node):
  if node is None:
    return None
  else:
    return Tree(node.value, invert(node.right), invert(node.right))

2.2, 函数式语言的特点:

函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值

函数式语言的优点:

面向对象編程通过封装不确定因素来使代码能被人理解;函式编程通过尽量减少不确定因素来使代码能被人理解。

3,Show me the code

首先安装GHC

然后命令行ghci

加载haskell代码: :load ~/Desktop/xxx.hs

-- 定义函数,声明(参数a是Num/Ord类型,返回bool)+实现
biggerThanthree :: (Num a,Ord a) => a -> Bool
biggerThanthree a
     | a > 3 = True
     |otherwise =  False


capital :: String -> String
capital "" = "Empty string, whoops!"
capital all@(x:xs) = "The first letter of " ++ all ++ " is " ++ [x]


lucky' :: (Integral a) => a -> String
lucky' 7 = "LUCKY NUMBER SEVEN!"
lucky' x = "Sorry, you're out of luck, pal!"

-- 实现阶乘,递归
factorial :: (Integral a) => a -> a
factorial 0 = 1
factorial n = n * factorial (n - 1)

 
initials :: String -> String -> String
initials firstname lastname = [f]
    where (f:_) = firstname

-- 取list中的最大值
maximum' :: (Ord a) => [a] -> a
maximum' [] = error "maximum of empty list"
maximum' [x] = x
maximum' (x:xs)
	| x > maxTail = x
	| otherwise = maxTail
	where maxTail = maximum' xs

-- 多个重复数字组成的列表 
replicate' :: (Num i,Ord i) => i -> a ->[a]
replicate' n x
    | n <= 0 = []
		| otherwise = x:some
			where some = replicate' (n-1) x
-- replicate 4 3 输出[3,3,3,3]


-- List中取前n个数
take' :: (Num i,Ord i) => i ->[a] -> [a]
take' n []
	| n <= 0 = []
take' _ [] = []
take' n (x:xs) = x:take'(n-1) xs

-- 列表反转,递归
reverse' ::[a] ->[a]
reverse' [] = []
reverse' (x:xs) = reverse' xs ++ [x]

repeat' :: a -> [a]
repeat' x = x:repeat' x

-- 实现zip函数 输入两个列表,输出一个合并列表
zip' :: [a] -> [b] -> [(a,b)]
zip' [] _ = []
zip' _ [] = []
zip' (x:xs) (y:ys) = [(x,y)] ++ zip' xs ys
-- zip' [1,2,3,4] [2,2,2,2] 
-- 输出[(1,2),(2,2),(3,2),(4,2)]

-- exist
elem' :: (Eq a) => a -> [a] -> Bool
elem' _ [] = False
elem' a (x:xs) = a == x || elem' a xs


elem'' :: (Eq a) => a -> [a] -> Bool
elem'' a [] = False
elem'' a (x:xs)
    | a == x = True
    | otherwise = a `elem''` xs

-- 快速排序,简洁之美
quicksort' :: (Ord a) => [a] -> [a]
quicksort' [] = []
quicksort' (x:xs) = sm ++ [x] ++ bg
    where sm = quicksort'[a | a <- xs ,a <= x]
          bg = quicksort'[a | a <- xs ,a > x]

-- 调用两次函数 (a -> a)定义了函数
applyTwice :: (a -> a) -> a -> a
applyTwice f x = f (f x)
-- applyTwice (+3) 10
-- 输出: 16

-- zip两个列表然后函数映射上去
zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith' _ [] _ = []
zipWith' _ _ [] = []
zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys
-- zipWith' (+) [1,1,1] [2,2,2]
-- 输出: [3,3,3]


flip' :: (a -> b -> c) -> (b -> a -> c)
flip' f y x = f x y

-- 递归实现map
map' :: (a -> b) -> [a] -> [b]
map' _ [] = []
map' f (x:xs) = f x : map' f xs

-- 递归实现filter
filter' :: (a -> Bool) -> [a] -> [a]
filter' _ [] = []
filter' p (x:xs)
	| p x = x : filter' p xs
	| otherwise = filter' p xs


largestDivisible :: (Integral a) => a
largestDivisible = head[x | x <- [100000,99999.. ] , x `mod` 3829 ==0]

largestDivisible' :: (Integral a) => a
largestDivisible' = head (filter p [100000,99999..])
    where p x = x `mod` 3829 == 0

nini :: (Integral a) => a
nini = sum [x^2 | x <- [10000,9999..1], odd x]


chain :: (Integral a) => a -> [a]
chain 1 = [1]
chain n
	| even n = n:chain (n `div` 2)
	| odd n = n:chain (n*3 + 1)


sum' :: (Num a) => [a] -> a
sum' xs = foldl (\acc x -> acc + x) 0 xs


sum'' :: (Num a) => [a] -> a
sum'' = foldl1 (\acc x -> acc + x)


elem''' :: (Eq a) => a -> [a] -> Bool
elem''' y ys = foldl (\acc x -> if x == y then True else acc) False ys


maximum''' :: (Ord a) => [a] -> a
maximum''' = foldl1(\acc x -> if x > acc then x else acc )

reverse'' :: [a] -> [a]
reverse'' = foldl (\acc x -> x : acc) []

4,一个感叹

接触了很多的语言,但也许永远学不会Haskell, 他太独特了,宛若一个艺术品。

在公司分享的时候有同事问写API能不能用Haskell,答案是当然可以,但这就好像开保时捷去开滴滴一样,不适合,那是丰田大众们(Java PHP)干的事情。

对于95%的工程师而言,掌握一门语言,做出不错的产品,实现商业价值,才是正途。不应该花时间纠结编程语言的优劣,那是Geeker们干的事情。

对编程语言的思考