6

Maybeモナドは、(None を返すことによって) すべてが失敗する可能性がある一連の演算子をチェーンすることを可能にし、最後にSome result、すべてのサブオペレーションが成功した場合、またはNone何かが失敗した場合に戻ります。小さなダミーの例を次に示します。

type MaybeBuilder() =
    member this.Return(x) = 
        Some x
    member this.Bind(m, f) = 
        match m with
        | Some x -> f x
        | None -> None

let maybe = MaybeBuilder()

let list = [1;2;3;4]

// evaluates to Some 3
maybe {
    let! x1 = List.tryFind ((=) 1) list
    let! x2 = List.tryFind ((=) 2) list
    return x1 + x2
}

// evaluates to None 
maybe {
    let! x1 = List.tryFind ((=) 1) list
    let! x2 = List.tryFind ((=) 6) list
    return x1 + x2
}

これはこれとほぼ同等です:

// evaluates to Some 3
match List.tryFind ((=) 1) list with
| None -> None
| Some x1 ->
    match List.tryFind ((=) 2) list with
    | None -> None
    | Some x2 -> Some (x1 + x2)

// evaluates to None
match List.tryFind ((=) 1) list with
| None -> None
| Some x1 ->
    match List.tryFind ((=) 6) list with
    | None -> None
    | Some x2 -> Some (x1 + x2)

私が持っているコードでは、現在これの「反対」を行っており、最初の成功したヒットを返しています。

// evaluates to Some 1
match List.tryFind ((=) 1) list with
| Some x1 -> Some x1
| None ->
    match List.tryFind ((=) 2) list with
    | Some x2 -> Some x2
    | None -> None

// evaluates to Some 2
match List.tryFind ((=) 6) list with
| Some x1 -> Some x1
| None ->
    match List.tryFind ((=) 2) list with
    | Some x2 -> Some x2
    | None -> None

これは、素敵な計算式の構文を取得するためにモナドで行うことも可能ですか?

4

5 に答える 5

8

少し前に、命令型計算ビルダーを F# に実装するブログを書きました。たとえば、次の計算は 0 を返し、printfnステートメントを実行しません。

let test() = imperative {
  return 0
  printfn "after return!"
  return 1 }

あなたのコードサンプルは次のように書くことができると思います:

imperative { return! List.tryFind ((=) 1) list 
             return! List.tryFind ((=) 2) list }

それはどのように機能しますか?

あなたが示唆するように (そして Lee も言及したように)、型も型に基づいていoption<'T>ますが、遅延オプション値を使用しているという小さな違いがあります (評価せずに計算を構成できるようにするため)、モナドの型タイプは実際には次のとおりです。

type Imperative<'T> = unit -> option<'T>

計算ビルダーの重要なトリックは、最初の計算を実行し、その結果を返す ( だった場合) または残りを実行する ( だった場合)を追加することです (Combineとは実際にはここで関数です -そのため、それらを呼び出して結果を遅らせる必要があります):mplusSomeNoneab

member x.Combine(a, b) = (fun () ->
  match a() with 
  | Some(v) -> Some(v) // if it returned, we can return the result immediately
  | _ -> b() )         // otherwise, we need to run the second part

実際には非常にうまく機能します-ループと例外処理のサポートを追加できます。型をより複雑にする場合は、次のような他の機能を追加できますbreak

imperative { 
  for x in 1 .. 5 do 
    if (x % 2 = 0) then do! continue
    printfn "number = %d" x }
于 2013-10-15T15:05:43.970 に答える
6

とのトーマスの解

imperative { 
    return! List.tryFind ((=) 1) list
    return! List.tryFind ((=) 2) list }

やりたいことはできますが、これだけでもっと簡単に必要なことを達成できることに気づきました。

// evaluates to Some 1
[1;2] |> List.tryPick (fun x -> List.tryFind ((=) x) list)
// evaluates to Some 2
[6;2] |> List.tryPick (fun x -> List.tryFind ((=) x) list)
于 2013-10-15T15:37:21.783 に答える
5

Haskell は、次のようにMonadPlus定義された型クラスでこれを行います。

class Monad m => MonadPlus m where
  mzero :: m a
  mplus :: m a -> m a -> m a

Maybeこの型クラスを次のように実装します

instance MonadPlus Maybe where
  mzero                   = Nothing
  Nothing `mplus` Nothing = Nothing
  Just x  `mplus` Nothing = Just x
  Nothing `mplus` Just x  = Just x
  Just x  `mplus` Just y  = Just x

のとのメンバーは、F# の計算式で使用されるmzeroとのmplusメンバーにMonadPlus対応しているようです。ZeroCombine

于 2013-10-15T15:05:28.303 に答える
4

これを行う単純な関数を定義することもできます。

let orElse f = function
  | None -> f()
  | Some _ as x -> x

Some最初の結果が式全体の結果として返されるように、好きなだけ関数を連鎖させることができます。

List.tryFind ((=) 1) list
|> orElse (fun () -> List.tryFind ((=) 2) list)
|> orElse (fun () -> List.tryFind ((=) 3) list)
|> orElse (fun () -> List.tryFind ((=) 4) list)
于 2013-10-15T15:37:58.527 に答える
1

この特殊なケースは、シーケンスでもエミュレートできます。

let tests = seq {
  yield List.tryFind ((=) 5) list
  yield List.tryFind ((=) 3) list
  yield List.tryFind ((=) 6) list
}

tests |> Seq.tryFind Option.isSome |> Option.bind id
于 2013-10-17T18:40:32.153 に答える