F #: let mutable vs. ref
Во-первых, я признаю, что этот вопрос может быть дублирующим; просто дайте мне знать.
Мне любопытно, какая общая "лучшая практика" для тех ситуаций, когда желательна изменчивость. Кажется, что F # предлагает две возможности для этого: привязка let mutable
, которая, похоже, работает как переменные на большинстве языков, и эталонная ячейка (созданная с помощью функции ref
), которая требует явного разыменования.
Есть несколько случаев, когда один из них "принудительно" в один или другой:.NET interop имеет тенденцию использовать mutable с <-
, а в вычислениях рабочего процесса следует использовать ref
с :=
. Таким образом, эти случаи довольно ясны, но мне любопытно, что делать, создавая свои собственные изменяемые переменные за пределами этих сценариев. Какое преимущество имеет один стиль над другим? (Возможно, помощь в этом поможет.)
Спасибо! (И извините за "чрезмерное использование" кавычек.)
Ответы
Ответ 1
Я могу только поддерживать то, что сказал 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
хранит изменяемое значение в стеке (как изменчивую переменную в С#), в то время как 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
Связанный с этим вопрос: "Вы упомянули, что локальные измененные значения не могут быть захвачены закрытием, поэтому вам нужно использовать ref вместо этого. Причина этого в том, что изменяемые значения, зафиксированные в закрытии, должны быть выделенных в куче (поскольку закрытие выделено в куче)". from F # ref-mutable vars vs object fields
Я думаю, что let mutable
предпочтительнее контрольных ячеек. Я лично использую только контрольные ячейки, когда они необходимы.
Большинство написанных мной кодов не используют изменяемые переменные благодаря рекурсивным и хвостовым вызовам. Если у меня есть группа изменяемых данных, я использую запись. Для объектов я использую let mutable
для создания частных переменных. Я действительно использую только ссылочные ячейки для замыканий, обычно событий.
Ответ 3
Эта статья Брайана может дать ответ.
Mutables просты в использовании и эффективны (без обертывания), но не могут быть захвачены в лямбдах. Реф-ячейки могут быть захвачены, но являются подробными и менее эффективными (? - не уверены в этом).
Ответ 4
Как описано в этой статье блога MSDN в разделе Упрощенное использование изменяемых значений, вам больше не нужны ref-ячейки для лямбда.
Таким образом, вы вообще не нуждаетесь в них.
Ответ 5
Вы можете взглянуть на раздел Mutable Data в wikibook.
Для удобства здесь приведены некоторые релевантные кавычки:
Часто используется изменяемое ключевое слово с типами записей для создания изменчивых записи
Переменные переменные несколько ограничено: mutables недоступны вне сферы действия функции где они определены. В частности, это означает, что ссылка изменена в подфункции другой функции.
Реф-клетки обходят некоторые из ограничения переменных. Фактически, ref ячейки - очень простой тип данных, который завернуть изменчивое поле в записи тип.
Поскольку ячейки ref выделяются на кучи, они могут несколько функций