2015-09-26 17 views
10

Thông thường thời gian tôi thấy mình cần được bỏ qua phần còn lại của lặp (như continue trong C) trong Haskell:Làm cách nào để "tiếp tục" trong vòng lặp `Monad`?

forM_ [1..100] $ \ i -> 
    a <- doSomeIO 
    when (not $ isValid1 a) <skip_rest_of_the_iteration> 
    b <- doSomeOtherIO a 
    when (not $ isValid2 b) <skip_rest_of_the_iteration> 
    ... 

Tuy nhiên, tôi không thể tìm thấy một cách dễ dàng để làm như vậy. Cách duy nhất tôi biết là có lẽ là Trans.Maybe, nhưng có cần phải sử dụng biến đổi đơn nguyên để đạt được điều gì đó tầm thường?

+2

Có một vài gói trên Hackage cung cấp vòng của các loại khác nhau. Điều thú vị nhất tôi từng thấy có lẽ là https://hackage.haskell.org/package/loops – dfeuer

+3

Biến thế Monad không bao giờ cần thiết - chỉ đơn giản là thuận tiện. –

Trả lời

13

Hãy nhớ rằng các vòng như thế này trong Haskell không phải là phép thuật ... chúng chỉ là những thứ hạng nhất bình thường mà bạn có thể tự viết.

Đối với những gì nó đáng giá, tôi không nghĩ rằng nó quá hữu ích khi nghĩ về MaybeT như một biến áp Monad. Với tôi, MaybeT chỉ là trình bao bọc newtype để thực hiện thay thế (>>=) ... giống như cách bạn sử dụng Product, Sum, First, And, v.v ... để thực hiện thay thế mappendmempty.

Ngay bây giờ, (>>=) dành cho bạn là IO a -> (a -> IO b) -> IO b. Nhưng sẽ hữu ích hơn khi có (>>=) ở đây là IO (Maybe a) -> (a -> IO (Maybe b) -> IO (Maybe b). Ngay sau khi bạn nhận được hành động đầu tiên trả về một số Nothing, thật sự không thể "ràng buộc" thêm nữa. Đó chính xác là những gì MaybeT mang đến cho bạn. Bạn cũng có một "trường hợp tùy chỉnh" của guard, guard :: Bool -> IO (Maybe a), thay vì guard :: IO a.

forM_ [1..100] $ \i -> runMaybeT $ do 
    a <- lift doSomeIO 
    guard (isValid1 a) 
    b <- lift $ doSomeOtherIO a 
    guard (isValid2 b) 
    ... 

và đó là nó :)

MaybeT không phải là ma thuật, hoặc, và bạn có thể đạt được về cơ bản tác dụng tương tự bằng cách sử dụng lồng nhau when s. Đó là không cần thiết, nó chỉ làm cho mọi thứ đơn giản hơn và sạch hơn :)

+0

Cảm ơn câu trả lời tuyệt vời. Tuy nhiên, có lẽ tôi đã không nói rõ (tôi đang chỉnh sửa câu hỏi của mình), nhưng những gì tôi muốn không phải là một 'break' mà là' tiếp tục'. Có một sự khác biệt tinh tế --- Tôi muốn bỏ qua phần còn lại của * lặp *, không phải toàn bộ vòng lặp. Tôi đã thay đổi câu hỏi. – trVoldemort

2

Nếu bạn muốn lặp qua danh sách hoặc vùng chứa khác để thực hiện hành động và/hoặc tạo giá trị tóm tắt và bạn đang tìm kiếm thông thường các công cụ tiện lợi như for_foldM không đủ tốt cho công việc, bạn có thể muốn xem xét foldr, đủ mạnh cho công việc. Khi bạn không thực sự looping trên một container, bạn có thể sử dụng đệ quy cũ đồng bằng hoặc kéo trong một cái gì đó như https://hackage.haskell.org/package/loops hoặc (cho một hương vị rất khác nhau) https://hackage.haskell.org/package/machines hoặc có lẽ https://hackage.haskell.org/package/pipes.

+0

Cảm ơn. Tôi đang thử 'vòng lặp', nhưng tôi không thể nhận được' break_' để làm việc. Bạn có thể xem [câu hỏi này] (http://stackoverflow.com/questions/32918416/how-do-i-use-break-in-the-package-loops) không? – trVoldemort

5

Đây là cách bạn sẽ làm điều đó bằng xương trần đệ quy:

loop [] = return() -- done with the loop 
loop (x:xs) = 
    do a <- doSomeIO 
    if ...a... 
     then return() -- exit the loop 
     else do -- continuing with the loop 
       b <- doSomeMoreIO 
       if ...b... 
        then return() -- exit the loop 
        else do -- continuing with the loop 
          ... 
          loop xs -- perform the next iteration 

và sau đó gọi nó với:

loop [1..100] 

Bạn có thể sắp xếp gọn gàng này lên một chút với when chức năng từ Control. Monad:

loop [] = return() 
    loop (x:xs) = 
     do a <- doSomeIO 
     when (not ...a...) $ do 
      b <- doSomeMoreIO 
      when (not ...b...) $ do 
      ... 
      loop xs 

Ngoài ra còn có unless trong Control.Mona d mà bạn có thể thích sử dụng.

Sử dụng @ lời khuyên hữu ích Ørjan Johansen 's, đây là một ví dụ đơn giản:

import Control.Monad 

loop [] = return() 
loop (x:xs) = do 
    putStrLn $ "x = " ++ show x 
    a <- getLine 
    when (a /= "stop") $ do 
    b <- getLine 
    when (b /= "stop") $ do 
    print $ "iteration: " ++ show x ++ ": a = " ++ a ++ " b = " ++ b 
    loop xs 

main = loop [1..3] 
Các vấn đề liên quan