1

私はこれを行うための簡単な(方法と比較して、try catchかなり簡単な)方法を見つけることができません:

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

悪い(Parse2回呼び出す)アプローチは次のようなものです。

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

そのようなタスクを実行する標準的な方法はありますか?これを処理するために再帰関数を作成する必要がありますか?

入力が配列ではなくストリーム/シーケンスである場合はどうなりますか?

アップデート:

@ダニエルの方法は素晴らしいです。

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 optionNoneNone

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

val it:int [] option = None


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

val it:int [] option = Some [| 10; 12 |]

于 2013-01-22T21:58:22.543 に答える
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 に答える
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 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シーケンスを2回トラバースしますが、別の方法として、1回トラバースして、 (単一の値が見つかった場合は役に立たなくなる可能性がある)アンラップを解除します。これは、よりコストがかかるように見えます。OptionNone

// 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 に答える