5

是否已经有一种方法可以执行 achooseTill或 a之类的操作foldTill,在收到 None 选项之前它会一直处理?实际上,任何带有“直到”选项的高阶函数。诚然,像地图这样的东西没有意义,但我发现我经常需要这种东西,我想确保我没有重新发明轮子。

一般来说,写这样的东西很容易,但我很好奇是否已经有办法做到这一点,或者这是否存在于某个已知的库中?

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"
4

1 回答 1

14

我认为您可以通过组合Seq.scanand轻松获得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

这个想法是Seq.scan在做类似的事情Seq.fold,但不是等待最终结果,而是在进行时产生中间状态。然后,您可以继续采用中间状态,直到到达终点。在上面的示例中,状态是当前字符和连接的字符串(以便我们可以检查字符是否为空格)。

基于返回函数的更通用版本option可能如下所示:

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
于 2013-09-19T19:45:06.347 回答