パスカルの三角形(Haskell)

パスカルの三角形」というのはこういうやつですね。

              1               
             1 1              
            1 2 1             
           1 3 3 1            
          1 4 6 4 1           
        1 5 10 10 5 1         
       1 6 15 20 15 6 1       
     1 7 21 35 35 21 7 1      

この出力を目標に、Haskell で書いてみます。

以前に Ruby で書いてみました。そのコードを再掲しましょう。

pascal = ->(n) {
  each_cons = ->(ar) {
    (ar.size < 2) ? [] : [ar[0] + ar[1]] + each_cons[ar.drop(1)]
  }
  n.zero? ? [1] : [1] + each_cons[pascal[n - 1]] + [1]
}

n = 8
n.times {|i| puts pascal[i].join(" ").center(30)}

簡潔ですね。これを手本にしてみます。

こんな感じでしょうか。

main :: IO ()
main = putStr $ unlines $ map (centering 30 . toString) (map pascal [0..7])

centering :: Int -> String -> String
centering n str = (replicate i ' ') ++ str ++ (replicate j ' ')
    where l = length str
          i = div (n - l) 2
          j = n - l - i

toString :: [Int] -> String
toString xs = tail $ concat $ map f xs
    where f x = ' ': show x

pascal :: Int -> [Int]
pascal 0 = [1]
pascal n = [1] ++ eachCons (pascal (n - 1)) ++ [1]
       
eachCons :: [Int] -> [Int]
eachCons xs = if length xs < 2
              then []
              else (head xs + (head . tail) xs): (eachCons . tail) xs

いやー、これだけでも悩みましたよ。
関数 pascal と eachCons は Ruby の動作とまったく同じです。あとは関数 toString と centering で結果を成形された文字列にしています。

ちなみに、

  • unlines 関数:[String]を改行を入れて結合する。[String] -> String。
  • concat 関数:リストの平坦化。
  • div 関数:整数/整数をして整数で返す。

です。

初心者が気づいたこと。

  • Haskell で繰り返しをするのはまず map を使うとラク でなければ再帰
  • show 関数を使えばとりあえず String になる
  • Ruby でのメソッドチェーンみたいなことができる

 

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!