Haskell
gereeter/hsdecomp: A decompiler for GHC-compiled Haskell (github.com)
反编译脚本
pip install pyelftools
pip install capstone
git clone https://github.com/gereeter/hsdecomp.git
cd hsdecomp
python3 setup.py install
一切都错了:像m一样对Haskell进行逆向工程|Kishou Yusa
LANGS | Haskell 趣學指南 (mno2.org)
安装#
GHCup (haskell.org) —— 最好用linux
curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
ghci#
输入 ghci
可进入交互模式, :l myfunctions.hs
可以加载Haskell脚本,修改 .hs
之后,再 :l myfunctions.hs
或者 :r
都可以重载脚本
:set prompt "ghci> "
把前缀改成 ghci>
不然以后装载模块之后会很长
运算#
ghci> 2 + 15
17
ghci> 49 * 100
4900
ghci> 5 == 5
True
ghci> 5 /= 5
False
函数#
ghci> div 8 2
4
ghci> 8 `div` 2
4
前缀函数转换成中缀函数,用 `` 包裹起来
ghci> succ 2
3
ghci> max 2 3
3
ghci> min 1 2
1
succ函数返回一个数的后继
定义函数#
ghci> doubleMe x = x + x
ghci> doubleMe 2
4
ghci> doubleUs x y = doubleMe x + doubleMe y
也可以写进 doub.hs
doubleMe x = x + x
doubleUs x y = doubleMe x + doubleMe y
然后
$ ghci
ghci> :l doub.hs
ghci> doubleUs 2 3
10
函数声明是没有顺序的,哪个先声明都可以
控制流#
doubleSmallNumber x = if x > 100
then x
else x*2
必须有else
这样定义也可以:
doubleSmallNumber' x = (if x > 100 then x else x*2) + 1
这里的 ' 是合法标识符,通常用于区分修改了一点点的函数
甚至这样:
conanO'Brien = "It's a-me, Conan O'Brien!"
- 函数首字母不能大写
- 这个是没有参数的函数,被称作”定义“(或者”名字“),一旦定义,conanO'Brien 就和 "It's a-me, Conan O'Brien!" 完全等价
List入门#
Note: ghci 下,用 let
关键字来定义一个常量。在 ghci 下 let a=1
与在脚本中 a=1
是等价的。
ghci> lostNumbers = [1,2,3,4,5]
ghci> lostNumbers
[1,2,3,4,5]
不能包含不同类型的元素
ghci> nums = [1,'2',3]
<interactive>:20:9: error:
• No instance for (Num Char) arising from the literal ‘1’
• In the expression: 1
In the expression: [1, '2', 3]
In an equation for ‘nums’: nums = [1, '2', 3]
字符串 "Hello"
实际上是 ['H','e','l','l','o']
的语法糖
ghci> str = ['H','e','l','l','o']
ghci> str
"Hello"
ghci> str = "Hello"
ghci> str
"Hello"
所以可以用处理List的方式处理字符串
ghci> "Hel" ++ "lo"
"Hello"
ghci> "H":"ello"
<interactive>:34:5: error:
• Couldn't match type ‘Char’ with ‘[Char]’
Expected: [String]
Actual: String
• In the second argument of ‘(:)’, namely ‘"ello"’
In the expression: "H" : "ello"
In an equation for ‘it’: it = "H" : "ello"
ghci> 'H':"ello"
"Hello"
ghci> ['H','e']++"llo"
"Hello"
ghci> ['H','e']++['l','l','o']
"Hello"
++
是 :
的语法糖,"He"++"llo"
等价于 'H':'e':"llo"
在使用
++
运算子处理长字串时要格外小心(对长 List 也是同样),Haskell 会遍历整个 List(++
符号左边的那个)。在处理较短的字串时问题还不大,但要是在一个 5000 万长度的 List 上追加元素,那可得执行好一会儿了。所以说,用:
运算子往一个 List 前端插入元素会是更好的选择。
索引获取元素、数组长度判断
ghci> "Hello" !! 2
'l'
ghci> [2,3,4] > [4,4,2,2]
False
![[image-20240612104447969.png]]
ghci> list = "abcdef"
ghci> head list
'a'
ghci> tail list
"bcdef"
ghci> init list
"abcde"
ghci> last list
'f'
危险:如果 list 是空,就会报错,先用 null
或者 length
判断是否为空
ghci> null list
False
ghci> null []
True
ghci> length list
6
ghci> length []
0
reverse
反转、 take
拿取n个元素、 drop
扔掉前n个元素
ghci> reverse list
"fedcba"
ghci> take 4 list
"abcd"
ghci> drop 2 list
"cdef"
maximum
获取最大, minimum
获取最小
ghci> list = [1,2,3,4,5]
ghci> maximum list
5
ghci> minimum list
1
sum
求和, product
求积
ghci> sum list
15
ghci> product list
120
elem
判断一个元素是否在 list 中,通常用中缀
ghci> elem 'a' "abcde"
True
ghci> 'a' `elem` "abcde"
True
使用 Range#
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> ['a'..'z']++['A'..'Z']
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
指定步长
ghci> [2,4..20]
[2,4,6,8,10,12,14,16,18,20]
ghci> [3,6..20]
[3,6,9,12,15,18]
反过来
ghci> [20,19..1]
[20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1]
最好不要用浮点数!
ghci> [0.1,0.2..1]
[0.1,0.2,0.30000000000000004,0.4,0.5,0.6,0.7000000000000001,0.8,0.9,1.0]
如果不表明上限,就可以拿到无限长的 List,怎么取到 24 个 13 的倍数呢?
ghci> [13,26..24*13]
[13,26,39,52,65,78,91,104,117,130,143,156,169,182,195,208,221,234,247,260,273,286,299,312]
ghci> take 24 [13,26..]
[13,26,39,52,65,78,91,104,117,130,143,156,169,182,195,208,221,234,247,260,273,286,299,312]
由于 Haskell 是惰性的,它不会对无限长度的 List 求值,否则会没完没了的。它会等着,看你会从它那儿取多少。
repeat
接受一个值作参数,返回一个包含该值的无限List
ghci> repeat 5
[5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,....
replicate
可以获取指定长度相同元素的list
ghci> take 5 (repeat 3)
[3,3,3,3,3]
ghci> replicate 5 3
[3,3,3,3,3]
List Comprehension#
Haskell 魔法!
![[image-20240612120129046.png]]
Haskell中也有类似的表示,称为 “过滤” —— 从一个 List 中筛选出符合条件的的操作
ghci> [ x*2 | x <- [1..10]]
[2,4,6,8,10,12,14,16,18,20]
再加一些限制条件
ghci> [ x*2 | x <- [1..10], x*2 >= 12]
[12,14,16,18,20]
ghci> [ x*2 | x <- [1..10], odd x]
[2,6,10,14,18]
ghci> [ x*2 | x <- [1..50], x `mod` 7 == 3]
[6,20,34,48,62,76,90]
更高级的使用#
boomBangs xs = [ if x < 10 then "BANG!" else "BOOM!" | x <- xs, odd x]
ghci> boomBangs [7..13]
["BANG!","BANG!","BOOM!","BOOM!"]
多个限制条件#
ghci> [x | x <- [1..20], not (x `elem` [5..9]), x /= 2, x /= 12]
[1,3,4,10,11,13,14,15,16,17,18,19,20]
多个 List#
ghci> [x*y | x <- [2..5], y <- [1..3]]
[2,4,6,3,6,9,4,8,12,5,10,15]
自己实现 length#
ghci> list = "HelloWorld!"
ghci> length list
11
ghci> length' xs = sum [1| _ <- xs]
ghci> length' list
11
_
表示我们并不关心从 List 中取什么值,与其弄个永远不用的变量,不如直接一个_
嵌套 List Comprehension#
ghci> let xxs = [[1,3,5,2,3,1,2,4,5],[1,2,3,4,5,6,7,8,9],[1,2,4,2,1,6,3,1,3,2,3,6]]
ghci> [[x1 | x1 <- x, even x1] | x <- xxs]
[[2,2,4],[2,4,6,8],[2,4,2,6,2,6]]
Tuple#
类似Python的Tuple,但是一个 list 只能有同一种类型的 Tuple
ghci> [(1,2),(3,4)]
[(1,2),(3,4)]
ghci> [(1,2),(3,4,5)]
error
ghci> [(1,2),(3,"a")]
error
获取元素(仅适用于二元元组)
ghci> t = (1,2)
ghci> fst t
1
ghci> snd t
2
zip
自动交叉分配元组
ghci> zip [1,2,3,4,5] ['a','b','c','d','e']
[(1,'a'),(2,'b'),(3,'c'),(4,'d'),(5,'e')]
因为惰性求值,所以无限长的也可以处理
ghci> zip [1..] [1..5]
[(1,1),(2,2),(3,3),(4,4),(5,5)]
实验:获取指定三角形#
取得所有三边长度皆为整数且小于等于 10,周长为 24 的直角三角形
ghci> [(x,y,z)|x<-[1..10],y<-[1..10],z<-[1..10],x+y+z==24,x+y>z,x+z>y,y+z>x, x^2+y^2==z^2, x<z, y<z, x>=y]
[(8,6,10)]
得到正确结果!这便是函数式编程语言的一般思路:先取一个初始的集合并将其变形,执行过滤条件,最终取得正确的结果。