F #: cho phép biến đổi so với tham chiếu


82

Đầu tiên, tôi thừa nhận khả năng rằng câu hỏi này có thể là một bản sao; hãy cho tôi biết.

Tôi tò mò "phương pháp hay nhất" nói chung là gì cho những tình huống khi mong muốn khả năng gây đột biến. F # dường như cung cấp hai phương tiện cho việc này: let mutableràng buộc, dường như hoạt động giống như các biến trong các ngôn ngữ "hầu hết" và ô tham chiếu (được tạo bằng refhàm) yêu cầu sử dụng tham chiếu rõ ràng.

Có một vài trường hợp mà người ta bị "buộc" vào cái này hay cái kia: .NET interop có xu hướng sử dụng có thể thay đổi với <-và trong tính toán quy trình làm việc, người ta phải sử dụng refvới :=. Vì vậy, những trường hợp đó khá rõ ràng, nhưng tôi tò mò không biết phải làm gì khi tạo các biến có thể thay đổi của riêng tôi bên ngoài các tình huống đó. Một phong cách có lợi thế gì hơn phong cách khác? (Có lẽ cái nhìn sâu sắc hơn về việc triển khai sẽ hữu ích.)

Cảm ơn!



4
Lưu ý rằng trong F # phiên bản 4, có thể thay đổi có thể được sử dụng ở những nơi bạn từng cần tham chiếu. blogs.msdn.com/b/fsharpteam/archive/2014/11/12/…
James Moore

Câu trả lời:


133

Tôi chỉ có thể ủng hộ những gì gradbot đã nói - khi tôi cần đột biến, tôi thích hơn let mutable.

Về việc triển khai và sự khác biệt giữa hai refô - về cơ bản được thực hiện bởi một bản ghi rất đơn giản có chứa trường bản ghi có thể thay đổi. Bạn có thể tự mình viết chúng một cách dễ dàng:

type ref<'T> =  // '
  { mutable value : 'T } // '

// the ref function, ! and := operators look like this:
let (!) (a:ref<_>) = a.value
let (:=) (a:ref<_>) v = a.value <- v
let ref v = { value = v }

Một sự khác biệt đáng chú ý giữa hai cách tiếp cận là let mutablelưu trữ giá trị có thể thay đổi trên ngăn xếp (như một biến có thể thay đổi trong C #) trong khi reflưu trữ giá trị có thể thay đổi trong một trường của bản ghi được phân bổ theo heap. Điều này có thể có một số tác động đến hiệu suất, nhưng tôi không có bất kỳ con số nào ...

Nhờ đó, các giá trị có thể thay đổi được sử dụng refcó thể được đặt biệt danh - nghĩa là bạn có thể tạo hai giá trị tham chiếu cùng một giá trị có thể thay đổi:

let a = ref 5  // allocates a new record on the heap
let b = a      // b references the same record
b := 10        // modifies the value of 'a' as well!

let mutable a = 5 // mutable value on the stack
let mutable b = a // new mutable value initialized to current value of 'a'
b <- 10           // modifies the value of 'b' only!

2
Chỉ cần một lời nhắc nhở: là trên stack hay trên heap là một chi tiết thực hiện và không chính xác liên quan đến câu hỏi (nhưng câu trả lời tuyệt vời dù sao)
Bruno Brant

5
Tôi sẽ tranh luận rằng việc biết liệu điều gì đó có phát sinh chi phí phân bổ và thu thập đống hay không là cực kỳ phù hợp khi quyết định xem phương pháp hay nhất là gì.
jackmott

@jackmott hãy xem bài viết này của Eric Lippert được gọi là Ngăn xếp là chi tiết triển khai
jaromey 14/04

5
@jaromey vâng Tôi hiểu rằng về cơ bản tôi hoàn toàn không đồng ý với eric về điểm đó. Bạn không thể bỏ qua các cân nhắc về hiệu suất khi xem xét đâu là 'phương pháp hay nhất'. Người ta thường tuyên bố rằng hiệu suất không quan trọng vì vậy nhiều phần mềm bị chậm do chết từ hàng nghìn chi tiết triển khai.
jackmott

18

Câu hỏi liên quan: "Bạn đã đề cập rằng các giá trị có thể thay đổi cục bộ không thể được nắm bắt bởi một bao đóng, vì vậy bạn cần sử dụng ref thay thế. Lý do cho điều này là các giá trị có thể thay đổi được thu thập trong bao đóng cần được cấp phát trên heap (vì bao đóng được cấp phát trên đống). " từ F # ref-mutable vars so với các trường đối tượng

Tôi nghĩ let mutableđược ưu tiên hơn các ô tham chiếu. Cá nhân tôi chỉ sử dụng các ô tham chiếu khi chúng được yêu cầu.

Hầu hết mã tôi viết không sử dụng các biến có thể thay đổi nhờ vào các lệnh gọi đệ quy và đuôi. Nếu tôi có một nhóm dữ liệu có thể thay đổi, tôi sử dụng một bản ghi. Đối với các đối tượng tôi sử dụng let mutableđể tạo các biến có thể thay đổi riêng tư. Tôi chỉ thực sự sử dụng các ô tham chiếu cho các bao đóng, nói chung là các sự kiện.


9

Như được mô tả trong bài viết Blog MSDN này trong phần Sử dụng đơn giản các giá trị có thể thay đổi , bạn không cần ô tham chiếu cho lambdas nữa. Vì vậy, nói chung bạn không còn cần chúng nữa.


4

Bài viết này của Brian có thể cung cấp một câu trả lời.

Mutables rất dễ sử dụng và hiệu quả (không cần gói), nhưng không thể bắt được trong lambdas. Các ô tham chiếu có thể được ghi lại, nhưng dài dòng và kém hiệu quả hơn (? - không chắc chắn về điều này).


"(...) và kém hiệu quả hơn" - có thể, một loại trình bao bọc chiếm nhiều bộ nhớ hơn.
JMCF125,

2
Điều này đã thay đổi, vì F # 4.0 có thể thay đổi được trong hầu hết các trường hợp và nhu cầu về ref giờ đây ít hơn nhiều.
Abel

3

Bạn có thể muốn xem phần Dữ liệu có thể thay đổi trong wikibook.

Để thuận tiện, đây là một số trích dẫn có liên quan:

Từ khóa có thể thay đổi thường được sử dụng với các loại bản ghi để tạo bản ghi có thể thay đổi

Các biến có thể thay đổi được hơi hạn chế: các biến có thể thay đổi không thể truy cập được bên ngoài phạm vi của hàm mà chúng được xác định. Cụ thể, điều này có nghĩa là không thể tham chiếu đến một biến thể trong một chức năng con của một chức năng khác.

Các ô tham chiếu có một số hạn chế của các biến. Trên thực tế, các ô ref là kiểu dữ liệu rất đơn giản bao bọc một trường có thể thay đổi trong một kiểu bản ghi.

Vì các ô tham chiếu được phân bổ trên heap, chúng có thể được chia sẻ trên nhiều chức nă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.