Когда мы должны использовать FSharpFunc.Adapt? - PullRequest
17 голосов
/ 17 марта 2011

Глядя на источник в FSharp.Core и PowerPack, я вижу, что многие функции высшего порядка, которые принимают функцию с двумя или более параметрами, используют FSharpFunc.Adapt .Например:

let mapi f (arr: ResizeArray<_>) =
   let f = FSharpFunc<_,_,_>.Adapt(f)
   let len = length arr
   let res = new ResizeArray<_>(len)
   for i = 0 to len - 1 do
       res.Add(f.Invoke(i, arr.[i]))
   res

Документация по FSharpFunc.Adapt довольно тонкая.Является ли это общей практикой, которую мы должны использовать всякий раз, когда у нас есть функция более высокого порядка с аналогичной сигнатурой?Только если переданная функция вызывается несколько раз?Насколько это оптимизация?Должны ли мы использовать Adapt везде, где сможем, или очень редко?

Спасибо за ваше время.

1 Ответ

13 голосов
/ 17 марта 2011

Это довольно интересно! У меня нет официальной информации (и я нигде этого не видел), но вот некоторые мысли о том, как может работать функция Adapt.

Функции наподобие mapi принимают каррированный вид функции, что означает, что тип аргумента компилируется во что-то вроде FSharpFunc<int, FSharpFunc<T, R>>. Однако многие функции фактически скомпилированы непосредственно как функции двух аргументов, поэтому фактическое значение обычно будет FSharpFunc<int, T, R>, которое наследуется от FSharpFunc<int, FSharpFunc<T, R>>.

Если вы вызываете эту функцию (например, f 1 "a"), компилятор F # генерирует что-то вроде этого:

FSharpFunc<int, string>.InvokeFast<a>(f, 1, "a");

Если вы посмотрите на функцию InvokeFast с помощью Reflector, вы увидите, что она проверяет, скомпилирована ли функция как оптимизированная версия (f :? FSharpFunc<int, T, R>). Если да, то он напрямую звонит Invoke(1, "a"), а если нет, то ему нужно сделать два звонка Invoke(1).Invoke("a").

Эта проверка выполняется каждый раз, когда вы вызываете функцию, переданную в качестве аргумента (вероятно, быстрее выполнить проверку, а затем использовать оптимизированный вызов, потому что это более распространено).

Функция Adapt выполняет преобразование любой функции в FSharpFunc<T1, T2, R> (если функция не оптимизирована, она создает оболочку для нее, но в большинстве случаев это не так). Вызовы адаптированной функции будут выполняться быстрее, поскольку им не нужно каждый раз выполнять динамическую проверку (проверка выполняется только один раз внутри Adapt).

Итак, в итоге, Adapt может улучшить производительность, если вы вызываете функцию, переданную в качестве аргумента, который принимает более 1 аргумента большое количество раз. Как и в случае любых оптимизаций, я бы не стал использовать это вслепую, но это интересно знать при настройке производительности!

(Кстати: спасибо за очень интересный вопрос, я не знал, что компилятор делает это: -))

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...