8

我想检查前一个的条件if condition以确定下一个if condition是否要执行。每个都if condition可能返回一个值。

编辑:抱歉,我之前提供的示例看起来有点奇怪......:(这是我的真实示例,我想简化if-then-elsefor goingToMove

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;

我实际上在做的是从func1(args), func2(args),func3(args)中找到第一个func4(args)返回非空值的结果。

在 Haskell 中,我将func1func2func3和建模func4为返回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

并且由于惰性评估的奇迹,仅在返回func2 arg时才评估,与 Java 示例中相同。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

routes如果它的长度为 2 或来自一系列应用程序的第一个非空结果,则您想要它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 回答