2017-06-01 6 views
2

Neu bei Haskell und versuchen, eine [[(Int, Int)]] zu konstruieren, wobei jedes Element seine entsprechende Gitterposition hat, die aus einer anderen Platine [[a]] aufgebaut ist. Daher ist eine quadratische [[a]] der Seitenlänge 3 schaffen würdeElement im Index wegwerfen, Elementtupel

[[(0, 0), (1, 0), (2, 0)] 
,[(0, 1), (1, 1), (2, 1)] 
,[(0, 2), (1, 2), (2, 2)]] 

(schließlich werde ich mit einem map (map ...) in Abhängigkeit von Art über diese werden Iterieren [[a]] -> (Int, Int) -> b einen [[b]] zu schaffen, also wenn ich etwas fehlt bin massiv einfacher, lassen Sie mich wissen)

In Python ich mag etwas tun könnte:

[[(x,y) for (x,_) in enumerate(board[y])] for (y,_) in enumerate(board)] 

das heißt, ich habe die enumerate builtinzu konstruieren, verwenden würde, 0 Tupel und wegwerfen das Element.

Ich weiß, in Haskell ich tun kann:

[[(x,y) | x <- [0..length (board!!y)-1]] | y <- [0..length board-1]] 

aber diese Arten von Konstruktionen in Python (for foo in range(len(bar))) sind ein bisschen ein anti-Muster und stark abgeraten. Trifft das auch in Haskell zu?

Wenn ich die Haskell schreiben waren, wie ich die Python schreiben würde, würde ich tun:

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

Ist das verpönt?

+0

Allgemeiner: In Python sprechen wir über etwas, das "Pythonic" ist, wenn es gut geschrieben und idiomatisch ist. Gibt es hier einen ähnlichen Begriff für ein "Haskellion" -Konstrukt? –

Antwort

4

Ihr letzter "Schreibe es wie Python" Vorschlag ist fast gut, aber Sie verwerfen unnötigerweise die Reihen des Boards und erstellen sie dann mit (!!) neu. Schreibt es so stattdessen wäre völlig in Ordnung:

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

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

Das ist perfekt. Nicht sicher, warum ich selbst nicht daran gedacht habe - danke! –

2

Gut in der Regel die (!!) :: [a] -> Int -> a nicht wirklich ein netter Betreiber ist: es erfordert O (k) Zeit, um das k -te Element zuzugreifen. Für Ihr kleines Beispiel ist das natürlich nicht wirklich ein Problem, aber es kann einige Algorithmen von O (n) in O (n) drehen.

Regel in Haskell, will man es vermeiden, durch geschickte Algorithmen schreiben, die Iterierte durch die Liste kann, statt einen (zufälligen) Index zu erhalten.

In Python können Sie Ihre neu schreiben:

[[(x,y) for (x,_) in enumerate(board[y])] for (y,_) in enumerate(board)] 

in:

[[(x,y) for (x,_) in enumerate(by)] for (y,by) in enumerate(board)]

und das Äquivalent in Haskell wäre:

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

Oder wir können durch den Code sauberer machen zuerst Einführung einer enumerate :: (Enum a, Num a) => [b] -> [(a, b)] Funktion in Haskell:

enumerate :: (Enum a, Num a) => [b] -> [(a, b)] 
enumerate = zip [0..] 

und dann schreiben:

[ [ (x,y) | (x,_) <- enumerate by ] | (y,by) <- enumerate board ] 
1

Ihre beiseite zu legen nahe scheinen, dass Sie in der folgenden Funktion interessiert sein könnten, manchmal genannt mapWithIndex (z.B. in containers) und manchmal auch imap (in 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 

So mapWithIndex (\i -> mapWithIndex (\j y -> (i,j,y))) wird eine Liste von Listen nehmen und jedes Element mit seiner Position mit Anmerkungen versehen. Natürlich können Sie, statt zu kommentieren, eine beliebige Berechnung durchführen.