2010-02-08 34 views
32

Kết hợp mẫu trong Haskell là gì và nó liên quan đến phương trình được bảo vệ như thế nào?So khớp mẫu Haskell - nó là gì?

Tôi đã thử tìm kiếm một lời giải thích đơn giản, nhưng tôi chưa tìm thấy lời giải thích nào.

EDIT: Ai đó được gắn thẻ làm bài tập về nhà. Tôi không đi học nữa, tôi chỉ học Haskell và tôi đang cố hiểu khái niệm này. Tinh khiết không quan tâm.

+4

Có lẽ cũng nên bao gồm khái niệm đối sánh mẫu trong F # ... –

+1

Hàng nghìn ngôn ngữ có mẫu khớp, không chỉ Haskell và F #. – Joe

+1

Đó là một tính năng phổ biến của các ngôn ngữ có chức năng thuần túy và hạn chế. Ví dụ: Prolog, Erlang và SML. – outis

Trả lời

52

Tóm lại, các mẫu giống như xác định các hàm piecewise trong toán học. Bạn có thể chỉ định các hàm chức năng khác nhau cho các đối số khác nhau bằng cách sử dụng các mẫu. Khi bạn gọi một hàm, cơ thể thích hợp được chọn bằng cách so sánh các đối số thực tế với các mẫu đối số khác nhau. Đọc A Gentle Introduction to Haskell để biết thêm thông tin.

Hãy so sánh:

Fibonacci sequence

với Haskell tương đương:

fib 0 = 1 
fib 1 = 1 
fib n | n >= 2 
     = fib (n-1) + fib (n-2) 

Lưu ý "n ≥ 2" trong chức năng piecewise trở thành một người bảo vệ trong phiên bản Haskell, nhưng hai điều kiện khác chỉ đơn giản là các mẫu. Mẫu là điều kiện kiểm tra các giá trị và cấu trúc, chẳng hạn như x:xs, (x, y, z) hoặc Just x. Theo định nghĩa từng phần, các điều kiện dựa trên quan hệ = hoặc (về cơ bản, các điều kiện nói điều gì đó "là" cái gì đó khác) trở thành các mẫu. Các vệ sĩ cho phép điều kiện chung hơn. Chúng ta có thể viết lại fib để sử dụng bảo vệ:

fib n | n == 0 = 1 
     | n == 1 = 1 
     | n >= 2 = fib (n-1) + fib (n-2) 
+0

chưa bao giờ thấy giải thích tốt hơn về điều này! +1. [Tài liệu F #] này (http://msdn.microsoft.com/en-us/library/dd547125.aspx) cũng rất tuyệt. – nawfal

3

Trong một ngôn ngữ chức năng, mô hình phù hợp liên quan đến việc kiểm tra một cuộc tranh cãi đối với các hình thức khác nhau. Một ví dụ đơn giản liên quan đến các hoạt động được xác định đệ quy trên các danh sách. Tôi sẽ sử dụng OCaml để giải thích sự khớp mẫu vì nó là ngôn ngữ chức năng của tôi, nhưng các khái niệm giống nhau trong F # và Haskell, AFAIK.

Đây là định nghĩa của hàm để tính toán độ dài của danh sách lst. Trong OCaml, một `` một danh sách is defined recursively as the empty list [] , or the structure h :: t , where h is an element of type a ( a being any type we want, such as an integer or even another list), t is a list (hence the recursive definition), and :: `là toán tử toán tử, tạo danh sách mới ra khỏi phần tử và danh sách.

Vì vậy, các chức năng sẽ trông như thế này:

let rec len lst = 
    match lst with 
    [] -> 0 
    | h :: t -> 1 + len t 

rec là một modifier mà nói OCaml rằng một hàm sẽ gọi riêng của mình một cách đệ quy. Đừng lo về phần đó. Tuyên bố match là những gì chúng tôi đang tập trung vào. OCaml sẽ kiểm tra lst đối với hai mẫu - danh sách trống hoặc h :: t - và trả về một giá trị khác dựa trên đó. Vì chúng tôi biết mọi danh sách sẽ khớp với một trong các mẫu này, chúng tôi có thể yên tâm rằng chức năng của chúng tôi sẽ trở lại an toàn.

Lưu ý rằng mặc dù hai mẫu này sẽ xử lý tất cả các danh sách, nhưng bạn không bị giới hạn đối với chúng. Một mẫu như h1 :: h2 :: t (khớp với tất cả các danh sách có độ dài từ 2 trở lên) cũng hợp lệ.

Tất nhiên, việc sử dụng các mẫu không bị hạn chế đối với các cấu trúc dữ liệu được xác định đệ quy hoặc các hàm đệ quy.Dưới đây là một hàm (contrived) để cho bạn biết số có là 1 hay 2:

let is_one_or_two num = 
    match num with 
    1 -> true 
    | 2 -> true 
    | _ -> false 

Trong trường hợp này, các dạng của mẫu của chúng tôi là số. _ là dạng bắt giữ đặc biệt được sử dụng làm trường hợp mặc định, trong trường hợp không có mẫu nào ở trên phù hợp.

12

Kết hợp mẫu là, ít nhất trong Haskell, gắn liền với khái niệm algebraic data types. Khi bạn khai báo một kiểu dữ liệu như thế này:

data SomeData = Foo Int Int 
       | Bar String 
       | Baz 

... nó định nghĩa Foo, Bar, và Baz như constructors --not nên nhầm với "nhà xây dựng" trong OOP - đó xây dựng một giá trị SomeData ngoài các giá trị khác.

mẫu phù hợp là gì khác hơn là làm điều này ngược lại --một mẫu sẽ "mổ xẻ" một giá trị SomeData thành từng miếng cấu thành của nó (trên thực tế, tôi tin rằng mô hình phù hợp là chỉ cách để trích xuất các giá trị trong Haskell).

Khi có nhiều hàm tạo cho một kiểu, bạn viết nhiều phiên bản của một hàm cho mỗi mẫu, với đúng kiểu được chọn tùy thuộc vào hàm tạo nào được sử dụng (giả sử bạn đã viết mẫu để khớp với tất cả các cấu trúc có thể-- mà thường là thực hành tốt để làm).

+1

"Nhiều phiên bản của một hàm" thực sự chỉ là cú pháp đường cho câu lệnh case: http://learnhaskell.blogspot.com/2007/09/lesson-3-case-3.html –

1

Kết hợp mẫu là một trong những hoạt động đau đớn khó có thể có được đầu nếu bạn đến từ nền lập trình thủ tục. Tôi thấy khó có thể tham gia vì cú pháp tương tự được sử dụng để tạo cấu trúc dữ liệu có thể được sử dụng để khớp.

Trong F # bạn có thể sử dụng các khuyết điểm điều hành :: để thêm một yếu tố để khởi đầu của một danh sách như vậy:

let a = 1 :: [2;3] 
//val a : int list = [1; 2; 3] 

Tương tự như vậy bạn có thể sử dụng toán tử cùng chia danh sách lên như vậy:

let a = [1;2;3];; 
match a with 
    | [a;b] -> printfn "List contains 2 elements" //will match a list with 2 elements 
    | a::tail -> printfn "Head is %d" a //will match a list with 2 or more elements 
    | [] -> printfn "List is empty" //will match an empty list 
+1

"... cùng cú pháp được sử dụng để tạo ra một cấu trúc dữ liệu có thể được sử dụng để phù hợp với "- đó là khá nhiều điểm; bạn sử dụng đối sánh mẫu để tìm ra hàm tạo nào được sử dụng để tạo ra một giá trị - xem câu trả lời của Norman Ramsey hoặc camccann. Nó cũng giúp nhận ra rằng nhược điểm không chỉ đơn thuần là một hàm hoạt động trên các danh sách (như chiều dài hoặc nối), mà còn là một hàm tạo danh sách. Tất nhiên, như các ví dụ Fibonacci hiển thị, bạn có thể khớp mẫu trên các giá trị cụ thể như 0 hoặc 1 cũng như các hàm tạo như khuyết điểm hoặc 'Chỉ' (một giá trị phổ biến từ Haskell). – Nefrubyr

23

Có những câu trả lời hay khác, vì vậy tôi sẽ cung cấp cho bạn câu trả lời rất kỹ thuật. Mô hình kết hợp việc loại bỏ xây dựng cho kiểu dữ liệu đại số:

  • "Xoá bỏ xây dựng" có nghĩa là "làm thế nào để tiêu thụ hoặc sử dụng một giá trị"

  • "đại số kiểu dữ liệu", ngoài các hàm hạng nhất, là ý tưởng lớn trong một ngôn ngữ chức năng được gõ tĩnh như Clean, F #, Haskell, hoặc ML

Ý tưởng về các kiểu dữ liệu đại số là rằng bạn định nghĩa một loại điều, và bạn nói tất cả các cách bạn có thể làm điều đó. Như một ví dụ, chúng ta hãy định nghĩa "Chuỗi các String" như một kiểu dữ liệu đại số, với ba cách để làm cho nó:

data StringSeq = Empty     -- the empty sequence 
       | Cat StringSeq StringSeq -- two sequences in succession 
       | Single String   -- a sequence holding a single element 

Giờ đây, có tất cả các loại điều sai trái với định nghĩa này, nhưng là một ví dụ thật thú vị bởi vì nó cung cấp liên tục thời gian của chuỗi các chiều dài tùy ý. (Có nhiều cách khác để đạt được điều này.) Tuyên bố giới thiệu Empty, CatSingle, tất cả các cách có tạo trình tự. (Điều đó làm cho mỗi người một giới thiệu xây dựng — một cách để làm cho mọi việc.)

  • Bạn có thể thực hiện một chuỗi rỗng mà không cần bất kỳ giá trị khác.
  • Để tạo chuỗi với Cat, bạn cần hai trình tự khác.
  • Để thực hiện một chuỗi với Single, bạn cần một yếu tố (trong trường hợp này một chuỗi)

Ở đây có những điểm mấu chốt: các cấu trúc loại bỏ, mô hình phù hợp, mang lại cho bạn một cách để rà soát một chuỗi và yêu cầu đó là câu hỏi bạn đã tạo ra hàm tạo nào?. Bởi vì bạn phải chuẩn bị cho bất kỳ câu trả lời nào, bạn cung cấp ít nhất một giải pháp thay thế cho mỗi hàm tạo. Dưới đây là một chức năng chiều dài:

slen :: StringSeq -> Int 
slen s = case s of Empty -> 0 
        Cat s s' -> slen s + slen s' 
        Single _ -> 1 

Tại cốt lõi của ngôn ngữ, tất cả các mô hình phù hợp được xây dựng trên case cấu trúc này. Tuy nhiên, do kiểu dữ liệu đại số và mô hình kết hợp rất quan trọng đối với thành ngữ của ngôn ngữ, có đặc biệt "cú pháp đường" để thực hiện mô hình kết hợp trong tờ khai của một định nghĩa hàm:

slen Empty = 0 
slen (Cat s s') = slen s + slen s' 
slen (Single _) = 1 

Với đường cú pháp này, tính toán bằng cách khớp mẫu trông giống như định nghĩa bằng phương trình. (Ủy ban Haskell đã làm điều này có chủ đích.) Và như bạn có thể thấy trong các câu trả lời khác, có thể chuyên hoặc là một phương trình hoặc một thay thế trong một biểu thức case bằng cách tát một bảo vệ trên đó. Tôi không thể nghĩ ra một người bảo vệ hợp lý cho ví dụ trình tự, và có rất nhiều ví dụ trong các câu trả lời khác, vì vậy tôi sẽ để nó ở đó.

Các vấn đề liên quan