Почему это недопустимое использование byref?

let iter2D (map: 'T byref -> unit) (arr: 'T[][]) =
    for y = 0 to arr.Length - 1 do
        let row = arr.[y]
        for x = 0 to row.Length - 1 do
            let mutable elem = arr.[y].[x]
            map &elem

Последняя строка имеет: «Адрес переменной 'elem' не может быть использован в данный момент». Что случилось?


person Asik    schedule 11.04.2016    source источник


Ответы (1)


В F# 'T byref отображается как обычный тип, но на самом деле это не так — он соответствует параметрам ref и out в C#, а это специальные аннотации к аргументам метода. Вот почему 'T byref немного странный в F#.

Я думаю, вы не сможете использовать его через обычные функции F#, потому что функция T1 -> T2 компилируется как FSharpFunc<T1, T2> с методом T2 Invoke(T1 arg) - и вы не можете передать тип byref в дженерики (поскольку это не настоящий тип).

Обходной путь — определить собственный делегат с типом byref:

type FastAction<'T> = delegate of 'T byref -> unit

При этом вы можете написать iter2D, который выполняет итерацию непосредственно по массиву:

let iter2D (map:FastAction<'T>) (arr: 'T[][]) =
    for y = 0 to arr.Length - 1 do
        let row = arr.[y]
        for x = 0 to row.Length - 1 do
            map.Invoke(&arr.[y].[x])

Затем следующее изменит значение внутри массива:

let arr = [| [| 0 |] |]
iter2D (FastAction(fun a -> a <- 10)) arr
person Tomas Petricek    schedule 11.04.2016
comment
Что вы имеете в виду, говоря, что byref не является реальным типом? Что тогда возвращает MakeByRefType()? А для чего нужно свойство IsByRef? - person user4003407; 11.04.2016
comment
Yuk, FastAction(fun bla ...) вызывает 2 выделения... Похоже, что нет никакого способа обойти это, F # не имеет отдельного синтаксиса анонимного делегата, такого как C #. - person Asik; 11.04.2016