简体   繁体   English

如何在Haskell中简化嵌套if返回值

[英]How to simplify nested-if using to return value in Haskell

I want to check the condition of the previous if condition to determine the next if condition is to be executed or not. 我想检查上一个if condition以确定下一个if condition是否要执行。 Each if condition may return a value. 每个if condition可以返回一个值。

Edit: Sorry for that the example I provided before look a bit odd...:( This is my real example, and I want to simplify the if-then-else for goingToMove 编辑:对不起,我之前提供的示例有点奇怪... :(这是我的真实示例,我想简化go if-then-else ToMoveif-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 

Edit:Bad example When this thing is written in java, I may use a mutable boolean flag, and return a mutable data. 编辑:错误的例子当这个东西用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;
        }
    }

But in Haskell, there is no mutable data, and only if-then-else condition. 但是在Haskell中, 没有可变数据,只有if-then-else条件。 Then the code will looks like this, and I want to simplify this because in my real work, there are 8 levels of if-then-else which look terrible and messy.... 然后代码将看起来像这样,并且我想简化一下,因为在我的实际工作中,共有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

In Java if I wanted to do the following: 在Java中,如果我想执行以下操作:

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

What I'm essentially doing is finding the first result from func1(args) , func2(args) , func3(args) , func4(args) that returns non-null. 我实际上所做的是从func1(args)func2(args)func3(args)func4(args)中找到返回非null的第一个结果。

In Haskell, I'd model func1 , func2 , func3 , and func4 as functions that returned a Maybe a value, so that they could return Nothing if they failed. 在Haskell中,我func1func2func3func4建模为返回Maybe a值的函数,以便它们在失败时可以不返回Nothing

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

Then I can use the <|> operator (from Control.Applicative ), which has the following definition for Maybe a : 然后,我可以使用<|>运算符(来自Control.Applicative ),对于Maybe a具有以下定义:

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

So I can convert the above Java to 所以我可以将上面的Java转换为

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

And due to the miracle of lazy evaluation, func2 arg is only evaluated if func1 arg returns Nothing , same as in the Java example. 并且由于懒惰求值的奇迹,仅当func1 arg返回Nothing时才对func2 arg求值,这与Java示例中相同。

Apart from the nice employment of <|> that rampion gave and the similar suggestion of sclv , another common way is to use guards, and exploit laziness, 除了漂亮的就业<|>风铃草给了和的类似建议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

Due to laziness, move i ( i = 3, 2, 1 ) is only evaluated if it's needed. 由于懒惰,仅在需要时才评估move ii = 3, 2, 1 3,2,1)。

In the given case, move 3 <|> move 2 <|> move 1 is much nicer, but in cases where the conditions require evaluating different functions with different return types, the use of guards and lazy bindings in a where clause can be the natural solution to avoid awkward nested if s. 在给定的情况下, move 3 <|> move 2 <|> move 1更好,但是在条件需要评估具有不同返回类型的不同函数的情况下,可以在where子句中使用保护和惰性绑定自然的解决方案,以避免尴尬嵌套if秒。

edit : Here's some code for the new example: 编辑 :这是新示例的一些代码:

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

Note that this returns a maybe. 请注意,这可能返回一个。 You can use fromMaybe to stick in a default case. 您可以使用fromMaybe坚持默认情况。

Here's the old (but typechecking) code from the first proposed example 这是第一个建议的示例中的旧代码(但类型检查)

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

You want routes if its length is 2 or the first non-null result from a series of applications of goingToMove that vary by which corner function is applied to p . 您需要routes如果routes的长度为2或goingToMove一系列应用的第一个非空结果, 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
                    ]

An option without Maybe could be to add a flag to the recursion (in the example below, you would call the function with the flag set to one): 没有Maybe的选项可能是在递归中添加一个标志(在下面的示例中,您可以将标志设置为一个来调用该函数):

move p routes points wh 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.
                        _ -> []

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM