fold or choose till None?

隐身守侯 提交于 2019-12-10 17:17:34

问题


Is there already a way to do something like a chooseTill or a foldTill, where it will process until a None option is received? Really, any of the higher order functions with a "till" option. Granted, it makes no sense for stuff like map, but I find I need this kind of thing pretty often and I wanted to make sure I wasn't reinventing the wheel.

In general, it'd be pretty easy to write something like this, but I'm curious if there is already a way to do this, or if this exists in some known library?

let chooseTill predicate (sequence:seq<'a>) = 
    seq {
            let finished = ref false                            
            for elem in sequence do
                if not !finished then
                    match predicate elem with
                        | Some(x) -> yield x 
                        | None -> finished := true
    }

let foldTill predicate seed list = 
    let rec foldTill' acc = function
        | [] -> acc
        | (h::t) -> match predicate acc h with 
                        | Some(x) -> foldTill' x t
                        | None -> acc
    foldTill' seed list

let (++) a b = a.ToString() + b.ToString()

let abcdef =  foldTill (fun acc v -> 
                        if Char.IsWhiteSpace v then None 
                        else Some(acc ++ v)) "" ("abcdef ghi"  |> Seq.toList)

// result is "abcdef"

回答1:


I think you can get that easily by combining Seq.scan and Seq.takeWhile:

open System

"abcdef ghi"
|> Seq.scan (fun (_, state) c -> c, (string c) + state) ('x', "")
|> Seq.takeWhile (fst >> Char.IsWhiteSpace >> not)
|> Seq.last |> snd

The idea is that Seq.scan is doing something like Seq.fold, but instead of waiting for the final result, it yields the intermediate states as it goes. You can then keep taking the intermediate states until you reach the end. In the above example, the state is the current character and the concatenated string (so that we can check if the character was whitespace).

A more general version based on a function that returns option could look like this:

let foldWhile f initial input =
  // Generate sequence of all intermediate states
  input |> Seq.scan (fun stateOpt inp -> 
       // If the current state is not 'None', then calculate a new one
       // if 'f' returns 'None' then the overall result will be 'None'
       stateOpt |> Option.bind (fun state -> f state inp)) (Some initial)
  // Take only 'Some' states and get the last one
  |> Seq.takeWhile Option.isSome
  |> Seq.last |> Option.get


来源:https://stackoverflow.com/questions/18903519/fold-or-choose-till-none

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!