F #: нехай змінюється проти посилання


83

По-перше, я визнаю можливість того, що це питання може бути дублікатом; просто дайте мені знати.

Мені цікаво, якою є загальна "найкраща практика" для тих ситуацій, коли бажана змінність. F #, здається, пропонує дві можливості для цього: let mutableприв'язка, яка, здається, працює як змінні в "більшості" мов, і посилальна комірка (створена за допомогою refфункції), для використання якої потрібно явне розмежування.

Є кілька випадків, коли один "примусово" переходить в той чи інший: .NET-взаємодія, як правило, використовує змінні з <-, а в обчисленнях робочого процесу потрібно використовувати refз :=. Отже, ці випадки досить чіткі, але мені цікаво, що робити при створенні власних змінних змінних поза цими сценаріями. Яку перевагу має один стиль перед іншим? (Можливо, подальше розуміння реалізації допоможе.)

Дякую!



4
Зверніть увагу, що у F # версії 4, мутабельний можна використовувати там, де раніше вам потрібен був посилання. blogs.msdn.com/b/fsharpteam/archive/2014/11/12/…
Джеймс Мур,

Відповіді:


134

Я можу підтримати лише те, що сказав gradbot - коли мені потрібна мутація, я віддаю перевагу let mutable.

Щодо реалізації та відмінностей між двома refклітинками, по суті, реалізується дуже простий запис, що містить змінне поле запису. Ви можете легко написати їх самі:

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 }

Помітна відмінність між двома підходами полягає в тому, що let mutableзберігається змінне значення в стеку (як змінна змінна в C #), тоді як refзберігається змінне значення в полі запису, виділеного купою. Це може мати певний вплив на продуктивність, але я не маю цифр ...

Завдяки цьому змінні значення, які використовуються, refможуть бути псевдонімами - це означає, що ви можете створити два значення, які посилаються на одне і те ж змінне значення:

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
Тільки нагадування: перебування в стеку або в купі - це деталь реалізації, яка не зовсім відповідає запитанню (але тим не менше чудова відповідь)
Бруно Брант,

6
Я міг би стверджувати, що знання, чи виникає щось накладне на розподіл та збір купи, є надзвичайно актуальним при прийнятті рішення про те, що є найкращою практикою.
jackmott

@jackmott ознайомтесь із цією статтею Еріка Ліпперта під назвою «Стек - це деталь реалізації»
jaromey

6
@jaromey так, я розумію, що я принципово повністю не згоден з Еріком у цьому питанні. Ви не можете залишати осторонь міркувань щодо продуктивності, розглядаючи, що є "найкращою практикою". Часто стверджують, що продуктивність поки не має значення, настільки програмне забезпечення повільне через смерть від тисячі деталей реалізації.
джекмотт

18

Пов’язане запитання: "Ви згадали, що локальні змінні значення не можуть бути зафіксовані закриттям, тому замість цього вам потрібно використовувати ref. Причиною цього є те, що змінні значення, захоплені у закритті, повинні бути розподілені в купі (оскільки закриття розподіляється на купи) ". від F # ref-mutable vars проти об'єктних полів

Я думаю, що let mutableце кращий варіант порівняно з клітинками. Я особисто використовую еталонні комірки лише тоді, коли вони потрібні.

Більшість коду, який я пишу, не використовує змінних змінних завдяки рекурсії та зворотним викликам. Якщо у мене є група змінних даних, я використовую запис. Для об'єктів, які я використовую let mutableдля створення приватних змінних змінних. Я дійсно використовую лише еталонні комірки для закриття, як правило, подій.


9

Як описано в цій статті блогу MSDN у розділі Спрощене використання змінних значень , вам більше не потрібні клітинки ref для лямбда. Тож загалом вони вам більше взагалі не потрібні.


4

Ця стаття Брайана може дати відповідь.

Змінні зручні у використанні та ефективні (без упаковки), але їх неможливо захопити лямбдами. Клітини реф. Можуть бути захоплені, але багатослівні та менш ефективні (? - не впевнений у цьому).


"(...) і менш ефективний" - можливо, тип обгортки займає більше пам'яті.
JMCF125,

2
Це змінилося, оскільки F # 4.0 мутабельний може бути захоплений у більшості випадків, і потреба в ref зараз значно менша.
Абель

3

Можливо, ви захочете поглянути на розділ "Змінні дані " у вікікнизі.

Для зручності, ось кілька відповідних цитат:

Змінюване ключове слово часто використовується з типами записів для створення змінних записів

Змінні змінні дещо обмежені: мутабелі недоступні поза сферою дії функції, де вони визначені. Зокрема, це означає, що неможливо посилатися на мутабель в підфункції іншої функції.

Реф-клітини обходять деякі обмеження змінних елементів. Насправді клітинки ref - це дуже простий тип даних, який обертає змінне поле у ​​тип запису.

Оскільки комірки ref розміщені в купі, вони можуть бути спільними для кількох функцій

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.