Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/sorting/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 在do块中将两个函数的返回值赋给两个变量_Haskell - Fatal编程技术网

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

Haskell 在do块中将两个函数的返回值赋给两个变量,haskell,Haskell,抱歉的标题戈尔(如果你能建议一个更好的,请做)。但我的问题是,我不太明白如何让这个do块工作。我有一个返回列表中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

抱歉的标题戈尔(如果你能建议一个更好的,请做)。但我的问题是,我不太明白如何让这个do块工作。我有一个返回列表中
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)
但这看起来有点难看


另外,有没有一种方法可以让它工作,而不必将
0
发送到
xpo
ypo

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

findFive :: [[Int]] -> (Int, Int)
findFive rs = let
    x = xPos rs 0
    y = yPos rs 0
    in (x,y)
或者,在以下位置使用

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

您不能以这种方式使用
do
块,因为在
do
块中,您必须(1)将名称绑定到一元值的“内容”,以及(2)返回与(1)中使用的相同一元类型的值。在这种情况下,一元类型将是列表。返回(行、列)对的列表是合适的,因为这样会自动处理未找到数字或多次找到数字的情况。所以我们可以做一些类似的事情

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
import-Control.Monad
findFive::
[[Int]]->--^一个数字矩阵。
[(Int,Int)]--^编号的(零索引)行和列
--^@5@,如果有(否则为空列表)。
findFive xss=
做

(xs,rowIdx)假设
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)
这里有几个变化:

  • 它适用于定义等式的任何类型的列表,而不仅仅是
    Int
  • 它一般用于查找任何值
    k::a
    ,而不仅仅是5
  • 它可以处理找不到包含
    k
    的任何行的问题
  • 有了这个定义,我们可以将
    findFive
    定义为

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

    使用
    Control.Lens
    ,您可以将功能
    pos 5
    分解出来,以便只需写入一次。将两个
    都看作是成对的
    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)
    
    完成后,通过在(pos 5)
    id&&transpose
    上组合
    ,您可以轻松地以无点方式编写
    findFive

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

    XPO和YPO除了名称之外是相同的。这是完全不必要的,避免了函数的整个要点。当你发现自己用复制粘贴的方式编码时,几乎总有更好的方法。@Code学徒是的。我注意到它,改为一个
    pos
    函数。并在我发送的列表上调用transpose以获取Y位置一条小评论。。。在
    (yPos(转置(xs))
    xs
    周围不需要括号。请记住,在Haskell中,函数应用程序是一个简单的空格。括号用于指定运算符优先级。这与调用函数时在其他语言中使用括号不同。您可能已经知道了这一点。我只想为将来的读者指出这一点。
    findFive = over both (pos 5) . (id &&& transpose)