- Cảnh báo: rất giống Python
- Chú ý: không cần biết Python
Haskell (/ˈhæskəl/) - visub: ha-s-kồ - 1990 (lớn hơn Python 1 tuổi) Trang chủ: https://www.haskell.org/
Haskell là ngôn ngữ functional (lập trình hàm), thuộc nhóm "pure" blah blah blah có thể bỏ qua và gõ cho đến cuối bài, work first, talk is cheap, later.
Cài đặt
Hướng dẫn trang chủ, hỗ trợ cả Windows
curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
enter enter enter ... rồi mở terminal mới, gõ ghci
.
ghci --version
The Glorious Glasgow Haskell Compilation System, version 8.10.5
Trên Ubuntu có thể dùng:
sudo apt-get update && sudo apt-get install -y haskell-stack
REPL
REPL - Read Eval Print Loop, là chương trình nhận code người dùng nhập vào (Read), chạy code đó (Eval), in kết quả ra màn hình (Print), và cứ tiếp tục vậy (Loop).
Khái niệm này bắt nguồn từ ngôn ngữ lập trình cổ thứ 2 thế giới: LISP.
Việc viết code khi dùng các ngôn ngữ có REPL thường theo các bước:
- bật REPL lên
- gõ code thử cho tới khi thu được kết quả mong muốn
- copy code đó vào editor/IDE
Câu lệnh bật REPL của Haskell có tên ghci
.
$ ghci
GHCi, version 8.10.5: https://www.haskell.org/ghc/ :? for help
Prelude> 1 + 1
2
Prelude> 2 * 1024
2048
Prelude> :quit
Leaving GHCi.
Haskell Hello world
Prelude> print "Hello Pymier!"
"Hello Pymier!"
Integer
cộng +
trừ -
nhân *
mũ/lũy thừa ^
Prelude> 54 + 5 * (2 + 1)
69
Prelude> 2 ^ 1000
10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376
Float
Prelude> 4 / 2
2.0
Prelude> 5 / 2
2.5
Prelude> 5 / 2.5
2.0
Prelude> 0.1 + 0.1 + 0.1
0.30000000000000004
Prelude> 0.1 + 0.1 + 0.1 == 0.3
False
Prelude> 0.1 + 0.1 + 0.1 /= 0.3
True
Tại sao 0.1 + 0.1 + 0.1 /= 0.3
Boolean
Prelude> 2 < 5
True
Prelude> 2 > 5
False
Prelude> 1 + 1 == 2
True
Prelude> 2 - 1 /= 0
True
Prelude> 2 <= 2
True
Prelude> 1/0
Infinity
and
Prelude> True && True
True
Prelude> True && False
False
Prelude> False && True
False
Prelude> False && False
False
or
Prelude> True || True
True
Prelude> True || False
True
Prelude> False || True
True
Prelude> False || False
False
not
Prelude> not True
False
Prelude> not False
True
Haskell boolean có tính short-circuit - dừng lại ngay khi có thể kết luận kết quả biểu thức boolean.
Prelude> error "huhu"
*** Exception: huhu
CallStack (from HasCallStack):
error, called at <interactive>:2:1 in interactive:Ghci2
Prelude> True || error "huhu"
True
Prelude> False && error "huhu"
False
hay nói cụ thể:
||
sẽ dừng lại và trả về kết quả ngay khi gặpTrue
&&
sẽ dừng lại và trả về kết quả ngay khi gặpFalse
Với các ngôn ngữ khác, boolean có short-circuit hay không thì tùy từng ngôn ngữ quyết định, nhưng với một tính năng nổi bật của Haskell: Lazy, short-circuit là chuyện đương nhiên.
type
Các câu lệnh trong ghci
:help
:info
Prelude> :info mod
type Integral :: * -> Constraint
class (Real a, Enum a) => Integral a where
...
mod :: a -> a -> a
...
-- Defined in ‘GHC.Real’
infixl 7 `mod`
Prelude> :info (+)
type Num :: * -> Constraint
class Num a where
(+) :: a -> a -> a
...
-- Defined in ‘GHC.Num’
infixl 6 +
Hiển thị type:
Prelude> :set +t
Prelude> 1
1
it :: Num p => p
Prelude> 0.1
0.1
it :: Fractional p => p
số nguyên 1 có kiểu Num
, số float 0.1
có kiểu Fractional
Prelude> True
True
it :: Bool
giá trị boolean True có kiểu Bool
Prelude> "con mèo trèo"
"con m\232o tr\232o"
it :: [Char]
Prelude> length "con mèo trèo"
12
it :: Int
string là 1 list của các Char
(character - ký tự)
Prelude> [1..10]
[1,2,3,4,5,6,7,8,9,10]
it :: (Num a, Enum a) => [a]
kiểu list được biểu diễn bởi dấu []
it
là biểu thức/kết quả cuối cùng bạn đã gõ. Tương tự _
trong Python interpreter.
Prelude> 1
1
Prelude> it + 2
3
ProjectEuler problem 1
https://projecteuler.net/problem=1
Chú ý: theo chương trình học của Pymi.vn, phần này được học ở buổi số 4. Bạn đọc cần biết Python để hiểu phần này hoặc chỉ cần gõ theo. Có thể đọc thêm tại đây.
If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. Find the sum of all the multiples of 3 or 5 below 1000.
Tạo list các số từ 1 đến 5
Prelude> [1..5]
[1,2,3,4,5]
Haskell không dùng %
cho phép chia lấy phần dư (modulo/remainder), code Python 10 % 3
tương đương với viết Haskell mod 10 3
hoặc
rem 10 3
Haskell dùng || &&
thay Python or and
Python 2.0 MƯỢN list comprehension từ Haskell
>>> sum([i for i in range(1000) if i % 3 == 0 or i % 5 == 0])
233168
Haskell dùng |
thay chữ for
, dùng <-
thay chữ in
, dùng ,
thay chữ if
so với Python
Prelude> sum [i | i <- [1..999], mod i 3 == 0 || mod i 5 == 0]
233168
Bonus: bài PE16:
Tính tổng các chữ số của 2 mũ 1000
Prelude> sum [read [i] :: Integer | i <- show (2^1000)]
1366
Các đặc tính nổi bật của Haskell
Theo wiki Haskell
Haskell is a computer programming language. In particular, it is a polymorphically statically typed, lazy, purely functional language, quite different from most other programming languages. The language is named for Haskell Brooks Curry, whose work in mathematical logic serves as a foundation for functional languages. Haskell is based on the lambda calculus, hence the lambda we use as a logo.
- polymorphically statically typed
- lazy
- purely functional
hoặc xem phần features trên https://www.haskell.org/
lập trình hàm là gì
Haskell là ngôn ngữ thuộc nhóm functional (lập trình hàm).
Trên lý thuyết, có nghĩa nó dựa trên "lambda calculus", một mô hình/hệ thống tính toán dùng các function (hàm toán học), khác với mô hình Turing Machine mà các ngôn ngữ lập trình C, Java, Python, Go... dựa trên. Hai mô hình này được chứng minh về mặt toán học là có khả năng như nhau.
Về mặt thực hành, code với 1 ngôn ngữ functional thường có nghĩa là:
- không dùng vòng lặp for/while mà dùng các function có sẵn để làm việc tương tự (vd: map, filter, fold, reduce,...) hoặc viết các recursive function để thu được kết quả tương ứng.
- Các kiểu dữ liệu thường là immutable, khi thay đổi 1 list, Haskell sẽ tạo ra 1 list mới với những thay đổi đã thực hiện (và bỏ list cũ đi).
- First class function: quen với việc dùng function này làm đầu vào cho function
khác. Ví dụ map
map (\x -> x * 2) [1..5]
nhận function\x -> x * 2
và 1 list để thực hiện function đó trên tất cả các phần tử trong list.
Haskell purely functional là gì
pure function là một function không có "side effect", giống hàm toán học, kết quả đầu ra chỉ phụ thuộc đầu vào.
f(x) = 2x + 1
f(30) luôn luôn bằng 61
Side effect là việc function thực hiện 1 thay đổi nào đó (thay đổi phần tử 1 list, đọc ghi 1 file, in ra màn hình, kết nối internet, sleep chương trình, ...) hay phụ thuộc vào yếu tố khác với đầu vào (một chương trình phụ thuộc vào thời gian lúc nó chạy) nghe hơi vô lý khi một ngôn ngữ mà không tương tác với thế giới bên ngoài thì... chỉ để học toán. Nhưng Haskell sẽ dựa trên 1 khái niệm/cơ chế hoàn toàn khác để thực hiện các việc nói trên.
Haskell lazy là gì
lazy là chỉ thực hiện tính toán khi thực sự cần tới giá trị. Ví dụ có thể viết code tạo ra list từ 1 tới vô cùng, nhưng vì Haskell lazy, nó chỉ lấy ra phần tử nó cần, chứ không tạo list từ 1 tới vô cùng từ đầu.
Prelude> take 10 [1..]
[1,2,3,4,5,6,7,8,9,10]
Prelude> take 20 [1..]
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
Trong Python, khái niệm gần nhất với lazy là generator
Để Haskell bắt buộc phải tính list [1..] tới vô cùng, ta dùng 1 function cần
phải duyệt đến phần tử cuối cùng của list - như tính độ dài : length
Prelude> length [1..]
^CInterrupted.
code này sẽ chạy mãi cho tới khi người dùng tắt chương trình hoặc bấm Ctrl-C.
Kết luận
Ngày đầu của Haskell không hề khó hơn ngày đầu học Python. Đừng vì "cộng đồng mạng" nói khó mà chưa thử đã tin!
Tham khảo
- https://pymi.vn/tutorial/python-integer/
- https://pymi.vn/tutorial/python-calculation-2/
- RealWorldHaskell
What next?
Loạt bài viết dự kiến có 6-8 bài, ứng với 6-8 buổi học Python tại pymi.vn
Comments