2016-09-25 54 views
2

对于标题gore(如果您可以提出更好的建议,请做)对不起。但我的问题是,我不太明白如何让这个阻止工作。我有一个代码在列表中返回5的位置。喜欢这样:将两个函数的返回值分配给一个do块中的两个变量

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)) 

但这看起来有点丑。

另外,有没有办法让这个工作没有发送0xPosyPos

+0

XPOS和yPos是,除了名字相同。这是完全不必要的,并且避免了整个功能点。当你发现自己使用复制和粘贴进行编码时,几乎总是有一个更好的方法。 –

+0

@ Code-Apprentice是的。我注意到,改为一个'pos'函数。在调用Y位置的列表中调用转置 –

+0

'(yPos(转置(xs))'中的一个小注释''不需要围绕'xs'的括号。请记住,在Haskell中,函数应用程序是一个简单的空格,括号用于指定运算符的优先级,这与调用函数时使用其他语言的括号不同,你可能已经知道了,我只是想为未来的读者指出它 –

回答

8

为什么do?那里没有monad。一个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 
+0

谢谢!我刚刚开始了解单子。 –

0

因为do块,你必须(1)绑定名称不能使用do块这样的 '内容' (2)返回一个与(1)中使用的monadic类型相同的值。在这种情况下,monadic类型将成为列表。返回(行,列)对的列表是合适的,因为它会自动处理找不到数字或找到多次的情况。因此,我们可以这样做

import Control.Monad 

findFive :: 
    [[Int]] -> --^a matrix of numbers. 
    [(Int, Int)] --^the (zero-indexed) rows and columns of the number 
       --^@[email protected], 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 
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

有了这个定义,我们可以定义为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使只需要写入一次。

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

一旦你做到了这一点,你可以随便写在卡点式findFive,通过组合over both (pos 5)id &&& transpose

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