Haskell的新手,并尝试构造一个[[(Int, Int)]]
,其中每个元素都是其对应的网格位置,由另一个板[[a]]
构成。因此,边长3的正方形[[a]]
将创建
[[(0, 0), (1, 0), (2, 0)]
,[(0, 1), (1, 1), (2, 1)]
,[(0, 2), (1, 2), (2, 2)]]
(最终我将使用map (map ...)
将其重复到[[a]] -> (Int, Int) -> b
类型的函数中以创建[[b]]
,所以如果我错过了一些非常简单的内容,请让我知道!)
在Python中我可能会做类似的事情:
[[(x,y) for (x,_) in enumerate(board[y])] for (y,_) in enumerate(board)]
也就是说,我会使用enumerate
内置来构造(index, element)
元组并扔掉元素。
我知道在Haskell中我能做到:
[[(x,y) | x <- [0..length (board!!y)-1]] | y <- [0..length board-1]]
但是Python中的那些构造(for foo in range(len(bar))
)有点像反模式而且非常沮丧。在Haskell也是如此吗?
如果我像编写Python那样编写Haskell,我会这样做:
[[(x,y) | (x,_) <- zip [0..] (board!!y)] | (y,_) <- zip [0..] board]
这令人不悦吗?
答案 0 :(得分:4)
你的决赛&#34;把它写成Python&#34;建议几乎是好的,但是你不必要地抛弃董事会的行,然后用(!!)
重新创建它们。像这样写它会非常好:
board :: [[Char]]
board = ["abc", "def", "ghi"]
board' :: [[(Int, Int)]]
board' = [[(x, y) | (x, _) <- zip [0..] row]
| (y, row) <- zip [0..] board]
答案 1 :(得分:2)
通常,(!!) :: [a] -> Int -> a
并不是一个很好的运算符:它需要 O(k)时间来访问 k -th元素。对于你的小例子当然不是真正的问题,但它可以将一些算法从 O(n)变成 O(n 2 )
通常在Haskell中,人们的目标是通过编写能够迭代通过列表的聪明算法来避免它,而不是获得(随机)索引。
在Python中,您可以重写:
[[(x,y) for (x,_) in enumerate(board[y])] for (y,_) in enumerate(board)]
成:
[[(x,y) for (x,_) in enumerate(by)] for (y,by) in enumerate(board)]
和Haskell中的等价物将是:
[ [ (x,y) | (x,_) <- zip [0..] by ] | (y,by) <- zip [0..] board ]
或者我们可以通过在Haskell中引入enumerate :: (Enum a, Num a) => [b] -> [(a, b)]
函数来使代码更清晰:
enumerate :: (Enum a, Num a) => [b] -> [(a, b)]
enumerate = zip [0..]
然后写:
[ [ (x,y) | (x,_) <- enumerate by ] | (y,by) <- enumerate board ]
答案 2 :(得分:1)
您的旁白似乎暗示您可能对以下函数感兴趣,有时称为mapWithIndex
(例如,in containers
),有时称为imap
(in lens
)。< / p>
mapWithIndex :: (Int -> a -> b) -> [a] -> [b]
mapWithIndex f = go 0
where
go !_i [] = []
go i (x : xs) = f i x : go (i + 1) xs
因此mapWithIndex (\i -> mapWithIndex (\j y -> (i,j,y)))
将获取列表列表并使用其位置注释每个元素。当然,您可以执行任意计算,而不是注释。