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