Ví dụ về giao diện đồ họa trong Elm

Một phần của tài liệu Nghiên cứu lập trình phản ứng với ngôn ngữ ELM (Trang 33)

Elm có 2 yếu tố đồ họa chính: Elements and Forms

Element là kiểu giá trị thành phần chúng có thể chứa văn bản, video, hình ảnh… chúng dễ dàng đƣợc tạo và biên soạn. Xét ví dụ sau:

1 import Graphics.Element exposing (..)

2 main : Element

3 main = flow down (List.map (width 150) content)

5 content : List Element 6 content=

7 [ show "Hello, Welcom Elm!"

8 , image 200 200 "/imgs/heart.jpg" 9 , image 472 315 "/imgs/hockkey.jpg" 10 ]

Nhìn vào đoạn mã trên ta thấy hàm main có kiểu thành phần chức năng hiển thị biến content ra ngoài màn hình. Các thành phần có thể đƣợc kết hợp trong cùng một giao diện phức tạp bằng việc sử dụng hàm flow.

flow : Direction -> [Element] -> Element

Hàm flow dễ dàng thay đổi hƣớng hiển thị của các thành phần bằng việc sử dụng các giá trị (Direction):down, up, left, right, inward, outward. Đoạn mã trên sẽ hiển thị các thành phần của biến content theo hƣớng dọc từ trên xuống dƣới

34

(flow down) với dòng chữ : "Hello, Welcom Elm!" phía dƣới là một hình ảnh có tên “heart.jpg” và dƣới nữa là một hình ảnh tên là “hockkey.jpg”.

Một kiểu Form có thể đƣợc tạo ra từ: Đƣờng kính, hình dạng, văn bản, hình ảnh và mầu sắc. Các Forms có thể đƣợc di chuyển và quay và mở rộng với các hàm (moved, rotated, scaled)theo các tín hiệu đầu vào.

Đoạn mã dƣới đây sẽ tạo các Form gồm một hình tròn và 2 hình vuông. 1 import Color exposing (..)

2 import Graphics.Collage exposing(..)

3 import Graphics.Element exposing (..)

4 -- khai báo một hình tròn đầu vào: bán kính -> mầu sắc -> Form 5 myCircle : Float -> Color -> Form

6 myCircle r col = filled col (oval(2*r)(r*2))

7 -- khai báo một hình vuông đầu vào: bán kính -> mầu sắc -> Form 8 mySquare : Float -> Color -> Form

9 mySquare r col = filled col (square r)

10 -- tạo collage tập các forms lại với nhau 11 render = collage 400 400 [

12 myCircle 20 red ,

13 mySquare 50 blue 14 |> move (50, 50) ,

15 -- Tạo một hình chữ nhật với đường viềm bao quanh mầu xanh 16 outlined (solid blue) (rect 50 50)

17 |> move (-50, -50)

18 ]

19 main = render

Ngoài việc tạo các kiểu Form trong thƣ viện Elm còn có nhiều kiểunhƣ: Các đƣờng (Line) và hình dáng (shape). Line trong đó có một danh sách các điểm và trả

35

về một đƣờng mà đi qua mỗi điểm, đa giác (polygon) trong đó có một danh sách các điểm và trả về một đa giác không đều mà đi qua mỗi điểm và các hàm trong thƣ viện nhƣ “rect” để tạo hình chữ nhật, “oval” xây dựng hình bầu dục, và “ngon” để xây dựng đa giác.

Hàm collage cung cấp cung cấp khả năng kết hợp các yếu tố đồ họa trong một Form một cách có cấu trúc để tạo ra một thành phần. Nó sẽ thực hiện với các tham số nhƣ độ rộng, chiều cao và danh sách các forms.

Collage : Int -> Int -> [Form] -> Element

Đoạn mã bên dƣới biểu diễn cách tạo một vài Forms, thay đổi các thuộc tính của chúng và chuyển đổi chúng và sử dụng collage để kết hợp chung thành một thành phần.

1 import Color exposing (..)

2 import Graphics.Collage exposing (..)

3 import Graphics.Element exposing (..)

4 import Signal exposing (Signal, map)

5 import Text exposing (..)

6 import Mouse 7 square =rect 70 70 8 pentagon = ngon 5 20 9 circle = oval 50 50 10 zigzag = path [ (0,0), (10,10), (0,30),(10,40)] 11 main = collage 400 400 12 [-- Tạo một hình ngũ giác mầu xanh 13 filled green pentagon,

14 --Tạo một hình tròn viền xanh dương 15 outlined (dashed blue) circle,

16 --Tạo một hình vuông vơi đường biên là mầu đên và nghiêng 70 độ 17 rotate (degrees 70)(outlined (solid black) square),

18 --Tạo một đường zic zắc với tọa độ là (x =40, y = 40) 19 (traced (solid red) zigzag)

20 |> move 50 50 21 ]

36

2.2.3 Các tín hiệu tương tác trong Elm

Một tín hiệu là một dòng các giá trị mà thay đổi theo thời gian. Cho phép các lập trình viên tạo ra các tƣơng tác với giao diện ngƣời dùng thông qua các tín hiệu từ môi trƣờng bên ngoài. Elm cung cấp một số tín hiệu đƣợc sử dụng trong các chƣơng trình Elm.

Bảng dƣới đây sẽ mô tả các tín hiệu đầu vào cấu trúc của chúng.

Signal Type and Description

Mouse.position Signal (Int, Int)

Current coordinates of the mouse. Mouse.Clicks Signal()

Triggers on mouse clicks. Window.dimensions Signal (Int, Int)

Current dimensions of window. Time.every Time -> Signal Time

Update every t milisecongds. Time.fps Float -> Signal Time

Time deltas, updating at the given FPS. Touch.touches Signal [Touch]

List of ongoing touches. Useful for denfining gestures. Touch.taps Signal {x: Int, y: Int}

Position of the latest tap. Keyboard.keysDown Signal [keyCode]

37

List of keys that are currently pressed. Keyboard.arrows Signal {x: Int, y: Int}

Arrow keys pressed. Keyboard.shift Signal Bool

Is the shift key down?

Input.text String -> (Signal Element, Signal String) Create a text input field.

Hàm Time.every thực hiện cập nhật sự kiện trong khoảng thời gian là t milliseconds cho ra kết quả tín hiệu là thời gian hiện tại. Hàm Time.fps cho phép ngƣời lập trình xác định và miêu tả tỷ lệ khung hình diễn ra trong một giây để dễ dàng tạo ra các các bƣớc thời gian (step – time) động, tỷ lệ khung hình đƣợc quản lý bởi thời gian chạy của Elm.

Hàm input.text thể hiện nhƣ một cặp các tín hiệu: Một thành phần đồ họa, và một giá trị nhập vào. Các thành phần sẽ hiển thị trên màn hình và các tín hiệu sẽ đƣợc cập nhật ở thời điểm khi văn bản đƣợc nhập vào trƣờng input.

Input.text : String -> (Signal Element, Signal String)

Để hiểu rõ hơn về tính tƣơng tác của tín hiệu đầu vào với giao diện ngƣời dùng ta xét một số ví dụ sau:

Ví dụ 1: Hiện tọa độ vị trí trỏ chuột khi di chuyển. 1 import Graphics.Element exposing(..)

2 import Signal exposing(Signal, map)

3 import Mouse

4 main : Signal Element

5 main = map show Mouse.position

38

Elm xử lý các sự kiện chuột bằng phƣơng tiện của cái gọi là tín hiệu. Khi di chuyển chuột thì chƣơng trình hiển thị tọa độ x của con trỏ chuột. Các giá trị thay đổi khi con trỏ thay đổi vị trí của nó Mouse.x biểu diễn cho một tín hiệu của trỏ chuột ở tọa độ x. Khi trỏ chuột thay đổi vị trí thì giá trị tín hiệu cũng thay đổi phù hợp.

Vị trí chỏ chuột đƣợc biểu diễn nhƣ một tín hiệu bởi cặp số nguyên, chỉ ra sự phối hợp khi di chuyển chuột.

Mouse.position : Signal (Int, Int)

Sẽ mất một một hàm chuyển giá trị tín hiệu a sang giá trị tín hiệu b. Map : (a -> b) -> signal a -> signal b

Trong ví dụ trên hàm show biểu diễn tọa độ chuột, hàm map chuyển giá trị một tín hiệu tọa độ sang một tín hiệu thành phần. Vị trí chuột thay đổi cũng sẽ làm các thành phần bắt đầu thay đổi theo nhƣ ở hình bên trên.

Ví dụ 2: Xét đoạn mã sau thể hiện sự phối hợp tọa độ chuột với tín hiệu của cửa sổ (Window.dimensions).

1 view (w,h) (x,y) = 2 container w h middle 3 <|color lightGray 4 <|collage 200 200 [

5 toForm(show " Chao mung ban den voi ELM ")

6 ]

7 main = map2 view Mouse.position Window.dimensions

Hàm view có 2 tham số và đƣợc truyền thông qua tín hiệu chuột và kích cỡ cửa sổ. Với tham số (x,y) sẽ phối hợp với cặp số nguyên của tín hiệu chuột và kích

39

thƣớc cửa sổ dẫn đến việc thay đổi tọa độ của dòng văn bản. Khi tín hiệu chuột thay đổi thì tọa độ dòng văn bản cũng thay đổi theo.

Ví dụ 3: Di chuyển một đối tƣợng bằng tín hiệu đầu vào là các phím mũi tên trên bàn phím (Keyboard.arrows). Ví dụ này thể hiện đƣợc kiến trúc mô hình lập trình phổ biến trong ngôn ngữ Elm.

1 import Graphics.Element exposing (..)

2 import Graphics.Collage exposing (..)

3 import Color exposing (..)

4 import Time exposing (..)

5 import Signal exposing (..)

6 import Mouse 7 import Window 8 import Keyboard

9 -- MODEL

10 type alias Keys = {x: Int, y: Int}

11 type alias Data = {x: Float, y: Float, vx: Float, vy: Float}

12 defaultGame : Data

13 defaultGame = { x = 0, y = 0, vx = 0, vy = 0}

14 -- UPDATE

15 update : (Float, Keys) -> Data -> Data 16 update (t, keys) obj = {obj|

17 x <- obj.x + toFloat (keys.x)*10, 18 y <- obj.y + toFloat (keys.y)*10

19 } 20 -- INPUT 21 input =

22 Signal.sampleOn delta (Signal.map2 (,) delta Keyboard.arrows)

23 delta = Signal.map (\t -> t/20) (fps 30)

24 -- VIEW

25 view (w,h) obj = container w h middle 26 <| color lightGray 27 <| collage 800 400 [

28 -- tạo một hình tròn mầu đỏ bán kính 20

29 filled red (circle 20)

30 |> move (obj.x, obj.y)

31 ] 32 -- SIGNAL

33 main = Signal.map2 view Window.dimensions gameState 34 gameState = Signal.foldp update defaultGame input

40

Lớp VIEW là lớp tạo giao diện cho chƣơng trình. Ví dụ này ta sẽ tạo một hình tròn bán kính 20 và mầu đỏ có thể di chuyển theo phím mũi tên trên bàn phím. Hình tròn này nằm trong một hình chữ nhật có chiều dài 800 và rộng 400 đƣợc định nghĩa bởi hàm collage. Hàm view đƣợc định nghĩa bởi hàm container với các thuộc tính là chiều rộng và cao của cửa sổ (w,h) và thuộc tính middle sẽ cho các thành phần bên trong nó ra giữa màn hình, ở đây là hình chữ nhật 800 x 400. Hình này đƣợc tô mầu xám nhẹ với hàmcolor.

Lớp MODEL sẽ khai báo các kiểu dữ liệu và khởi tạo giá trị các đối tƣợng cho chƣơng trình. Chƣơng trình của chúng ta có một đối tƣợng ta sẽ có các giá trị thuộc tính của hình tròn là {x: Float, y: Float, vx: Float, vy: Float}, ngoài ra ta khai báo thêm kiểu dữ liệu nhập vào từ bàn phím là một cặp số nguyên.

type alias Keys = {x: Int, y: Int}

Lớp INPUT thể hiện các tín hiệu đầu vào để phản ứng với đối tƣợng. Chƣơng trình sẽ có 2 tín hiệu đầu vào là delta và Keyboard.arrows. Hàm delta là hàm thời gian có chức năng làm cho hình ảnh đối tƣợng di chuyển một cách động hơn nhờ vào hàm (fps 30) thể hiện tỷ lệ khung hình diễn ra nhanh nhất có thể. Tín hiệu Keyboard.arrows sẽ di chuyển đối tƣợng.

Lớp UPDATE dữ liệu của đối tƣợng sẽ đƣợc cập nhật theo tín hiệu đầu vào là các phím mũi tên và tham số thời gian.

Lớp SIGNAL sử dụng các hàm thao tác với tín hiệu nhƣ hàm map, foldp. Hàm main sẽ thực thi chƣơng trình. Với chƣơng trình trên hàm main sử dụng hàm map2 vì hàm viewcó 2 tham số, hàm này có chức năng là kết hợp các tín hiệu thông qua giá trị hàm view. HàmSignal.foldp nhớ các giá trị của hàm update và trả về giá trị mới nhất của hàm dựa trên tín hiệu nhập vào.

41

2.3 Các ƣu điểm của ngôn ngữ Elm

Elm là ngôn ngữ lập trình hàm phản ứng có 2 tính năng chính: Hỗ trợ khai báo đơn giản cho lập trình hàm phản ứng luồng dữ liệu không đồng bộ và tạo giao diện chức năng đồ họa ngƣời dùng một cách dễ dàng.[5]

Lập trình hàm phản ứng không đồng bộ cho phép lập trình viên tạo ra các cơ chế xử lý sự kiện để ngƣời dùng có thể tác động ngay tức thì vào luồng dữ liệu mà không làm ảnh hƣởng tới các tiến trình xử lý logic khác thông qua giao diện ngƣời dùng. Việc tính toán trong thời gian dài có thể thực thi đƣợc luồng dữ liệu không đồng bộ và không ảnh hƣởng tới việc đáp ứng giao diện ngƣời sử dụng.

Giao diện Elm sử dụng cơ chế khai báo các hàm theo một chuẩn chung, giúp tạo và kết hợp văn bản, hình ảnh, video và hiển thị đồ họa một cách đa dạng phong phú.

Lập trình hàm phản ứng áp dụng cho các mô hình lập trình hàm giống nhau với các giá trị thời gian khác nhau đƣợc biết nhƣ là các tín hiệu (signals). Lập trình hàm phản ứng là một xu hƣớng mới hiện nay cho cách tiếp cận việc thực hiện giao diện đồ họa ngƣời dùng, nơi mà các giá trị biến đổi theo thời gian có thể biểu diễn đƣợc dữ liệu đầu vào và đầu ra gồm các tƣơng tác ngƣời sử dụng, các yêu cầu và đáp ứng trên server.

Hệ thống lập trình hàm phản ứng đồng thời sẽ giúp chúng ta giải quyết 2 việc đó là: Không cần thiết tính toán lại và chậm chễ toàn cục.

42

Một chậm chễ toàn cục (Global delay) xảy ra khi một giao diện tƣơng tác ngƣời dùng không thể ngay lập tức thực thi các sự kiện đƣợc ngƣời dùng tác động. Các công thức của hệ thống lập trình hàm trƣớc đây thƣờng duy trì quá trình xử lý tuần tự của các luồng dữ liệu dẫn đến việc phải chờ đợi xử lý xong từng bƣớc mới có thể xử lý tới bƣớc tiếp theo mà trong thực tế thì đôi khi điều này thực sự không cần thiết. [6]

Không cần thiết tính toán lại (Needless recomputation) xảy ra khi dữ liệu đầu vào không thay đổi thì hàm đó không nhất thiết phải tính toán lại. Trƣớc đây khi một trong các đầu vào của một chƣơng trình lập trình hàm thay đổi thì toàn bộ chƣơng trình sẽ đƣợc tính toán lại mặc dù hầu hết các dữ liệu đầu vào không thay đổi. Hệ thống chạy đồng thời của Elm sẽ giúp tránh đƣợc điều này vì trong Elm sự kiện xảy ra là rời rạc và các dòng dữ liệu đầu vào không liên quan sẽ không cần thiết tính toán lại.

Các ngữ nghĩa của ngôn ngữ lập trình hàm phản ứng gán với sự thay đổi tín hiệu liên tục. Do đó việc thực hiện các mẫu tín hiệu đầu vào nhanh nhất có thể và liên tục tính toán lại chƣơng trình với các giá trị mới nhất của các tín hiệu. Trong thực tế nhiều tín hiệu thay đổi kín đáo và không thƣờng xuyên và cùng với các mẫu bất biến dẫn đến việc tính toán lại là không cần thiết. Ngƣợc lại Elm giả định rằng tất cả các tín hiệu rời rạc và sử dụng các giả định này để phát hiện khi một tín hiệu là không thay đổi và trong trƣờng hợp đó không cần thiết tính toán lại.

Trong Elm các tín hiệu chỉ xảy ra khi một sự kiện rời rạc xảy ra. Một sự kiện xảy ra khi một chƣơng trình đầu vào (nhƣ vị trí của con chuột) thay đổi. Các sự kiện yêu cầu tính toán lại chƣơng trình và kết quả của chƣơng trình thay đổi.

Hệ thống lập trình hàm trƣớc đây yêu cầu các sự kiện phải đƣợc xử lý đồng bộ: Các sự kiện xảy ra theo thứ tự chính xác. Tuy nhiên xử lý một sự kiện có thể mất thời gian đáng kể dẫn đến kết quả có thể chậm trễ cho toàn bộ hệ thống lập trình hàm phản ứng.

43

Việc xử lý các đƣờng dẫn (Pipeling) sự kiện có thể giảm bớt độ chậm trễ, nhƣng việc các sự kiện đƣợc lên thứ tự trƣớc cần phải đƣợc tuân thủ và một sự kiện không thể hoàn thành đến tận khi một sự kiện trƣớc đƣợc kết thúc. Trong các giao diện tƣơng tác ngƣời dùng việc tuân thủ quá trình xử lý sự kiện sẽ bị hạn chế vì giao diện ngƣời dùng cần phải đƣợc đáp ứng liên tục, thậm chí một hành động của ngƣời dùng trƣớc đó đƣợc kích hoạt và hoạt động trong một tính toán lâu dài.

Elm cung cấp cho ngƣời lập trình các nhìn trừu tƣợng đơn giản để xác định và tính toán các sự kiện diễn ra không đồng bộ. Bằng việc kết hợp các đƣờng dẫn xử lý, các cách thực thi các sự kiện cho phép xử lý đồng thời, ngay tức thì với các sự kiện khác nhau, tránh sự chập chễ toàn cục và cho phép các giao diện ngƣời dùng duy trì việc đáp ứng lại với ngƣời dùng. Khả năng xác định các tính toán không đồng bộ trong mô hình FRP chính là chìa khóa mới lạ.

2.4 Các thao tác với tín hiệu trong Elm

Một chƣơng trình Elm có thể coi là một đồ thị tín hiệu, các thao tác trong chƣơng trình chủ yếu là thao tác với tín hiệu. Tín hiệu nhƣ là giá trị của dòng dữ

Một phần của tài liệu Nghiên cứu lập trình phản ứng với ngôn ngữ ELM (Trang 33)