Nếu bạn đã có một phiên bản bắt buộc, bạn có thể follow a set of small steps to refector to a recursive implementation.
Recursion
Trong khi tôi không biết những gì phiên bản bắt buộc của bạn trông như thế nào, đây là một phiên bản đệ quy:
let pack xs =
let rec imp acc = function
| [] -> acc
| h::t ->
match acc with
| [] -> imp [(h, 1)] t
| (i, count) :: ta ->
if h = i
then imp ((i, count + 1) :: ta) t
else imp ((h, 1) :: (i, count) :: ta) t
xs |> imp [] |> List.rev
Chức năng này có kiểu 'a list -> ('a * int) list when 'a : equality
. Nó sử dụng một chức năng thực hiện riêng được gọi là imp
để thực hiện công việc. Chức năng này là đệ quy, và đề một bộ tích lũy (gọi là acc
) trong suốt. Bộ tích lũy này là danh sách kết quả, có loại ('a * int) list
.
Nếu danh sách ắc là trống rỗng, người đứng đầu danh sách ban đầu (h
), cũng như các tính 1
, được tạo ra như một tuple là yếu tố duy nhất của ắc cập nhật, và các chức năng imp
được đệ quy gọi với bộ tích lũy được cập nhật đó.
Nếu bộ tích lũy đã chứa ít nhất một phần tử, phần tử được trích xuất thông qua khớp mẫu và phần tử trong bộ tóan đó (i
) được so sánh với h
. Nếu h = i
, bộ tích lũy được cập nhật; nếu không, một tuple mới sẽ được chấp nhận trên acc
. Trong cả hai trường hợp, mặc dù, imp
được gọi đệ quy với bộ tích lũy mới.
Bạn có thể gọi nó với một danh sách tương đương với tuple ban đầu của bạn như thế này:
> pack [1; 2; 2; 3; 2; 2; 2; 4];;
val it : (int * int) list = [(1, 1); (2, 2); (3, 1); (2, 3); (4, 1)]
Fold
Một khi bạn có một phiên bản đệ quy, bạn thường có công thức cho một phiên bản sử dụng một gập lại. Trong trường hợp này, vì chức năng trên pack
phải đảo ngược bộ tích lũy cuối cùng (sử dụng List.rev
), phải gấp là phù hợp nhất.Trong F #, điều này được thực hiện với sự tích hợp List.foldBack
chức năng:
let pack' xs =
let imp x = function
| (i, count) :: ta when i = x -> (i, count + 1) :: ta
| ta -> (x, 1) :: ta
List.foldBack imp xs []
Trong trường hợp này, các hàm được chuyển vào List.foldBack
là một chút quá phức tạp để vượt qua như một chức năng ẩn danh, vì vậy tôi đã chọn để xác định nó như là một chức năng bên trong riêng. Nó tương đương với hàm đệ quy imp
được sử dụng bởi hàm pack
ở trên, nhưng bạn sẽ nhận thấy rằng nó không phải gọi chính nó một cách đệ quy. Thay vào đó, nó chỉ phải trả về giá trị mới cho bộ tích lũy.
Kết quả là như nhau:
> pack' [1; 2; 2; 3; 2; 2; 2; 4];;
val it : (int * int) list = [(1, 1); (2, 2); (3, 1); (2, 3); (4, 1)]
Câu trả lời hay. Một vài điều nhỏ thay đổi có thể làm cho nó thậm chí còn rõ ràng hơn, theo ý kiến của tôi. Bạn có thể thay thế 'let imp x acc = match acc ...' bằng 'let imp x = function ...' để tránh đặt tên một định danh mà bạn vừa phá hủy ngay lập tức. Quan trọng hơn, bạn có thể chuyển đổi thứ tự của các trường hợp khớp mẫu và sử dụng mệnh đề 'when' để hợp nhất các trường hợp' [] 'và' x <> i': '| (i, đếm) :: ta khi i = x -> (i, đếm + 1) :: ta | ta -> (x, 1) :: ta'. – kvb
@kvb Đó là những đơn giản hóa rất hay! Cảm ơn bạn. Đã cập nhật câu trả lời. –