F# Seq.tryPick 并将函数应用于未选取的成员
F# Seq.tryPick and apply function to members that were not picked
这有可能吗?我想使用 tryPick,然后继续使用集合的 "rest"。
我只能想到丑陋的方法来做到这一点:
let sequence = Seq.init 5 (fun (i) -> i)
let pickedMember =
sequence
|> Seq.tryPick (fun (i) ->
if i = 2 then
Some i
else
None
);;
let sequenceWithoutPickedMember = sequence |> Seq.filter (fun (i) -> not(i=pickedMember.Value));;
您可以使用 fold
返回一个包含唯一选项和其余选项的元组来同时执行这两项操作。
只要唯一的是 None
你检查你的谓词(这里等于 2)
当它为真时(如果有的话),您将单独的更新为该项目并从序列中跳过该项目
否则你只需 添加 该项目到结果 "rest sequence"
我把它变成了一个带有谓词参数的更通用的函数:
let firstAndRest predicate source =
source
|> Seq.fold (fun (lone, rest) item -> if Option.isNone lone && predicate item
then Some item, rest
else lone, seq { yield! rest; yield item })
(None, Seq.empty)
// usage with example given
let sequence = Seq.init 5 id
let pickedMember, sequenceWithoutPickedMember = firstAndRest ((=) 2) sequence
// pickedMember : Some 2
// sequenceWithoutPickedMember : seq [0; 1; 3; 4]
编辑
由于嵌套序列表达式与我有关(它可以使长序列的大量枚举器保持活动状态),这里有一个使用索引的替代方法。
我们的想法是找到该项目(如果有的话)的索引而不是项目本身。
完成后,我们得到了该索引处的项目,将序列分成两部分。
after
部分将包含找到的项目,因此我们需要丢弃它(通过再跳过一项)
如果找不到该项目,我们只会返回原始来源。
let firstAndRest predicate source =
let source = Seq.cache source // to prevent yielding different values for side-effect sequences
match Seq.tryFindIndex predicate source with
Some index -> let picked = source |> Seq.item index |> Some
let before, after = Seq.take index source, Seq.skip (index + 1) source
picked, Seq.append before after
| None -> None, source
根据您的评论,您说:
I want to pick a member from a collection but then also apply a
function to each member that were not picked
我会通过创建一个折叠集合的新函数 tryPickOrApplyFunc
来解决这个问题,returning 适当的值或应用提供的函数。
/// Applies the supplied choosing function to successive elements, returning the first result where the function returns `Some (x)` and applies the supplied function `f` to all other elements.
let tryPickOrApplyFunc chooser f sequ =
let folder acc v =
match acc, chooser v with
|Some x, _ -> // We have already picked an element - apply the function to v and return the already picked element
f v
Some x
|None, Some y -> // We haven't picked a value and the chooser function returns `Some (x)` - Pick the value
Some v
|None, None -> // We haven't picked a value and the chooser function returns `None` - apply the function and return `None`
f v
None
Seq.fold (folder) None sequ
示例用法 - 选择一个值或打印不匹配的结果:
tryPickOrApplyFunc (fun x -> if x = 3 then Some x else None) (printfn "%d") [1; 2; 3; 4; 5; 6;];;
1
2
4
5
6
val it : int option = Some 3
或者,如果我们超出值的范围:
tryPickOrApplyFunc (fun x -> if x = 7 then Some x else None) (printfn "%d") [1; 2; 3; 4; 5; 6;];;
1
2
3
4
5
6
val it : int option = None
如果您想 return 一组未选取的值,我会将其构建为列表,并建议切换到 foldBack
以便您获得原始值顺序:
/// Applies the supplied choosing function to successive elements, returning the first result where the function returns `Some (x)` and a sequence of all the other elements.
let tryPickWithRemaining chooser sequ =
let folder v acc =
match acc, chooser v with
|(Some x, lst), _ -> // We have already picked an element - return the already picked element and append v to the list of remaining values
Some x, v::lst
|(None, lst), Some y -> // We haven't picked a value and the chooser function returns `Some (x)` - Pick the value and keep the list of remaining values as it is
Some v, lst
|(None, lst), None -> // We haven't picked a value and the chooser function returns `None` - return `None` append v to the list of remaining values
None, v::lst
let pick, lst = Seq.foldBack (folder) sequ (None, [])
pick, Seq.ofList lst
从您的示例中不清楚您在 Seq.tryPick
returns None
时想要做什么。我假设整个表达式应 return 'None' 并且函数签名 chooser:('a -> 'b option) -> source:seq<'a> -> ('b * 'a list) option
对于您的用例是可以接受的。
您将获得返回元组的选项,其中包含选取的值和其余作为列表的值。它不需要是一个序列,因为您需要急切地使用整个输入序列来 return 结果。懒惰消失了,我们也可以使用列表。
let tryPickWithRest chooser source =
source
|> Seq.fold (fun (picked, rest) x ->
match picked, chooser x with
| None, Some newlyPicked -> Some newlyPicked, rest
| _ -> picked, x::rest ) (None, [])
|> function
| Some picked, rest -> Some(picked, List.rev rest)
| _ -> None
[0..4]
|> tryPickWithRest (fun i -> if i = 2 then Some "found 2" else None)
// val it : (string * int list) option = Some ("found 2", [0; 1; 3; 4])
编辑
既然您已经明确了在任何情况下都想对选择选项和筛选列表的元组求值,我们只是借用 的想法并使其更短。当然,所以最后一个是第一个。
let tryPickWithRest' chooser source =
Seq.foldBack (fun x (picked, rest) ->
match picked, chooser x with
| None, Some newlyPicked -> Some newlyPicked, rest
| _ -> picked, x::rest ) source (None, [])
[-4..0]
|> tryPickWithRest' (fun i -> if i >= 2 then Some "found 2" else None)
// val it : string option * int list = (null, [-4; -3; -2; -1; 0])
List.partition
或 Array.partition
是否符合您的要求?
> [1..5] |> List.partition ((=) 2);;
val it : int list * int list = ([2], [1; 3; 4; 5])
您可以使用 Seq.toList
或 Set.toArray
将所有有限序列转换为列表或数组,然后使用适当的 partition
函数。但是,有 .
这有可能吗?我想使用 tryPick,然后继续使用集合的 "rest"。
我只能想到丑陋的方法来做到这一点:
let sequence = Seq.init 5 (fun (i) -> i)
let pickedMember =
sequence
|> Seq.tryPick (fun (i) ->
if i = 2 then
Some i
else
None
);;
let sequenceWithoutPickedMember = sequence |> Seq.filter (fun (i) -> not(i=pickedMember.Value));;
您可以使用 fold
返回一个包含唯一选项和其余选项的元组来同时执行这两项操作。
只要唯一的是 None
你检查你的谓词(这里等于 2)
当它为真时(如果有的话),您将单独的更新为该项目并从序列中跳过该项目
否则你只需 添加 该项目到结果 "rest sequence"
我把它变成了一个带有谓词参数的更通用的函数:
let firstAndRest predicate source =
source
|> Seq.fold (fun (lone, rest) item -> if Option.isNone lone && predicate item
then Some item, rest
else lone, seq { yield! rest; yield item })
(None, Seq.empty)
// usage with example given
let sequence = Seq.init 5 id
let pickedMember, sequenceWithoutPickedMember = firstAndRest ((=) 2) sequence
// pickedMember : Some 2
// sequenceWithoutPickedMember : seq [0; 1; 3; 4]
编辑
由于嵌套序列表达式与我有关(它可以使长序列的大量枚举器保持活动状态),这里有一个使用索引的替代方法。
我们的想法是找到该项目(如果有的话)的索引而不是项目本身。
完成后,我们得到了该索引处的项目,将序列分成两部分。
after
部分将包含找到的项目,因此我们需要丢弃它(通过再跳过一项)
如果找不到该项目,我们只会返回原始来源。
let firstAndRest predicate source =
let source = Seq.cache source // to prevent yielding different values for side-effect sequences
match Seq.tryFindIndex predicate source with
Some index -> let picked = source |> Seq.item index |> Some
let before, after = Seq.take index source, Seq.skip (index + 1) source
picked, Seq.append before after
| None -> None, source
根据您的评论,您说:
I want to pick a member from a collection but then also apply a function to each member that were not picked
我会通过创建一个折叠集合的新函数 tryPickOrApplyFunc
来解决这个问题,returning 适当的值或应用提供的函数。
/// Applies the supplied choosing function to successive elements, returning the first result where the function returns `Some (x)` and applies the supplied function `f` to all other elements.
let tryPickOrApplyFunc chooser f sequ =
let folder acc v =
match acc, chooser v with
|Some x, _ -> // We have already picked an element - apply the function to v and return the already picked element
f v
Some x
|None, Some y -> // We haven't picked a value and the chooser function returns `Some (x)` - Pick the value
Some v
|None, None -> // We haven't picked a value and the chooser function returns `None` - apply the function and return `None`
f v
None
Seq.fold (folder) None sequ
示例用法 - 选择一个值或打印不匹配的结果:
tryPickOrApplyFunc (fun x -> if x = 3 then Some x else None) (printfn "%d") [1; 2; 3; 4; 5; 6;];; 1 2 4 5 6 val it : int option = Some 3
或者,如果我们超出值的范围:
tryPickOrApplyFunc (fun x -> if x = 7 then Some x else None) (printfn "%d") [1; 2; 3; 4; 5; 6;];; 1 2 3 4 5 6 val it : int option = None
如果您想 return 一组未选取的值,我会将其构建为列表,并建议切换到 foldBack
以便您获得原始值顺序:
/// Applies the supplied choosing function to successive elements, returning the first result where the function returns `Some (x)` and a sequence of all the other elements.
let tryPickWithRemaining chooser sequ =
let folder v acc =
match acc, chooser v with
|(Some x, lst), _ -> // We have already picked an element - return the already picked element and append v to the list of remaining values
Some x, v::lst
|(None, lst), Some y -> // We haven't picked a value and the chooser function returns `Some (x)` - Pick the value and keep the list of remaining values as it is
Some v, lst
|(None, lst), None -> // We haven't picked a value and the chooser function returns `None` - return `None` append v to the list of remaining values
None, v::lst
let pick, lst = Seq.foldBack (folder) sequ (None, [])
pick, Seq.ofList lst
从您的示例中不清楚您在 Seq.tryPick
returns None
时想要做什么。我假设整个表达式应 return 'None' 并且函数签名 chooser:('a -> 'b option) -> source:seq<'a> -> ('b * 'a list) option
对于您的用例是可以接受的。
您将获得返回元组的选项,其中包含选取的值和其余作为列表的值。它不需要是一个序列,因为您需要急切地使用整个输入序列来 return 结果。懒惰消失了,我们也可以使用列表。
let tryPickWithRest chooser source =
source
|> Seq.fold (fun (picked, rest) x ->
match picked, chooser x with
| None, Some newlyPicked -> Some newlyPicked, rest
| _ -> picked, x::rest ) (None, [])
|> function
| Some picked, rest -> Some(picked, List.rev rest)
| _ -> None
[0..4]
|> tryPickWithRest (fun i -> if i = 2 then Some "found 2" else None)
// val it : (string * int list) option = Some ("found 2", [0; 1; 3; 4])
编辑
既然您已经明确了在任何情况下都想对选择选项和筛选列表的元组求值,我们只是借用let tryPickWithRest' chooser source =
Seq.foldBack (fun x (picked, rest) ->
match picked, chooser x with
| None, Some newlyPicked -> Some newlyPicked, rest
| _ -> picked, x::rest ) source (None, [])
[-4..0]
|> tryPickWithRest' (fun i -> if i >= 2 then Some "found 2" else None)
// val it : string option * int list = (null, [-4; -3; -2; -1; 0])
List.partition
或 Array.partition
是否符合您的要求?
> [1..5] |> List.partition ((=) 2);;
val it : int list * int list = ([2], [1; 3; 4; 5])
您可以使用 Seq.toList
或 Set.toArray
将所有有限序列转换为列表或数组,然后使用适当的 partition
函数。但是,有