Понимание byref, ref и &

Ну, я понял, что F# может управлять ссылками (что-то вроде ссылок на С++). Это дает возможность изменять значение параметров, передаваемых в функции, а также позволяет программисту возвращать более одного значения. Однако вот что мне нужно знать:

  1. Ключевое слово Ref: ключевое слово ref используется для создания из значения ссылки на это значение предполагаемого типа. Так

    let myref = ref 10
    

    Это означает, что F# создаст объект типа Ref<int>, поместив туда (в изменяемое поле) мой int 10.

    В ПОРЯДКЕ. Поэтому я предполагаю, что ref используется для создания экземпляров типа Ref<'a>. Это правильно?

  2. Значение доступа: чтобы получить доступ к значению, хранящемуся в ссылке, я могу сделать это:

    let myref = ref 10
    let myval = myref.Value
    let myval2 = !myref
    

    В то время как оператор := просто позволяет мне редактировать значение следующим образом:

    let myref = ref 10
    myref.Value <- 30
    myref := 40
    

    Итак, ! (Bang) разыменовывает мою ссылку. И := отредактируйте его. Думаю, это тоже правильно.

  3. Оператор &: Что делает этот оператор? Следует ли применять его к ссылочному типу? Нет, я думаю, это должно быть применено к изменяемому значению, и что это возвращает? Ссылка? Адрес? При использовании интерактива:

    let mutable mutvar = 10;;
    &a;;
    

    Последняя строка выдает ошибку, поэтому я не понимаю, для чего нужен оператор &.

  4. ByRef: А как насчет byref? Это очень важно для меня, но я понимаю, что не понимаю этого. Я понимаю, что он используется в функции передачи параметров. Кто-то использует byref, когда хочет, чтобы переданное значение можно было отредактировать (это немного противоречит философии функциональных языков, но f# — это нечто большее). Рассмотрим следующее:

    let myfunc (x: int byref) =
        x <- x + 10
    

    Это странно. Я знаю, что если у вас есть ссылка let myref = ref 10, а затем сделать это, чтобы отредактировать значение: myref <- 10, то возникает ошибка, потому что должно быть так: myref := 10. Однако тот факт, что в этой функции я могу редактировать x с помощью оператора <-, означает, что x не является ссылкой, верно?

    Если я предполагаю, что x не является ссылкой, то я также предполагаю, что в функциях при использовании byref для параметра к этому параметру может применяться изменяемый синтаксис. Так что это просто вопрос синтаксиса, если я предполагаю, что у меня все в порядке, и на самом деле все работает (без ошибок компилятора). Однако что такое x?

  5. Вызов функций: как я могу использовать функцию, использующую параметры byref?

    Задействован оператор &, но не могли бы вы объяснить это лучше, пожалуйста? В этой статье: Параметры и аргументы MSDN приведен следующий пример:

    type Incrementor(z) =
        member this.Increment(i : int byref) =
           i <- i + z
    
    let incrementor = new Incrementor(1)
    let mutable x = 10
    // A: Not recommended: Does not actually increment the variable. (Me: why?)
    incrementor.Increment(ref x)
    // Prints 10.
    printfn "%d" x  
    
    let mutable y = 10
    incrementor.Increment(&y) (* Me: & what does it return? *)
    // Prints 11.
    printfn "%d" y 
    
    let refInt = ref 10
    incrementor.Increment(refInt) (* Why does it not work in A, but here it does? *)
    // Prints 11.
    printfn "%d" !refInt
    

person Andry    schedule 17.02.2011    source источник
comment
Советы по стилю: наличие нескольких вопросительных знаков не делает ваши вопросы более вопросительными и заставляет вас выглядеть немного глупо: одного достаточно. Текст, написанный заглавными буквами, плохо читается и обычно означает, что вы кричите: используйте форматирование (например, курсив и жирный шрифт) для акцента.   -  person R. Martinho Fernandes    schedule 17.02.2011


Ответы (1)


Ключевое слово Ref Да, когда вы пишете let a = ref 10, вы, по сути, пишете let a = new Ref<int>(10), где тип Ref<T> имеет изменяемое поле Value.

Значение доступа. Операторы := и ! — это просто сокращения для записи:

a.Value <- 10  // same as writing: a := 10
a.Value        // same as writing: !a

ByRef — это особый тип, который можно (разумно) использовать только в параметрах метода. Это означает, что аргумент должен быть по существу указателем на некоторую область памяти (выделенную в куче или стеке). Он соответствует модификаторам out и ref в C#. Обратите внимание, что вы не можете создать локальную переменную этого типа.

Операция & – это способ создать значение (указатель), которое можно передать в качестве аргумента функции/методу, ожидающему тип byref.

Вызов функций. Пример с byref работает, потому что вы передаете методу ссылку на локальную изменяемую переменную. Через ссылку метод может изменить значение, хранящееся в этой переменной.

Не работает следующее:

let a = 10            // Note: You don't even need 'mutable' here
bar.Increment(ref a)  

Причина в том, что вы создаете новый экземпляр Ref<int> и копируете значение a в этот экземпляр. Затем метод Increment изменяет значение, хранящееся в куче, в экземпляре Ref<int>, но у вас больше нет ссылки на этот объект.

let a = ref 10
bar.Increment(a)  

Это работает, потому что a является значением типа Ref<int>, и вы передаете указатель на экземпляр, выделенный в куче, в Increment, а затем получаете значение из эталонной ячейки, выделенной в куче, используя !a.

(Вы можете использовать значения, созданные с использованием ref, в качестве аргументов для byref, потому что компилятор специально обрабатывает этот случай — он автоматически использует ссылку на поле Value, потому что это полезный сценарий...).

person Tomas Petricek    schedule 17.02.2011
comment
Как бы то ни было, Томас, извините, но при передаче ссылки в функцию, принимающую байреф, интерпретатор сходит с ума... - person Andry; 17.02.2011
comment
@Andry: сумасшедший в каком смысле? Неожиданное поведение или сообщение об ошибке? (Я буду рад прояснить эту часть..) - person Tomas Petricek; 17.02.2011
comment
Извините за опоздание... Ну, он говорит мне, что ожидает ByRef‹'a›, но получает Ref‹'a›... вот что меня заводит... Интерпретатор просто не принимает этот аргумент :( - person Andry; 17.02.2011
comment
Только что увидел это, поскольку столкнулся с той же проблемой. Если у нас есть byref‹› в API-интерфейсе extern win32, тогда требуется оператор &, чтобы сообщить компилятору об byref, в противном случае, как сказал Томас, он должен работать с переменными ref, пусть изменяемый pid = 0u GetWindowThreadProcessId(новый IntPtr(phwnd),& pid) |> игнорировать - person Fahad; 03.04.2012