13

これは C++ で簡単に実行できます (注: これが正しいかどうかはテストしていません。これは、私がやろうとしていることを説明するためだけです)。

   const int BadParam = -1;
   const int Success = 0;

   int MyFunc(int param)
   {
      if(param < 0)
      {
         return BadParam;
      }

      //normal processing

      return Success;
   }

しかし、F# の早い段階でルーチンを終了する方法がわかりません。私がやりたいことは、入力が正しくない場合は関数を終了し、入力が問題ない場合は続行することです。F# のいくつかの基本的な特性が欠けているのでしょうか、それとも FP を学習しているだけなので、間違った方法で問題に取り組んでいますか? ここで私の唯一のオプションは失敗ですか?

これは私がこれまでに得たものであり、正常にコンパイルされます:

   #light

   module test1

       (* Define how many arguments we're expecting *)
       let maxArgs = 2;;
       (* The indices of the various arguments on the command line *)
       type ProgArguments =
           | SearchString = 0
           | FileSpec = 1;;

       (* Various errorlevels which the app can return and what they indicate *)
       type ProgReturn =
           | Success = 0
           | WrongNumberOfArgumentsPassed = 1;;

       [<EntryPoint>]
       let main (args:string[]) =

           printfn "args.Length is %d" args.Length

           let ProgExitCode = if args.Length <> maxArgs then
                                   printfn "Two arguments must be passed"
                                   int ProgReturn.WrongNumberOfArgumentsPassed
                                   (* Want to exit "main" here but how? *)
                               else
                                   int ProgReturn.Success

           let searchstring, filespec  = args.[int ProgArguments.SearchString],args.[int ProgArguments.FileSpec];

           printfn "searchstring is %s" searchstring
           printfn "filespec is %s" filespec

           ProgExitCode;;

この種のことを扱うFPの方法はありますか?

4

7 に答える 7

11

F# では、すべてが式で構成されています (他の多くの言語では、重要な構成要素はステートメントです)。関数を早期に終了する方法はありませんが、多くの場合、これは必要ありません。C ではif/else、分岐がステートメントで構成されるブロックがあります。F# にはif/else、各分岐が何らかの型の値に評価される式があり、if/else式全体の値はいずれかの分岐の値です。

したがって、このC++:

int func(int param) {
  if (param<0)
    return BadParam;
  return Success;
}

F# では次のようになります。

let func param =
  if (param<0) then
    BadParam
  else
    Success

あなたのコードは正しい軌道に乗っていますが、それをリファクタリングして、ほとんどのロジックをelseブランチに配置し、ブランチに「アーリー リターン」ロジックを配置できifます。

于 2009-10-22T20:58:05.687 に答える
6

私の意見では、一致式は、誤った条件を呼び出して個別に処理するための早期終了の F# 類似物です。あなたの例では、次のように書きます。

 [<EntryPoint>]
 let main (args:string[]) =
     printfn "args.Length is %d" args.Length
     match args with
     | [| searchstring; filespace |] -> 
       // much code here ...
       int Success
     | _ -> printfn "Two arguments must be passed"
       int WrongNumberOfArgumentsPassed

これにより、エラーのケースがうまく分離されます。一般に、何かの途中で終了する必要がある場合は、関数を分割してから、エラー ケースをmatch. 関数型言語の小さな関数に制限はありません。

余談ですが、判別共用体を整数定数のセットとして使用するのは少し奇妙です。そのイディオムが気に入った場合は、それらを参照するときに型名を含める必要がないことに注意してください。

于 2009-10-22T21:22:32.807 に答える
3

Pavel のオプションに似ていますが、独自のワークフロー ビルダーを必要としないオプションは、コード ブロックをseq式内に配置し、yieldエラー メッセージを表示することです。次に、式の直後に呼び出しFirstOrDefaultて、最初のエラー メッセージ (または null) を取得します。

シーケンス式は遅延評価されるため、最初のエラーのポイントまでしか進まないことを意味します (シーケンス以外では何も呼び出さないと仮定しますFirstOrDefault)。エラーがなければ、そのまま最後まで実行されます。なので、こうすれば早帰りとyield同じように考えることができます。

let x = 3.
let y = 0.

let errs = seq {
  if x = 0. then yield "X is Zero"
  printfn "inv x=%f" (1./x)
  if y = 0. then yield "Y is Zero"
  printfn "inv y=%f" (1./y)
  let diff = x - y
  if diff = 0. then yield "Y equals X"
  printfn "inv diff=%f" (1./diff)
}

let firstErr = System.Linq.Enumerable.FirstOrDefault errs

if firstErr = null then
  printfn "All Checks Passed"
else
  printfn "Error %s" firstErr
于 2014-01-31T01:37:51.677 に答える
1

この再帰的なフィボナッチ関数には、次の 2 つの終了ポイントがあります。

let rec fib n =
  if n < 2 then 1 else fib (n-2) + fib(n-1);;
                ^      ^
于 2009-10-22T21:01:02.527 に答える