[Declarative Programming + Elm] Bài 5 – String & List

Sau khi đã điểm qua những thao tác cơ bản với các giá trị số học và logic thì chúng ta còn Char và String là primitive. Nhân tiện thì khi nói tới String, mặc dù là primitive nhưng trong hầu hết các ngôn ngữ lập trình nói chúng thì String đều có thêm khả

Sau khi đã điểm qua những thao tác cơ bản với các giá trị số học và logic thì chúng ta còn Char
Stringprimitive. Nhân tiện thì khi nói tới String, mặc dù là primitive nhưng trong hầu
hết các ngôn ngữ lập trình nói chúng thì String đều có thêm khả năng tương tác giống với một
dải các giá trị được lưu trữ dạng liệt kê sequence. Do đó nên mình quyết định là sẽ mang List
vào nội dung của bài này và để dành TupleRecord cho bài viết sau cho dễ so sánh.

Char

Package:elm/core/Char

Các ngôn ngữ định kiểu dữ liệu tĩnh static-typing như C hay Java đều có kiểu Char tách
riêng khỏi String và đều sử dụng các cặp nháy đơn 'A' để biểu thị các giá trị Char trong
code. Ở đây Elm cũng sử dụng quy ước tương tự và chúng ta cũng có thêm một thư viện cung
cấp các sub-program để làm việc với các giá trị Char trong liên kết ở trên.

Số lượng các thao tác được cung cấp với Char cũng không nhiều, nên có lẽ chúng ta có thể liệt
kê hết ở đây. Đầu tiên sẽ là các thao tác kiểm tra ký tự số, ký tự chữ, kiểu viết thường, kiểu viết hoa.
Các ký tự kiểm tra trong ví dụ lần lượt là:

  • Chữ số 0
  • Chữ viết thường o
  • Chữ viết hoa O
Char.isDigit '0'   -- True
Char.isDigit 'o'   -- False
Char.isDigit 'O'   -- False

Char.isAlpha '0'   -- False
Char.isAlpha 'o'   -- True
Char.isAlpha 'O'   -- True

Char.isUpper '0'   -- False
Char.isUpper 'o'   -- False
Char.isUpper 'O'   -- True

Char.isLower '0'   -- False
Char.isLower 'o'   -- True
Char.isLower 'O'   -- False

Trong code ví dụ ở đây thì mình muốn viết đủ tên của module để bổ nghĩa cho tên của các
sub-program. Nếu bạn cảm thấy rườm rà và muốn code gọn hơn thì có thể thêm thao tác
exposing như trong các bài viết mở đầu.

import Char exposing (..)

Nhóm thao tác còn lại là chuyển đổi qua lại giữa kiểu chữ viết thường <=> chữ viết hoa, và
chuyển đổi giữa mã Unicode <=> ký tự chữ cái.

Char.toUpper 'a'   -- 'A'
char.toLower 'A'   -- 'a'

Char.toCode 'a'    -- 97
Char.fromCode 98   -- 'b'

String

Package:elm/core/String

Ngoài cách biểu thị bằng một cặp nháy kép "a string" thì Elm còn cho phép mô tả nội dung
văn bản dài trên nhiều dòng liền nhau bằng 3 cặp nháy kép –

"""A string with a line br
eak in Elm"""

-- "A string with a line brn  eak in Elm" : String

Ở đây mình có lưu ý một chút. Đó là cái khoảng trống giữa ký tự xuống dòng mới newline
eak... là do trong môi trường Elm REPL tự động thụt đầu dòng khi chúng ta viết code. Còn
logic chuẩn thì khi viết vào code trong tệp và chạy elm make hay elm reactor thì chúng ta sẽ
có từ brneak.

Số lượng thao tác thường sử dụng với String thì có rất nhiều, tuy nhiên ở đây mình sẽ chỉ liệt
kê một số thao tác mà mình chú ý ở cấp độ cú pháp JS và tham chiếu sang Elm.

-- "Check if a string is empty in JS" == ""
String.isEmpty "Check in Elm"

-- "JS String" + " concatenation"
"Elm String" ++ " concatenation"

-- "A" + "ppend a character in JS"
(String.fromChar 'A') ++ "ppend a character in Elm"

-- [ ... "JS String to Array" ]
String.toList "Elm String to List Char"

Các thao tác còn lại được JS cung cấp dưới dạng sub-program thì ở đây Elm cũng có khá đầy
đủ và cách thức sử dụng thì cũng không có gì khác biệt. Tuy nhiên có nhóm
Higher Order Functions
thì mình lưu ý là sẽ tạm thời chưa sử dụng trong Sub-Series này và để dành cho tới Sub-Series
tiếp theo về Functional Programming.

List

Package:elm/core/List

Thực tế thì ListArray là hai cấu trúc dữ liệu khác nhau và không thể so sánh tương đồng
hoàn toàn về cách thức sử dụng List trong Elm và cách thức sử dụng Array trong JS.
Điểm khác biệt căn bản giữa hai kiểu sequence này đó là List không hỗ trợ việc truy xuất
nhanh một phần tử bằng trị số chỉ vị trí index như kiểu JSarray[1001].

Tuy nhiên thì chúng ta có thể tạm thời quan tâm trước hết tới những thao tác khác, được JS
hoặc Elm hỗ trợ ở cấp độ cú pháp của ngôn ngữ –

-- var concatenated = [ ...["Array", "concatenation"], ...["in", "JS"] ]
["List", "concatenation"] ++ ["in", "Elm"]

-- var appended = [ "Append", ...["to", "Array", "in", "JS"] ]
"Append" :: ["to", "List", "in", "Elm"]
["Append"] ++ ["to", "List", "in", "Elm"]
List.append ["Append"] ["to", "List", "in", "Elm"]

-- var prepended = [ ...["JS", "Array", "and"], "Prepend" ]
List.append ["Elm", "List", "and"] ["Prepend"]

Ậy, mấy cái List trong Elm không có thao tác List.prepend nên code chỗ này trông có hơi
bất đồng về mặt cú pháp. Mặc dù chúng ta cũng có thể tự định nghĩa thêm thao tác prepend
dựa trên List.append nếu muốn; Tuy nhiên mình nghĩ là thiết kế sử dụng như thế này hẳn phải
có lý do và chúng ta nên tạm thời đặt một chút băn khoăn ở đây cho đến khi quen với thao tác
sử dụng List trong những tình huống cụ thể.

Ngoài ra thì còn thao tác tách lấy một phần nội dung trong sequence thì ở cấp độ cú pháp của
ngôn ngữ JS còn cung cấp một thao tác có tên là destructuring. Ở đây Elm chỉ hỗ trợ ở cấp
độ sub-program và có điểm mà chúng ta cần phải lưu ý ở phần kết quả thu được.

-- var [ head, ...tail ] = ["Head", "Tail", "of", "JS", "Array"]

List.head ["Head", "Tail", "of", "Elm", "List"]
-- Just "Head" : Maybe String

List.tail ["Head", "Tail", "of", "Elm", "List"]
-- Just ["Tail","of","Elm","List"] : Maybe (List String)

Các kết quả này đều có điểm chung là thuộc kiểu Maybe và có một tham số dữ liệu kiểu biến
thiên a bất kỳ. Mở định nghĩa của Maybe xem nào:
elm/core/Maybe

typeMaybea=Justa|Nothing

Chúng ta đang có Maybe a là một kiểu Union. và nếu như một giá trị thu được từ một
sub-program mà có kết quả được type-hintMaybe a, thì có khả năng giá trị đó sẽ thuộc
kiểu Just a hoặc Nothing. Trong đó thì Nothing không được định nghĩa và được xem là một
giá trị tương đương với null hay undefined ở các môi trường lập trình khác; Còn Just là một
kiểu vỏ bọc đơn giản được thiết kế để có thể chứa được một giá trị của kiểu bất kỳ a.

Lý do chung chung là thao tác tách lấy dữ liệu của một sequence như trên thì chúng ta có một
trường hợp đặc biệt đó là khi nguồn dữ liệu là một sequence rỗng –

-- var [ head, ...tail ] = []

List.head []   -- Nothing : Maybe a
List.tail []   -- Nothing : Maybe (List a)

Đối với trường hợp của JS thì chúng ta có kết quả thu được là một giá trị head = undefined
một mảng tail = []. Còn trong môi trường của Elm thì chúng ta thu được các giá trị Nothing
cùng kiểu với kiểu giá trị dự tính. Cụ thể là với List.head thì chúng ta mong muốn thu được một
giá trị a trong List, còn với List.tail thì chúng ta mong muốn thu được một List nhỏ hơn.

Thiết kế này nhằm mục đích khiến cho các giá trị thu được sẽ luôn có ý nghĩa trong mọi trường hợp
của List ban đầu; Và chúng ta sẽ có thể sử dụng các giá trị thu được này để điều hướng logic xử
lý tình huống case của code sau đó. Tuy nhiên chúng ta sẽ để dành việc tìm hiểu cấu trúc lệnh
điều hướng logic case và cả việc lặp thao tác xử lý looping cho đến khi điểm qua các thao tác
với TupleRecord.

(chưa đăng tải) [Declarative Programming + Elm] Bài 6 – Tuple & Record

Nguồn: viblo.asia

Bài viết liên quan

WebP là gì? Hướng dẫn cách để chuyển hình ảnh jpg, png qua webp

WebP là gì? WebP là một định dạng ảnh hiện đại, được phát triển bởi Google

Điểm khác biệt giữa IPv4 và IPv6 là gì?

IPv4 và IPv6 là hai phiên bản của hệ thống địa chỉ Giao thức Internet (IP). IP l

Check nameservers của tên miền xem website trỏ đúng chưa

Tìm hiểu cách check nameservers của tên miền để xác định tên miền đó đang dùn

Mình đang dùng Google Domains để check tên miền hàng ngày

Từ khi thông báo dịch vụ Google Domains bỏ mác Beta, mình mới để ý và bắt đầ