2017-06-01 87 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)))將採取列表並註釋每個元素的位置。當然,而不是註釋,你可以執行任意計算。