7

Chcę sprawdzić stan poprzedniego if condition, aby ustalić, czy następny if condition ma zostać wykonany, czy nie. Każdy if condition może zwracać wartość.Jak uprościć zagnieżdżanie - jeśli używasz, aby zwrócić wartość w Haskell

Edit: Przepraszam, że przykład I dostarczone przed wyglądają trochę dziwnie ... :( To jest mój prawdziwy przykład, i chcę, aby uprościć if-then-else dla 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 

Edit: zły przykład Kiedy ta sprawa jest napisany w języku Java, mogą używać zmienny logiczną flagi i zwracają zmienne dane.

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; 
     } 
    } 

Ale w Haskell, nie ma zmiennego danych i tylko stan if-then-else. Potem wola kod wygląda tak, i chcę, aby uprościć to, ponieważ w mojej prawdziwej pracy, istnieje 8 poziomów if-then-else które wyglądają straszne i brudny ....

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 
+0

Czy "ruch", do którego dzwonisz w kodzie Java, jest taki sam, jak definiowany "ruch"? Jeśli tak, nie widzę, w jaki sposób nie pętla nieskończenie dla niezerowych danych wejściowych. 'move (3)' wywołania 'move (3)' wywołania 'move (3)' ... – rampion

+0

Podany kod haskell ma również niewłaściwy typ. Wyraźnie zwracasz 'null' w pewnym momencie kodu java, co oznacza, że ​​kodiem haskell powinno być' Int -> Maybe String', nawet jeśli nie był to nieskończona pętla. Aha, a sugerowany kod haskell ma również błąd składniowy (brak linii z literą if?), Co sprawia, że ​​trudno jest dokładnie określić, co robisz. – Carl

+0

Podałem prawdziwy przykład :(przepraszam – code4j

Odpowiedz

16

W Javie jeśli chciałem wykonać następujące czynności:

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

Co mam zasadniczo robi jest znalezienie pierwszy wynik z func1(args), func2(args), func3(args), func4(args), który zwraca wartość inną niż null.

W Haskell, bym modelować func1, func2, func3 i func4 jako funkcji zwracanych wartości Maybe a, tak aby mogły powrócić Nothing gdyby nie powiodło się.

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

Wtedy mogę użyć operatora <|> (od Control.Applicative), który posiada następującą definicję Maybe a:

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

Więc mogę przekonwertować powyżej Java

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

I ze względu na cud leniwej oceny, func2 arg jest oceniany tylko wtedy, gdy func1 arg zwraca Nothing, tak samo jak w przykładzie Java.

+0

ładnie wygląda, czy Nic nie znaczy zero? – code4j

+0

'Może a' jest typem danych, który ma dwa konstruktory:' Nothing :: Maybe a' i 'Just :: a -> Maybe a'. Może on być zdefiniowany jako' data Może a = Nothing | Just a.. – rampion

+0

Beautiful .Mmmmmm. – AndrewC

1

edit: Oto niektóre kodu dla nowego przykładu:

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 

Zwróć uwagę, że to zwraca być może. Możesz użyć fromMaybe, aby zachować domyślną wielkość liter.

Oto stary (ale typechecking) kod z pierwszego proponowanego przykład

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 
+2

To się nie kompiluje (chociaż kod w pytaniu też się nie kompiluje, więc ...) –

+0

whoops to jest to, co otrzymuję za poleganie na moim mentalnym kontrolerze Type: – sclv

+0

Podałem prawdziwy przykład :(przepraszam – code4j

12

Oprócz miłej zatrudnienia <|> że rampion dał i podobna sugestia sclv, innym popularnym sposobem jest użycie strażników i wykorzystać lenistwo,

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 

Z powodu lenistwa, move i (i = 3, 2, 1) jest tylko ocenić, czy jest to potrzebne.

W danym przypadku move 3 <|> move 2 <|> move 1 jest o wiele ładniejszy, ale w przypadku, gdy warunki wymagają oceny różne funkcje w różnych typach powrotów, stosowanie osłon i leniwych wiązań w where klauzuli może być naturalne rozwiązanie, aby uniknąć niewygodne zagnieżdżona if s.

+0

co oznacza "Just" i "isJust" ????? – code4j

+1

'Just' jest konstruktorem wartości' Maybe a'. Ponieważ zwracasz i sprawdzasz 'null' w twojej Javie, mając wersję Haskella, powróć do' Maybe String' jest właściwą analogią. 'isJust' (dostępny z modułu' Data.Maybe') sprawdza, czy wartość 'Maybe a' to' Just a' lub 'Nothing', więc' isJust move3' odpowiada 'if ((result = move (3))! = null) '[przerzucanie twojego' if/else's]. –

1

Chcesz routes jeśli jego długość wynosi 2 lub pierwsze niezerowe wynik z szeregu zastosowań goingToMove, które różnią się od funkcji, które stosuje się do rogu 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 
        ] 
0

Opcja bez Może być, aby dodać flagę do rekursji (w poniższym przykładzie, należy wywołać funkcję z ustawioną flagą jednego):

ruch p punktów tras 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. 
         _ -> []