Haskell - Chức năng

Các hàm đóng một vai trò quan trọng trong Haskell, vì nó là một ngôn ngữ lập trình hàm. Giống như các ngôn ngữ khác, Haskell có định nghĩa và khai báo chức năng của riêng nó.

  • Khai báo hàm bao gồm tên hàm và danh sách đối số của nó cùng với đầu ra của nó.

  • Định nghĩa hàm là nơi bạn thực sự xác định một hàm.

Hãy để chúng tôi lấy một ví dụ nhỏ về add chức năng để hiểu khái niệm này một cách chi tiết.

add :: Integer -> Integer -> Integer   --function declaration 
add x y =  x + y                       --function definition 

main = do 
   putStrLn "The addition of the two numbers is:"  
   print(add 2 5)    --calling a function

Ở đây, chúng ta đã khai báo hàm của mình ở dòng đầu tiên và ở dòng thứ hai, chúng ta đã viết hàm thực của mình sẽ nhận hai đối số và tạo ra một đầu ra kiểu số nguyên.

Giống như hầu hết các ngôn ngữ khác, Haskell bắt đầu biên dịch mã từ mainphương pháp. Mã của chúng tôi sẽ tạo ra kết quả sau:

The addition of the two numbers is:
7

Khớp mẫu

Khớp mẫu là quá trình đối sánh loại biểu thức cụ thể. Nó không là gì khác ngoài một kỹ thuật để đơn giản hóa mã của bạn. Kỹ thuật này có thể được triển khai vào bất kỳ loại lớp Type nào. If-Else có thể được sử dụng như một tùy chọn thay thế của đối sánh mẫu.

Đối sánh mẫu có thể được coi là một biến thể của đa hình động trong đó tại thời gian chạy, các phương thức khác nhau có thể được thực thi tùy thuộc vào danh sách đối số của chúng.

Hãy xem khối mã sau. Ở đây chúng ta đã sử dụng kỹ thuật Khớp mẫu để tính giai thừa của một số.

fact :: Int -> Int 
fact 0 = 1 
fact n = n * fact ( n - 1 ) 

main = do 
   putStrLn "The factorial of 5 is:" 
   print (fact 5)

Tất cả chúng ta đều biết cách tính giai thừa của một số. Trình biên dịch sẽ bắt đầu tìm kiếm một hàm được gọi là "fact" với một đối số. Nếu đối số không bằng 0, thì số sẽ tiếp tục gọi cùng một hàm với số ít hơn 1 so với đối số thực.

Khi mẫu của đối số khớp chính xác với 0, nó sẽ gọi mẫu của chúng ta là "fact 0 = 1". Mã của chúng tôi sẽ tạo ra kết quả sau:

The factorial of 5 is:
120

Lính canh

Guardslà một khái niệm rất giống với đối sánh mẫu. Trong đối sánh mẫu, chúng tôi thường đối sánh một hoặc nhiều biểu thức, nhưng chúng tôi sử dụngguards để kiểm tra một số thuộc tính của một biểu thức.

Mặc dù nó được khuyến khích sử dụng so khớp mẫu trên guards, nhưng từ quan điểm của một nhà phát triển, guardsdễ đọc và đơn giản hơn. Đối với người dùng lần đầu,guards có thể trông rất giống với câu lệnh If-Else, nhưng chúng khác nhau về mặt chức năng.

Trong đoạn mã sau, chúng tôi đã sửa đổi factorial chương trình bằng cách sử dụng khái niệm guards.

fact :: Integer -> Integer 
fact n | n == 0 = 1 
       | n /= 0 = n * fact (n-1) 
main = do 
   putStrLn "The factorial of 5 is:"  
   print (fact 5)

Ở đây, chúng tôi đã tuyên bố hai guards, được phân tách bằng "|" và gọifact chức năng từ main. Bên trong, trình biên dịch sẽ hoạt động theo cách tương tự như trong trường hợp khớp mẫu để mang lại kết quả đầu ra sau:

The factorial of 5 is:
120

Mệnh đề Where

Wherelà một từ khóa hoặc hàm có sẵn có thể được sử dụng trong thời gian chạy để tạo ra kết quả mong muốn. Nó có thể rất hữu ích khi việc tính toán hàm trở nên phức tạp.

Hãy xem xét một tình huống trong đó đầu vào của bạn là một biểu thức phức tạp với nhiều tham số. Trong những trường hợp như vậy, bạn có thể chia toàn bộ biểu thức thành các phần nhỏ bằng cách sử dụng mệnh đề "where".

Trong ví dụ sau, chúng ta đang sử dụng một biểu thức toán học phức tạp. Chúng tôi sẽ chỉ ra cách bạn có thể tìm nghiệm nguyên của phương trình đa thức [x ^ 2 - 8x + 6] bằng cách sử dụng Haskell.

roots :: (Float, Float, Float) -> (Float, Float)  
roots (a,b,c) = (x1, x2) where 
   x1 = e + sqrt d / (2 * a) 
   x2 = e - sqrt d / (2 * a) 
   d = b * b - 4 * a * c  
   e = - b / (2 * a)  
main = do 
   putStrLn "The roots of our Polynomial equation are:" 
   print (roots(1,-8,6))

Lưu ý mức độ phức tạp của biểu thức của chúng ta để tính các nghiệm nguyên của hàm đa thức đã cho. Nó khá phức tạp. Do đó, chúng tôi đang phá vỡ biểu thức bằng cách sử dụngwheremệnh đề. Đoạn mã trên sẽ tạo ra kết quả sau:

The roots of our Polynomial equation are:
(7.1622777,0.8377223)

Hàm đệ quy

Đệ quy là một tình huống mà một hàm gọi chính nó nhiều lần. Haskell không cung cấp bất kỳ phương tiện lặp lại bất kỳ biểu thức nào nhiều hơn một lần. Thay vào đó, Haskell muốn bạn chia toàn bộ chức năng của mình thành một tập hợp các chức năng khác nhau và sử dụng kỹ thuật đệ quy để triển khai chức năng của bạn.

Chúng ta hãy xem xét lại ví dụ về đối sánh mẫu của chúng ta, nơi chúng ta đã tính giai thừa của một số. Tìm giai thừa của một số là một trường hợp cổ điển của việc sử dụng Đệ quy. Ở đây, bạn có thể, "So khớp mẫu khác với đệ quy như thế nào?" Sự khác biệt giữa hai điều này nằm ở cách chúng được sử dụng. So khớp mẫu hoạt động khi thiết lập ràng buộc đầu cuối, trong khi đệ quy là một lời gọi hàm.

Trong ví dụ sau, chúng tôi đã sử dụng cả đối sánh mẫu và đệ quy để tính giai thừa của 5.

fact :: Int -> Int 
fact 0 = 1 
fact n = n * fact ( n - 1 ) 

main = do 
   putStrLn "The factorial of 5 is:" 
   print (fact 5)

Nó sẽ tạo ra kết quả sau:

The factorial of 5 is:
120

Chức năng đặt hàng cao hơn

Cho đến bây giờ, những gì chúng ta đã thấy là các hàm Haskell có một type làm đầu vào và tạo ra một typedưới dạng đầu ra, tương tự như trong các ngôn ngữ mệnh lệnh khác. Các hàm thứ tự cao hơn là một tính năng độc đáo của Haskell, nơi bạn có thể sử dụng một hàm làm đối số đầu vào hoặc đầu ra.

Mặc dù nó là một khái niệm ảo, nhưng trong các chương trình thế giới thực, mọi chức năng mà chúng ta xác định trong Haskell đều sử dụng cơ chế bậc cao hơn để cung cấp đầu ra. Nếu bạn có cơ hội xem xét hàm thư viện của Haskell, thì bạn sẽ thấy rằng hầu hết các hàm thư viện đã được viết theo cách thức cao hơn.

Hãy để chúng tôi lấy một ví dụ trong đó chúng tôi sẽ nhập một bản đồ hàm bậc cao có sẵn và sử dụng bản đồ đó để triển khai một hàm bậc cao khác theo lựa chọn của chúng ta.

import Data.Char  
import Prelude hiding (map) 

map :: (a -> b) -> [a] -> [b] 
map _ [] = [] 
map func (x : abc) = func x : map func abc  
main = print $ map toUpper "tutorialspoint.com"

Trong ví dụ trên, chúng tôi đã sử dụng toUpper chức năng của Type Class Charđể chuyển đổi đầu vào của chúng tôi thành chữ hoa. Ở đây, phương thức "map" đang nhận một hàm làm đối số và trả về kết quả đầu ra cần thiết. Đây là đầu ra của nó -

sh-4.3$ ghc -O2 --make *.hs -o main -threaded -rtsopts
sh-4.3$ main
"TUTORIALSPOINT.COM"

Lambda Expression

Đôi khi chúng ta phải viết một hàm chỉ được sử dụng một lần, trong toàn bộ vòng đời của một ứng dụng. Để đối phó với loại tình huống này, các nhà phát triển Haskell sử dụng một khối ẩn danh khác được gọi làlambda expression hoặc là lambda function.

Một hàm không có định nghĩa được gọi là hàm lambda. Một hàm lambda được biểu thị bằng ký tự "\". Chúng ta hãy lấy ví dụ sau, nơi chúng ta sẽ tăng giá trị đầu vào lên 1 mà không cần tạo bất kỳ hàm nào.

main = do 
   putStrLn "The successor of 4 is:"  
   print ((\x -> x + 1) 4)

Ở đây, chúng tôi đã tạo một hàm ẩn danh không có tên. Nó lấy số nguyên 4 làm đối số và in ra giá trị đầu ra. Về cơ bản chúng ta đang vận hành một hàm mà không cần khai báo nó đúng cách. Đó là vẻ đẹp của biểu thức lambda.

Biểu thức lambda của chúng tôi sẽ tạo ra kết quả sau:

sh-4.3$ main
The successor of 4 is:
5