Зависит от определения «функционал». Я бы сказал, что «функциональная» функция означает, что она всегда возвращает один и тот же результат для одних и тех же параметров и что она не изменяет какое-либо глобальное состояние (или значение параметров, если они изменчивы). Я думаю, что это разумное определение для F #, но это также означает, что нет ничего «не функционального» при использовании мутации локально.
На мой взгляд, следующая функция является "функциональной", потому что она создает и возвращает новую матрицу вместо изменения существующей, но, конечно, реализация функции использует мутацию.
let performStep m =
let res = Matrix.Generic.create 6 6 []
let pos = [(1.3,4.3); (5.6,5.4); (1.5,4.8)]
for pz, py in pos do
let z, y = int pz, int py
res.[z,y] <- (pz,py) :: m.[z,y]
res
Версия без мутаций:
Теперь, если вы хотите сделать реализацию полностью функциональной, я бы начал с создания матрицы, содержащей Some(pz, py)
в тех местах, где вы хотите добавить новый элемент списка к элементу матрицы, и None
во всех остальных. мест. Я думаю, это можно сделать, инициализировав разреженную матрицу. Примерно так:
let sp = pos |> List.map (fun (pz, py) -> int pz, int py, (pz, py))
let elementsToAdd = Matrix.Generic.initSparse 6 6 sp
Тогда вы сможете объединить исходную матрицу m
с вновь созданной elementsToAdd
. Это, безусловно, можно сделать с помощью init
(однако, что-то вроде map2
может быть лучше):
let res = Matrix.init 6 6 (fun i j ->
match elementsToAdd.[i, j], m.[i, j] with
| Some(n), res -> n::res
| _, res -> res )
Вполне вероятно, что некоторые мутации скрыты в функциях библиотеки F # (например, init
и initSparse
), но по крайней мере это показывает один из способов реализации операции с использованием более простых операций.
EDIT: Это будет работать, только если вам нужно добавить не более одного элемента в каждую ячейку матрицы. Если вы хотите добавить несколько элементов, вам сначала нужно сгруппировать их (например, используя Seq.groupBy
)