8

前の状態をチェックしてif condition、次if conditionを実行するかどうかを判断したい。それぞれif conditionが値を返す場合があります。

編集:前に提供した例が少し奇妙に見えて申し訳ありません...:(これは私の実際の例であり、goingToMoveのを単純化if-then-elseたい

goingToMove p routes points w h = 
                        if canMove p points
                            -- the point can be moved in the map 
                            then let r = routes ++ [p]
                                     l = remainList p points
                                in move p r l w h
                            -- the point cannot be moved in the maps
                            else []

move p routes points w h = 
            if (length routes) == 2 
                then routes
                else let one = goingToMove (tallRightCorner p) routes points w h in
                    if (null one)
                        then let two = goingToMove(tallRightBCorner p) routes points w h in
                            if (null two)
                                then let three = goingToMove (tallLeftBCorner p ) routes points w h in
                                    if (null three)
                                        then ....
                                        ...... -- until, let eight = ..
                                        else three
                                else two
                        else one 

編集: 悪い例 これが Java で書かれている場合、変更可能なブール値フラグを使用して、変更可能なデータを返すことがあります。

public String move (int number){
        // base case
        if (number == 0){
            return "Finished the recursion";
        }
        // general case
        else {
            String result;
            boolean isNull = false;

            if ((result = move(3)) == null){
                isNull = true;
            }
            else {
                return result;
            }

            // continue to execute the if-conditions if the previous condition failed
            if (isNull){
                if((result = move(2)) == null){
                    isNull = true;
                }
                else {
                    return result;
                }
            }

            if (isNull){
                if((result = move(1)) == null){
                    isNull = true;
                }
                else {
                    return result;
                }
            }

            return null;
        }
    }

しかし、Haskell では変更可能なデータはなく、if-then-else条件のみです。すると、コードは次のようになります。これを単純化したいのですが、実際の作業では、8 つのレベルがif-then-elseあり、ひどく乱雑に見えます....

move 0 = "Finished the recursion"
move n = 
    let one = move 3 in
    if null one
        then let two = move 2 in
            if null two
                then let three = move 1 in
                        then null
                        else three
                else two
        else one
4

5 に答える 5

16

Javaで次のことをしたい場合:

result = func1(arg);
if (result == null){
  result = func2(arg);
  if (result == null){
    result = func3(arg);
    if (result == null){
      result = func4(arg);
    }
  }
}
return result;

私が本質的にやっていることは、null以外を返すfunc1(args), func2(args),からの最初の結果を見つけることです。func3(args)func4(args)

Haskell では、 、 、 、および を値を返す関数としてモデル化func1し、func2失敗func3func4た場合にMaybe a値を返すことができるNothingようにしました。

func1, func2, func3, func4 :: Int -> Maybe Result

次に、<|>演算子 (from Control.Applicative) を使用できます。これは、次のように定義されていますMaybe a

Nothing <|> x = x
x       <|> _ = x

したがって、上記のJavaを次のように変換できます

func1 arg <|> func2 arg <|> func3 arg <|> func4 arg

また、遅延評価の奇跡により、 Java の例と同様に、が返されたfunc2 arg場合にのみ評価されます。func1 argNothing

于 2013-02-06T18:53:25.463 に答える
12

<|>そのRampionの優れた使用とsclvの同様の提案とは別に、別の一般的な方法は、ガードを使用し、怠惰を悪用することです。

move :: Int -> Maybe String
move n
    | n == 0       = Just "Finished the recursion"
    | isJust move3 = move3
    | isJust move2 = move2
    | isJust move1 = move1
    | otherwise    = Nothing
      where
        move3 = move 3
        move2 = move 2
        move1 = move 1

怠惰のため、move i( i = 3, 2, 1) は必要な場合にのみ評価されます。

与えられたケースでmove 3 <|> move 2 <|> move 1は、の方がはるかに優れていますが、条件が異なる戻り値の型で異なる関数を評価する必要がある場合、where句でガードと遅延バインディングを使用することは、厄介なネストされたifs を回避するための自然な解決策になる可能性があります。

于 2013-02-06T19:20:52.593 に答える
1

編集:新しい例のコードは次のとおりです。

move p routes points w h 
     | length routes == 2 = routes
     | otherwise = find (not . null) . map gtm [tallRightCorner, tallRightBCorner, tallLeftBCorner]
    where gtm f = goingToMove (f p) routes points w h

これは多分を返すことに注意してください。fromMaybeデフォルトの場合に固執するために使用できます。

これが最初に提案された例の古い(しかしタイプチェックの)コードです

move 0 = "Finished the recursion"
move n = concat . maybeToList . msum $ map move' [3,2,1]
  where move' x = let mx = move x in if null mx then Nothing else Just mx
于 2013-02-06T18:33:16.947 に答える
1

その長さが2であるかroutes、またはその一連のアプリケーションからの最初の非nullの結果がgoingToMove、どのコーナー関数に適用されるかによって異なるかどうかが必要ですp

move p routes points w h
  | length routes == 2 = routes
  | otherwise = head
              $ filter (not . null)
              $ map tryMove corners
    where tryMove f = goingToMove (f p) routes points w h
          corners = [ tallRightCorner
                    , tallRightBCorner
                    , tallLeftBCorner
                    -- et cetera
                    ]
于 2013-02-07T00:07:25.983 に答える
0

Maybe を使用しないオプションは、再帰にフラグを追加することです (以下の例では、フラグを 1 に設定して関数を呼び出します)。

p 個のルート ポイントを移動 Wh 1

move p routes points w h flag 
  | (length routes) == 2 = routes
  | otherwise = 
      if null result then move p routes points w h (flag+1)
      else result
        where result = case flag of 
                        1 -> goingToMove (tallRightCorner p) routes points w h
                        2 -> goingToMove (tallRightBCorner p) routes points w h
                        3 -> goingToMove (tallLeftBCorner p) routes points w h
                        --...etc.
                        _ -> []
于 2013-02-07T12:31:43.240 に答える