2017-06-01 61 views
2

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也是如此吗?

如果我写的Haskell就像我编写的Python,我会做:

[[(x,y) | (x,_) <- zip [0..] (board!!y)] | (y,_) <- zip [0..] board] 

是令人难以接受的?

+0

更一般地说:在Python中,如果它是写得很好并且习惯用法的,我们就会谈论某些“Pythonic”。 “Haskellion”结构中是否有类似的术语? –

回答

4

你最后的“写它像Python”的建议几乎是好的,但你不必要地丢掉了棋盘上的行,然后用(!!)重新创建它们。写像这样反而会是完美的罚款:

board :: [[Char]] 
board = ["abc", "def", "ghi"] 

board' :: [[(Int, Int)]] 
board' = [[(x, y) | (x, _) <- zip [0..] row] 
        | (y, row) <- zip [0..] board] 
+0

这是完美的。不知道为什么我自己没有想到这个 - 谢谢! –

2

那么通常(!!) :: [a] -> Int -> a是不是真的一个很好的操作:它需要O(K)时间访问ķ个元素。对于您的小例子当然不是真正的问题,但它可以将一些算法从O(n)转换为O(n )

一般在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 ] 
1

你一边似乎在暗示你可能有兴趣在下面的函数,有时也被称为mapWithIndex(例如,in containers),有时被称为imapin lens)。

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)))将采取列表并注释每个元素的位置。当然,而不是注释,你可以执行任意计算。