簡體   English   中英

F#Seq.tryPick並將函數應用於未被挑選的成員

[英]F# Seq.tryPick and apply function to members that were not picked

這有可能嗎? 我想使用tryPick,然后繼續使用該集合的“休息”。

我只能想到丑陋的方法來做到這一點:

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返回一個帶有單獨選項的元組和其余的fold

只要孤獨是None你檢查你的謂詞(這里與2相等)
如果它是真的(如果有的話),你將單獨的一個更新為該項並從序列中跳過該項
否則你只需該項添加到生成的“休息序列”中

我使用謂詞參數使其成為更通用的函數:

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

根據您的評論,您說:

我想從一個集合中選擇一個成員,但是然后還將一個函數應用於未被挑選的每個成員

我會通過創建一個新的函數tryPickOrApplyFunc來解決這個問題,該函數折疊集合,返回適當的值或應用提供的函數。

/// 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 

如果您想要返回一組未挑選的值,我會將其構建為一個列表,並建議切換到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返回None時,您的示例中不清楚您想要做什么。 我假設整個表達式將返回'None'並且函數簽名chooser:('a -> 'b option) -> source:seq<'a> -> ('b * 'a list) option是您的用例可以接受。

你將獲得一個元組的選項,包含選中的值,其余的作為列表。 它不需要是一個序列,因為您需要急切地使用整個輸入序列來返回結果。 懶惰消失了,我們也可以使用一個列表。

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])

編輯

現在你已經澄清了你想要在任何情況下評估選擇選項和過濾列表的元組,我們只是借用了TheInnerLight的概念並將其縮短。 當然,所以最后將是第一個。

 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.partitionArray.partition做你想要的嗎?

> [1..5] |> List.partition ((=) 2);;
val it : int list * int list = ([2], [1; 3; 4; 5])

您可以使用Seq.toListSet.toArray將所有有限序列轉換為列表或數組,然后使用適當的partition函數。 但是, 沒有Seq.partition功能

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM