Các hàm ML từ danh sách đa hình đến danh sách đa hình


8

Tôi đang học lập trình trong ML (OCaml), và trước đó tôi đã hỏi về các loại ML'a -> 'b . Bây giờ tôi đã thử nghiệm một chút với các chức năng của loại 'a list -> 'b list. Có một số ví dụ đơn giản rõ ràng:

let rec loop l = loop l
let return_empty l = []
let rec loop_if_not_empty = function [] -> []
                                   | l -> loop_if_not_empty l

Những gì tôi không thể tìm ra là làm thế nào để tạo ra một chức năng làm việc gì đó ngoài việc trả về danh sách hoặc vòng lặp trống (mà không sử dụng bất kỳ chức năng thư viện nào). Điều này có thể được thực hiện? Có cách nào để trả về danh sách không trống?

Chỉnh sửa: Có, nếu tôi có chức năng loại 'a -> 'b, thì tôi có thể tạo một loại khác hoặc chức năng của loại 'a list -> 'b list, nhưng điều tôi thắc mắc ở đây là làm thế nào để tạo ra loại đầu tiên.


1
Cũng như câu hỏi trước, vui lòng nhắm mục tiêu chương trình học tập của sinh viên CS101 vào câu trả lời của bạn, chứ không phải nhà lý thuyết loại mà câu trả lời của bạn có thể truyền cảm hứng cho anh ta sau này trở thành.
Gilles 'SO- ngừng trở nên xấu xa'

Lưu ý rằng nếu bạn có hàm f với loại này trả về một danh sách không trống thì vui vẻ a -> List.hd (f [a]) sẽ có loại 'a ->' b mà không bị chấm dứt hoặc đưa ra ngoại lệ.
gallais

Câu trả lời:


6

Chà, một cái gì đó được gọi là tham số cho chúng ta biết rằng nếu chúng ta xem xét tập con thuần túy của ML (nghĩa là không có đệ quy vô hạn refvà tất cả những thứ kỳ lạ đó), không có cách nào để định nghĩa một hàm với loại này ngoài loại trả về trống danh sách.

Tất cả điều này bắt đầu với các Định lý về giấy của Wadler miễn phí! Mùi. Bài viết này, về cơ bản, cho chúng ta hai điều:

  1. Nếu chúng ta xem xét các ngôn ngữ lập trình thỏa mãn một số điều kiện nhất định, chúng ta có thể suy ra một số định lý hay chỉ bằng cách xem chữ ký loại của hàm đa hình (cái này được gọi là Định lý tham số).
  2. ML (không có đệ quy vô hạn, refvà tất cả những thứ kỳ lạ đó) thỏa mãn những điều kiện đó.

Từ Parametricity lý chúng ta đều biết rằng nếu chúng ta có một chức năng f : 'a list -> 'b list, sau đó cho tất cả 'a, 'b, 'c, 'dvà cho tất cả các chức năng g : 'a -> 'c, h : 'b -> 'dchúng ta có:

map h ∘ f = f ∘ map g

(Lưu ý, fbên trái có loại 'a list -> 'b listfbên phải là 'c list -> 'd list.)

Chúng tôi có thể tự do lựa chọn bất cứ điều gì gchúng tôi thích, vì vậy hãy để 'a = 'cg = id. Bây giờ vì map id = id(dễ dàng chứng minh bằng cảm ứng về định nghĩa map), chúng ta có:

map h ∘ f = f

Bây giờ hãy để 'b = 'd = boolh = not. Chúng ta hãy giả sử cho một số zs : bool listđiều đó xảy ra rằng f zs ≠ [] : bool list. Rõ ràng là map not ∘ f = fkhông phải giữ, bởi vì

(map not ∘ f) zs ≠ f zs

Nếu phần tử đầu tiên của danh sách bên phải là true, thì bên trái phần tử đầu tiên là falsevà ngược lại!

Điều này có nghĩa, giả định của chúng tôi là sai và f zs = []. Chúng ta làm xong chưa? Không.

Chúng tôi cho rằng đó 'bbool. Chúng tôi đã chỉ ra rằng khi fđược gọi với kiểu f : 'a list -> bool listcho bất kỳ 'a, fphải luôn trả về danh sách trống. Có thể là khi chúng ta gọi fnhư f : 'a list -> unit listnó trả lại một cái gì đó khác nhau? Trực giác của chúng tôi cho chúng tôi biết rằng điều này là vô nghĩa: chúng tôi không thể viết bằng ML thuần túy một hàm luôn trả về danh sách trống khi chúng tôi muốn nó cung cấp cho chúng tôi một danh sách các booleans và có thể trả về một danh sách không trống! Nhưng đây không phải là một bằng chứng.

Những gì chúng ta muốn nói là fthống nhất : nếu nó luôn luôn trả về danh sách trống cho bool list, sau đó nó phải trả lại danh sách trống cho unit listvà, nói chung, bất kỳ 'a list. Đây chính xác là điểm thứ hai trong danh sách dấu đầu dòng trong câu trả lời của tôi.

Bài viết cho chúng ta biết rằng trong ML fphải lấy các giá trị liên quan đến các giá trị liên quan . Tôi không đi sâu vào chi tiết về các mối quan hệ, đủ để nói rằng các danh sách có liên quan khi và chỉ khi chúng có độ dài bằng nhau và các yếu tố của chúng có liên quan đến cặp đôi (nghĩa là [x_1, x_2, ..., x_m][y_1, y_2, ..., y_n]có liên quan khi và chỉ khi m = nx_1chỉ liên quan đến y_1x_2có liên quan đến y_2và như vậy). Và phần thú vị là, trong trường hợp của chúng tôi, vì flà đa hình, chúng tôi có thể xác định bất kỳ mối quan hệ nào trên các yếu tố của danh sách!

Hãy chọn bất kỳ 'a, 'bvà nhìn vào f : 'a list -> 'b list. Bây giờ nhìn vào f : 'a list -> bool list; chúng tôi đã chỉ ra rằng trong trường hợp này fluôn trả về danh sách trống. Bây giờ chúng tôi yêu cầu tất cả các yếu tố 'aliên quan đến bản thân họ (hãy nhớ rằng, chúng tôi có thể chọn bất kỳ mối quan hệ nào chúng tôi muốn), điều này ngụ ý rằng bất kỳ yếu tố nào zs : 'a listcó liên quan đến chính nó. Như chúng ta biết, flấy các giá trị liên quan đến các giá trị liên quan, điều này có nghĩa f zs : 'b listlà có liên quan f zs : bool list, nhưng danh sách thứ hai có độ dài bằng 0 và vì danh sách đầu tiên có liên quan đến nó, nên nó cũng trống.


Để đầy đủ, tôi sẽ đề cập rằng có một phần về tác động của đệ quy chung (có thể không chấm dứt) trong bài báo gốc của Wadler, và cũng có một bài viết tìm hiểu các định lý miễn phí với sự có mặt của seq.


Bây giờ tôi nghi ngờ bằng chứng có thể được thực hiện trong một bước nếu thay vì làm suy yếu định lý tham số bằng cách xem xét các quan hệ cụ thể gây ra bởi các hàm (ghtrong trường hợp này) đi thẳng với quan hệ chung được tạo ra tùy chỉnh
kirelagin

Nitpick, tham số không bắt đầu với bài báo của Wadler (mà tuyên bố là một bản tóm tắt các phương pháp để xác định tham số). Ý tưởng bắt nguồn từ bài báo của Reynold "Các loại, trừu tượng hóa và đa hình tham số." Ý tưởng này cũng có mặt trong bằng chứng bình thường hóa của Girard cho Hệ thống F theo như tôi biết.
Daniel Gratzer

4

Chúng ta hãy quay lại với các đối tượng đơn giản hơn: bạn không thể xây dựng một đối tượng đúng kiểu 'avì khi đó nó có nghĩa là đối tượng này xcó thể được sử dụng bất cứ nơi nào 'aphù hợp. Và điều đó có nghĩa là ở khắp mọi nơi: như một số nguyên, một mảng, thậm chí là một hàm. Ví dụ, điều đó có nghĩa là bạn có thể làm những việc như x+2, x.(1)(x 5). Các loại tồn tại chính xác để ngăn chặn điều này.

Đây là ý tưởng tương tự áp dụng với một hàm kiểu 'a -> 'b, nhưng có một số trường hợp loại này có thể tồn tại: khi hàm không bao giờ trả về một đối tượng kiểu 'b: khi lặp hoặc đưa ra một ngoại lệ.

Điều này cũng áp dụng cho các chức năng trả về một danh sách. Nếu chức năng của bạn thuộc loại t -> 'b listvà bạn xây dựng một đối tượng loại tvà áp dụng nó cho chức năng này, thì điều đó có nghĩa là nếu bạn truy cập thành công một phần tử của danh sách này thì bạn sẽ truy cập vào một đối tượng có tất cả các loại. Vì vậy, bạn không thể truy cập bất kỳ yếu tố nào của danh sách: danh sách trống hoặc ... không có danh sách.

Tuy nhiên, loại 'a list -> 'b listxuất hiện trong các bài tập thông thường nhưng chỉ khi bạn đã có chức năng của loại 'a -> 'b:

let rec map (f : 'a -> 'b) =
  function
  | [] -> []
  | x :: xs -> f x :: map f xs

Nhưng bạn có thể biết điều này.

val map : ('a -> 'b) -> 'a list -> 'b list

1
Các nhà lý thuyết loại cũ ít hơn hồi hộp bởi câu trả lời này. Ok, bối cảnh biến loại không trống là một cách để có một hàm theo nghĩa đen của loại 'a -> 'bhoặc 'a list -> 'b list, nhưng đó không phải là một quan sát thú vị. Trong thực tế, tôi sẽ chỉnh sửa câu hỏi để làm rõ rằng đây không phải là điều mà các sinh viên trẻ đang học lập trình đang thắc mắc.
Gilles 'SO- đừng trở nên xấu xa'

Nhưng các nhà lý thuyết loại cũ hơn biết rằng ML không phải là thiếu sót về mặt logic. Nếu bạn có thể tạo ra một chức năng f : 'a list -> 'b listtnhư vậy f t <> []thì chương trình này sẽ gõ kiểm tra nhưng có thể làm cách tồi tệ hơn việc nâng cao một ngoại lệ: let y = List.hd (f t) in (y y) (y + y.(0) + y.(0).(0)).
jmad

2

Định lý tham số từ định lý của Free miễn phí! giấy cho chúng ta biết các thuật ngữ ML có một thuộc tính rất đặc biệt: nếu chúng ta xem loại thuật ngữ là mối quan hệ với các giá trị của loại này, thì giá trị của thuật ngữ này sẽ liên quan đến chính nó. Dưới đây là cách xem các loại như quan hệ:

  • Một loại hàm 'a -> 'btương ứng với mối quan hệ được xác định bằng cách nói rằng hai hàm có liên quan nếu chúng lấy các giá trị liên quan đến các giá trị liên quan (giả sử'a'btương ứng với một số quan hệ).
  • Một loại danh sách 'a list tương ứng với mối quan hệ được xác định bằng cách nói rằng hai danh sách có liên quan nếu chúng có cùng độ dài và các yếu tố khớp của chúng có liên quan với nhau (giả sử 'atương ứng với một số mối quan hệ).
  • (Bây giờ là phần thú vị nhất.) Một loại đa hình tương ứng với mối quan hệ được xác định bằng cách nói rằng hai giá trị đa hình có liên quan nếu chúng ta có thể chọn bất kỳ hai loại, bất kỳ loại nào mối quan hệ giữa các yếu tố của các loại này, thay thế tất cả các trường hợp của biến loại bằng mối quan hệ và các giá trị kết quả sẽ vẫn liên quan.

foo : 'a -> 'afooA1A2Aa1:A1a2:A2Afooa1fooa2A

a1Aa2fooa1Afooa2.

Af:A1A2

f(a1)=a2f(fooa1)=fooa2,

hoặc, nói cách khác:

f(fooa1)=foo(f(a1)),

đó chính xác là định lý miễn phí cho idhàm : f . id = id . f.


foo : 'a list -> 'b listA1A2AB1B2BA1A2B1B2

Bây giờ chúng tôi sử dụng điều này để chứng minh rằng đối với hai loại bất kỳ ABhàm footrả về một danh sách trống cho bất kỳ đầu vào nào as : A list.

  • Để cho Một1= =Một2= = A và để Một là mối quan hệ danh tính, do đó, bất kỳ danh sách Một có liên quan tầm thường với chính nó.
  • Để cho B1= = Ø, B2= = BB bất kỳ mối quan hệ nào giữa chúng (chỉ có một, trống rỗng, nhưng không thành vấn đề).
  • ascó liên quan đến chính nó (như chúng ta đã chọn quan hệ nhận dạng trên A), do đó foo as : Ø listcó liên quan đến foo as : B list.
  • Chúng tôi biết rằng hai danh sách chỉ có thể liên quan nếu độ dài của chúng bằng nhau và chúng tôi cũng biết rằng danh sách đầu tiên của danh sách kết quả phải trống, vì không thể có các phần tử của Øloại.

Do đó, đối với bất kỳ A, Bas : A listchúng tôi có đó foo as : B listphải là một danh sách trống.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.