在do块中将两个函数的返回值分配给两个变量

时间:2016-09-25 18:17:56

标签: haskell

对不起标题血腥(如果你能建议更好,请做)。但我的问题是,我不太明白如何让这个块工作。我有一个代码,可以在列表列表中返回5的位置。像这样:

findFive :: [[Int]] -> (Int, Int)
findFive rs = do
    x <- xPos rs 0
    y <- yPos rs 0
    return ( (x,y) )

xPos :: [[Int]] -> Int -> Int
xPos (rs:[[]]) n             = n
xPos (rs:rss)  n | elem 5 rs = n
                 | otherwise = xPos rss (n+1)

yPos :: [[Int]] -> Int -> Int
yPos (rs:[[]]) n              = n
yPos (rs:rss)  n | elem 5 rs  = n
                 | otherwise  = yPos rss (n+1)

但我不能用这种方式阻止我。我可以通过

来实现它
findFive :: [[Int]] -> (Int, Int)
findFive xs = ( (xPos xs 0), (yPos (transpose (xs)) 0) )

但这看起来有点难看。

此外,有没有办法让这个工作无需发送到0 xPosyPos

3 个答案:

答案 0 :(得分:8)

为什么do?那里没有单子。 let..in足够了:

findFive :: [[Int]] -> (Int, Int)
findFive rs = let
    x = xPos rs 0
    y = yPos rs 0
    in (x,y)

或者,使用where

findFive :: [[Int]] -> (Int, Int)
findFive rs = (x, y)
    where
    x = xPos rs 0
    y = yPos rs 0

答案 1 :(得分:0)

您不能以这种方式使用do块,因为在do块中您必须(1)将名称绑定到&#39;内容&#39; monadic值,和(2)返回包含在(1)中使用的相同monadic类型的值。在这种情况下,monadic类型将是列表。返回(行,列)对的列表是合适的,因为它会自动处理未找到数字或多次查找的情况。所以我们可以做类似

的事情
import Control.Monad

findFive ::
  [[Int]] ->   -- ^ a matrix of numbers.
  [(Int, Int)] -- ^ the (zero-indexed) rows and columns of the number
               -- ^ @5@, if any (otherwise empty list).
findFive xss =
  do
    (xs, rowIdx) <- xss `zip` [0 ..]
    (x, colIdx) <- xs `zip` [0 ..]
    guard $ x == 5

    return (rowIdx, colIdx)

input :: [[Int]]
input = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

-- Should print "row: 1, col: 1".
main :: IO ()
main =
  forM_ (findFive input) $ \(rowIdx, colIdx) ->
    do
      putStr "row: "
      putStr $ show rowIdx
      putStr ", col: "
      print colIdx

答案 2 :(得分:0)

假设pos以这种方式定义:

pos :: Eq =>    a          -- A value to find
             -> [[a]]      -- A 2d matrix
             -> Maybe Int  -- Index of first row containing the value, if any
pos k rows = pos' rows 0
             where pos' [] _ = Nothing
                   pos' (x:xs) n | elem k x = n
                                 | otherwise = pos' xs (n+1)

这里有一些变化:

  1. 它适用于定义了相等性的任何类型的列表,而不仅仅是Int
  2. 一般来说,找到任何值k :: a,而不只是5。
  3. 它可以处理无法找到包含k任何行。
  4. 通过这个定义,我们可以将findFive定义为

    findFive :: [[Int]] -> (Maybe Int, Maybe Int)
    findFive xs = (pos 5 xs, pos 5 (transpose xs))
    

    使用Control.Lens,您可以将函数pos 5分解出来,这样它只需要写一次。将over both视为map的版本,而不是列表。

    import Control.Lens
    findFive xs = over both (pos 5) (xs, transpose xs)
    

    使用Control.Arrow,您可以将参数xs分解出去,这样 it 只需要写一次。

    import Control.Lens
    import Control.Arrow
    findFive xs = over both (pos 5) ((id &&& transpose) xs)
    -- id &&& transpose = \x -> (id x, transpose x)
    

    完成后,您可以通过撰写findFiveover both (pos 5)轻松编写id &&& transpose无点样式:

    findFive = over both (pos 5) . (id &&& transpose)