扔掉索引元素元组中的元素

时间:2017-06-01 21:33:27

标签: list haskell list-comprehension enumerate

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]

这令人不悦吗?

3 个答案:

答案 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),有时称为imapin 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)))将获取列表列表并使用其位置注释每个元素。当然,您可以执行任意计算,而不是注释。