Я пытался написать общую функцию mapFoldWhile
, которая является только mapFold
, но требует, чтобы state
был option
и останавливается, как только он сталкивается с состоянием None
.Как написать эффективные функции list/seq в F #? (mapFoldWhile)
Я не хочу использовать mapFold
, потому что он преобразует весь список, но я хочу, чтобы он остановился, как только будет найдено недопустимое состояние (т. Е. None
).
Это была попытка myfirst:
let mapFoldWhile (f : 'State option -> 'T -> 'Result * 'State option) (state : 'State option) (list : 'T list) =
let rec mapRec f state list results =
match list with
| [] -> (List.rev results, state)
| item :: tail ->
let (result, newState) = f state item
match newState with
| Some x -> mapRec f newState tail (result :: results)
| None -> ([], None)
mapRec f state list []
List.rev
раздражало меня, так как точка упражнения должна была выйти в начале и строительство новый список должен быть еще ниже.
Так что я посмотрел, что F # 'ы очень собственные map
делает, что было:
let map f list = Microsoft.FSharp.Primitives.Basics.List.map f list
Зловещий Microsoft.FSharp.Primitives.Basics.List.map
можно найти here и выглядит следующим образом:
let map f x =
match x with
| [] -> []
| [h] -> [f h]
| (h::t) ->
let cons = freshConsNoTail (f h)
mapToFreshConsTail cons f t
cons
consNoTail
материал также в этот файл:
// optimized mutation-based implementation. This code is only valid in fslib, where mutation of private
// tail cons cells is permitted in carefully written library code.
let inline setFreshConsTail cons t = cons.(::).1 <- t
let inline freshConsNoTail h = h :: (# "ldnull" : 'T list #)
Итак, я полагаю, что неизменяемые списки F # на самом деле изменяемы, потому что производительность? Я немного обеспокоен этим, воспользовавшись подменю-потом-обратным списком, поскольку я думал, что это «путь» в F #.
Я не очень опытен с F # или функциональным программированием в целом, поэтому возможно (возможно) вся идея создания новой функции mapFoldWhile
- это не то, что нужно делать, но тогда что мне делать?
Я часто оказываюсь в ситуациях, когда мне нужно «выйти раньше», потому что элемент коллекции «недействителен», и я знаю, что мне не нужно смотреть на остальных. В некоторых случаях я использую List.pick
или Seq.takeWhile
, но в других случаях мне нужно сделать больше (mapFold
).
Есть ли эффективное решение этой проблемы (mapFoldWhile
в частности и «выход рано» в целом) с концепциями функционального программирования, или мне нужно переключиться на императивное решение/использовать Collections.Generics.List
?
Если вы не обязаны использовать 'List', вы можете использовать 'ResizeArray ' внутренне в вашей 'mapFoldWhile' и возвращать' T [] '. Таким образом, вы строите результат с помощью изменчивости, но API функции неизменен (это то, что делает F # внутри). Или вы используете эффективную поточную библиотеку, такую как Nessos 'Streams' –
FuleSnabel
Определите эффективную. Если функция вашей папки достаточно дорога, чтобы гарантировать ранний выход, у вас, вероятно, нет причин заботиться о «List.rev». Вы это оценили? – scrwtp
@scrwtp Да, это была большая проблема, чем вчера (длинный понедельник после продолжительных выходных ..). Я был отброшен, когда понял, что «List.rev» не используется другими функциями «map», опасаясь, что это может быть очень медленным, а затем не найти альтернативы. Я верю, что ранние выходы - это то, что должно быть легко достигнуто, для чего Томас указал на хорошее решение. – enzi