1

我找不到任何简单的(与try catch相当简单的方法相比)方法来做到这一点:

try
    Some (line.Split delimiter |> Array.map Int32.Parse)
with
    |_ -> None

一个不好的(调用Parse两次)方法是这样的:

let array = line.Split delimiter
let parseSucceed =
    array |> Array.exist (Int32.TryParse >> fst >> not)
          |> not
if parseSucceed then Some (array |> Array.map Int32.Parse) else None

有没有做这些任务的标准方法?我是否必须编写一个递归函数来处理这个问题?

如果输入不是数组而是流/序列怎么办?

更新:

@Daniel 的方法很棒。

module Seq =
    let tryCatch myFun (x: seq<_>) =
        let rec loop acc (e: IEnumerator<_>) =
            if e.MoveNext() then
                match myFun e.Current with
                | Some v -> loop (v::acc) e
                | None   -> (false, acc |> List.rev |> List.toSeq)
            else
                (true, acc |> List.rev |> List.toSeq)
        use e = x.GetEnumerator()
        loop [] e

let parse x =
    printf "%d " x // log when the function is called.
    if x > 3 then None else Some x

let tt myFun x =
    let y = Seq.tryCatch myFun x
    if fst y then
        printfn "yes %A" (y |> snd |> Seq.toArray)
    else
        printfn "no %A" (y |> snd |> Seq.toArray)

tt parse [| 0;2;1;2;4;1;2 |]
tt parse [| 0;2;1;2 |]

> 
0 2 1 2 4 no [|0; 2; 1; 2|]  // parse is called minimum number of times
0 2 1 2 yes [|0; 2; 1; 2|]
4

4 回答 4

2

你所拥有的是'a option array并且你想要一个'a array optionwhere anyNone会导致整个值成为None。你可以定义一个这样的函数:

let lift a = 
         Array.foldBack 
                    (fun curr state -> Option.bind 
                                              (fun l -> Option.map (fun c -> c :: l) curr) state) a (Some []) 
         |> Option.map List.toArray;;

用法:

lift [| (Some 10); (Some 12); (None); |]

验证它:int [] 选项 = 无


lift [| (Some 10); (Some 12); |]

验证它:int [] 选项 = Some [|10; 12|]

于 2013-01-22T21:58:22.543 回答
1

如果您需要使用无限序列或流,最好将大函数拆分为几个较小的函数,您可以在需要时将它们组合起来:

let split delimiters (line: String) =
    line.Split delimiters
    |> Array.toSeq
let parse =
    Seq.map
        (fun x ->
            let (success, value) = Int32.TryParse x
            if success then Some value else None
        )
let isParseSucceed vals =
    vals
    |> Seq.forall Option.isSome
    |> fun b -> if b then Some (vals |> Seq.choose id) else None

注意我故意让一切都Seq支持无限序列。
此外,isParseSucceed遍历序列两次,但另一种方法是遍历一次解包Option(如果找到单个None值可能变得无用),这看起来更昂贵。

// Usage
let x1 = 
    "5,20,42,10"
    |> split [| ',' |]
    |> parse
    |> isParseSucceed
x1 |> printf "%A\n"
// output: Some (seq [5; 20; 42; 10])

let x2 = 
    "5,20,42,foobar,10"
    |> split [| ',' |]
    |> parse
    |> isParseSucceed
x2 |> printf "%A\n"
// output: null

// making an infinite sequence
// with all values are proper Int32's in a string form
// but the 5th value is "FOOBAR"
// We are not using `split` as it is not needed here
let x3 = 
    Seq.initInfinite (fun i -> if(i=5) then "FOOBAR" else i.ToString())
    |> parse
    |> isParseSucceed
x3 |> printf "%A\n"
// output: null

// the following will obviously hang
let x4 = 
    Seq.initInfinite (fun i -> i.ToString())
    |> parse
    |> isParseSucceed
x4 |> printf "%A\n"
于 2013-01-23T15:28:07.677 回答
1

你可以这样做:

let fields = line.Split(delimiter) 
let parsedFields = 
  fields
    |> Seq.map Int32.TryParse
    |> Seq.takeWhile fst
    |> Seq.map snd
    |> Seq.toArray
if parsedFields.Length = fields.Length 
then Some parsedFields
else None

或者,如果你想要一些更可重用的东西:

module Seq =
  let tryMap f (s: seq<_>) =
    use e = s.GetEnumerator()
    let res = ResizeArray()
    let rec loop() =
      if e.MoveNext() then
        match f e.Current with
        | Some x -> res.Add(x); loop()
        | None -> None
      else Some(seq res)
    loop()

type System.Int32 with
  static member tryParse s =
    match Int32.TryParse(s) with
    | true, i -> Some i
    | _ -> None

//Usage
line.Split(delimiter) |> Seq.tryMap Int32.tryParse
于 2013-01-22T21:59:32.313 回答
1

这应该有效:

let optInt32 str =
    match Int32.TryParse str with
    | false, _ ->
        None
    | true, value ->
        Some value


let tryParseDelimited (line : string) (delimiter : char) =
    let pieces = line.Split delimiter
    let parsedPieces = Array.choose optInt32 pieces
    if pieces.Length = parsedPieces.Length then
        Some parsedPieces
    else
        None
于 2013-01-22T21:50:54.327 回答