Je vais commencer par une amélioration du code du dernier article (merci @julienXX pour les indices).

La fonction createGeneration était comme ceci:

createGeneration :: [Int] -> Int -> [[Int]] -> [[Int]]
createGeneration [] width generation = generation
createGeneration cells width generation =
    let line = take width cells
     in createGeneration (drop width cells) width (line:generation)

Je l’ai transformé comme cela:

type Cell = Int

createGeneration :: Int -> [Cell] -> [[Cell]]
createGeneration _ [] = []
createGeneration width cells = line:(createGeneration width rest)
  where (line, rest) = splitAt width cells

Elle utilise maintenant splitAt, une fonction de base, qui simplifie la transformation d’une liste en une liste de listes. J’ai aussi créé un type Cell, qui me semble utile à des fins de documentation.

Affichage d’une génération

Le sujet principal de cet article, c’est l’affichage d’une génération dans le terminal. Voici ma solution:

import Data.List

formatGeneration :: [[Cell]] -> String
formatGeneration generation =
  let rows = intercalate "\n" (map (concatMap show) generation)
   in map replaceChar rows

replaceChar :: Char -> Char
replaceChar '1' = '@'
replaceChar '0' = ' '
replaceChar c   = c

En avant pour les explications pas à pas. concat concatène une liste de string et show transforme un élément en string.

> concat ["1", "0"]
"10"
> show 1
"1"

Je mappe la fonction show sur chaque élément d’une liste de nombres.

> map show [1,0]
["1","0"]

Puis je peux les concaténer.

> concat (map show [1,0])
"10"

concatMap est un raccourci pour concat (map ...).

> concatMap show [1,0]
"10"

On mappe le code précédent sur une génération complête.

> map (concatMap show) [[1,0], [0,0], [1,1]]
["10","00","11"]

Puis, grâce à intercalate, on joint les éléments avec un saut de ligne.

> import Data.List
> intercalate "\n" (map (concatMap show) [[1,0], [0,0], [1,1]]) 
"10\n00\n11"

Pour ce qui est de replaceChar, l’exemple suivant parle de lui-même.

> :load gol.hs 
> map replaceChar "10\n00\n11"
"@ \n  \n@@"

Voici le code actuel, n’hésitez pas à me faire part des améliorations possibles.

import System.Random
import Data.List

type Cell = Int

randomCells :: Int -> StdGen -> [Cell]
randomCells size gen = take size $ randomRs (0, 1) gen

createGeneration :: Int -> [Cell] -> [[Cell]]
createGeneration _ [] = []
createGeneration width cells = line:(createGeneration width rest)
  where (line, rest) = splitAt width cells

formatGeneration :: [[Cell]] -> String
formatGeneration generation =
  let rows = intercalate "\n" (map (concatMap show) generation)
   in map replaceChar rows

replaceChar :: Char -> Char
replaceChar '1' = '@'
replaceChar '0' = ' '
replaceChar c   = c

main :: IO()
main =
    let width = 80
        height = 24
        cells = randomCells (width * height) (mkStdGen 123)
        generation = createGeneration width cells
     in putStrLn $ formatGeneration generation
$ runhaskell gol.hs
@@@    @  @ @@@ @@@@@@ @ @  @ @   @ @ @  @@@@   @   @@  @  @@@@ @ @ @@@ @@  @  @
  @  @ @@ @@@ @@@@  @@ @@@@ @ @@@    @@ @ @ @@  @          @ @@@ @ @@@ @@@ @  @ 
  @ @@@ @@ @ @@ @@      @   @@@  @@    @  @ @@ @@ @@@   @@@@@@     @@  @@@@ @@ @
[...]