Pascal Triangle in Haskell

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
3
down vote

favorite
2












The code below generates a list of Pascal Coefficients.
e.g pascalList 3 outputs [[1], [1,1], [1,2,1], [1,3,3,1]
Can we write below sample in more idiomatic way



pascalList 0 = [[1]]
pascalList 1 = [[1], [1, 1]]
pascalList n = let pList = pascalList (n-1)
in pList ++ [([1] ++ pascalCoeff (last pList) ++ [1])]
where pascalCoeff (x:y:ys) = (x+y) : pascalCoeff (y:ys)
pascalCoeff (x:) =


Following code prints above list



listtoString :: [Int] -> String
listtoString =
listtoString [x] = show x
listtoString (x:xs) = show x ++ " " ++ listtoString xs

pascalTriangle :: Int -> IO ()
pascalTriangle n = mapM_ putStrLn (((justify n) . map listtoString) (pascalList n))

justify :: Int -> [String] -> [String]
justify n (x:xs) = (concat (replicate n " ") ++ x) : justify (n-1) xs
justify _ =


The sample output of the pascalTriangle 4 will be



 1
1 1
1 2 1
1 3 3 1
1 4 6 4 1






share|improve this question



























    up vote
    3
    down vote

    favorite
    2












    The code below generates a list of Pascal Coefficients.
    e.g pascalList 3 outputs [[1], [1,1], [1,2,1], [1,3,3,1]
    Can we write below sample in more idiomatic way



    pascalList 0 = [[1]]
    pascalList 1 = [[1], [1, 1]]
    pascalList n = let pList = pascalList (n-1)
    in pList ++ [([1] ++ pascalCoeff (last pList) ++ [1])]
    where pascalCoeff (x:y:ys) = (x+y) : pascalCoeff (y:ys)
    pascalCoeff (x:) =


    Following code prints above list



    listtoString :: [Int] -> String
    listtoString =
    listtoString [x] = show x
    listtoString (x:xs) = show x ++ " " ++ listtoString xs

    pascalTriangle :: Int -> IO ()
    pascalTriangle n = mapM_ putStrLn (((justify n) . map listtoString) (pascalList n))

    justify :: Int -> [String] -> [String]
    justify n (x:xs) = (concat (replicate n " ") ++ x) : justify (n-1) xs
    justify _ =


    The sample output of the pascalTriangle 4 will be



     1
    1 1
    1 2 1
    1 3 3 1
    1 4 6 4 1






    share|improve this question























      up vote
      3
      down vote

      favorite
      2









      up vote
      3
      down vote

      favorite
      2






      2





      The code below generates a list of Pascal Coefficients.
      e.g pascalList 3 outputs [[1], [1,1], [1,2,1], [1,3,3,1]
      Can we write below sample in more idiomatic way



      pascalList 0 = [[1]]
      pascalList 1 = [[1], [1, 1]]
      pascalList n = let pList = pascalList (n-1)
      in pList ++ [([1] ++ pascalCoeff (last pList) ++ [1])]
      where pascalCoeff (x:y:ys) = (x+y) : pascalCoeff (y:ys)
      pascalCoeff (x:) =


      Following code prints above list



      listtoString :: [Int] -> String
      listtoString =
      listtoString [x] = show x
      listtoString (x:xs) = show x ++ " " ++ listtoString xs

      pascalTriangle :: Int -> IO ()
      pascalTriangle n = mapM_ putStrLn (((justify n) . map listtoString) (pascalList n))

      justify :: Int -> [String] -> [String]
      justify n (x:xs) = (concat (replicate n " ") ++ x) : justify (n-1) xs
      justify _ =


      The sample output of the pascalTriangle 4 will be



       1
      1 1
      1 2 1
      1 3 3 1
      1 4 6 4 1






      share|improve this question













      The code below generates a list of Pascal Coefficients.
      e.g pascalList 3 outputs [[1], [1,1], [1,2,1], [1,3,3,1]
      Can we write below sample in more idiomatic way



      pascalList 0 = [[1]]
      pascalList 1 = [[1], [1, 1]]
      pascalList n = let pList = pascalList (n-1)
      in pList ++ [([1] ++ pascalCoeff (last pList) ++ [1])]
      where pascalCoeff (x:y:ys) = (x+y) : pascalCoeff (y:ys)
      pascalCoeff (x:) =


      Following code prints above list



      listtoString :: [Int] -> String
      listtoString =
      listtoString [x] = show x
      listtoString (x:xs) = show x ++ " " ++ listtoString xs

      pascalTriangle :: Int -> IO ()
      pascalTriangle n = mapM_ putStrLn (((justify n) . map listtoString) (pascalList n))

      justify :: Int -> [String] -> [String]
      justify n (x:xs) = (concat (replicate n " ") ++ x) : justify (n-1) xs
      justify _ =


      The sample output of the pascalTriangle 4 will be



       1
      1 1
      1 2 1
      1 3 3 1
      1 4 6 4 1








      share|improve this question












      share|improve this question




      share|improve this question








      edited Apr 3 at 16:57









      200_success

      123k14142399




      123k14142399









      asked Apr 3 at 13:06









      progrookie

      362




      362




















          2 Answers
          2






          active

          oldest

          votes

















          up vote
          6
          down vote













          I don't know if this is more idiomatic, but ever since I've seen an infinite list construction for the Fibonacci numbers, I've been in love with it, so here goes nothing.



          pascalTriangle :: [[Integer]]
          pascalTriangle = [1] : map newRow pascalTriangle
          where newRow y = 1 : zipWith (+) y (tail y) ++ [1]


          To understand this, perhaps first you should look at simpler examples, e.g. an infinite list of zeros,



          zeros = 0 : zeros


          or the list of natural numbers,



          nats = 0 : map (+1) nats


          or my favourite, the list of fibonacci numbers,



          fibs = 1 : 1 : zipWith (+) fibs (tail fibs)



          That being said...



          The line



          pascalList 1 = [[1], [1, 1]]


          is unnecessary, because you've already specified pascalList 0. The line



          [([1] ++ pascalCoeff (last pList) ++ [1])]


          is equivalent to



          [[1] ++ pascalCoeff (last pList) ++ [1]]


          which is equivalent to



          [ 1 : pascalCoeff (last pList) ++ [1]]


          Whenever possible, I try to use functions from the standard library instead of rolling my own using recursion, so for example instead of



          pascalCoeff (x:y:ys) = (x+y) : pascalCoeff (y:ys)
          pascalCoeff (x:) =


          I'd say that this function takes a list e.g. [1,2,3,4] and does the following:



           [1, 2, 3]
          [2, 3, 4]
          + ---------
          [3, 5, 7]


          so it takes the tail (all but the first element) of the list, and the init (all but the last element) of the list, and adds them together element-wise.



          There are built-in functions tail, and init that yield you these parts from a list, and luckily the function



          zipWith (+)


          does the adding part, so you can simply say



          pascalCoeff y = zipWith (+) (init y) (tail y)


          which is, due to the way zipWith works, the same as



          pascalCoeff y = zipWith (+) y (tail y)



          Similarly,



          listtoString = 
          listtoString [x] = show x
          listtoString (x:xs) = show x ++ " " ++ listtoString xs


          could simply be



          listToString = unwords . map show



          And



          justify :: Int -> [String] -> [String]
          justify n (x:xs) = (concat (replicate n " ") ++ x) : justify (n-1) xs
          justify _ =


          could be



          justify n ss = zipWith (++) padding ss
          where padding = [ replicate k ' ' | k <- [n, n-1 .. 1]]


          or equivalently, after eta-reduction



          justify n = zipWith (++) padding
          where padding = [ replicate k ' ' | k <- [n, n-1 .. 1]]



          Due to the fact that lists are linked lists, appending to a list is expensive, while prepending to it is cheap. So if you do decide to construct a list element-by-element, then I'd say you should prepend the new elements, and perhaps after the list is built, use a reverse.



          By the same logic, head is cheap, last is expensive.



          Here's a possible implementation that uses what I've just said.



          pascalList = reverse . pascalList'
          pascalList' 0 = [[1]]
          pascalList' n = new : old
          where new = 1 : pascalCoeff (head old) ++ [1]
          old = pascalList' (n-1)



          In my opinion it is a good idea to



          • separate pure and impure code,

          • keep your code as modular as possible,

          • write type signatures,

          • use a linter like hlint,

          • use functions from the standard library instead of writing explicit recursions.

          Keeping this in mind, here's how I'd write the printing part.



          listToString :: (Show a) => [a] -> String
          listToString = unwords . map show

          leftPadStrings :: [String] -> [String]
          leftPadStrings ss = map leftPad ss
          where maxlen = maximum $ map length ss
          leftPad s = replicate (div (maxlen - length s) 2) ' ' ++ s

          paddedPascalTriangle :: Int -> [String]
          paddedPascalTriangle n = leftPadStrings
          . map listToString
          . take n
          $ pascalTriangle

          printPascalTriangle :: Int -> IO ()
          printPascalTriangle = mapM_ putStrLn . paddedPascalTriangle


          Running this, you should get something like the following:



          *Main> printPascalTriangle 10
          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
          1 8 28 56 70 56 28 8 1
          1 9 36 84 126 126 84 36 9 1





          share|improve this answer























          • You have presented an alternative solution, but haven't reviewed the code. Please explain your reasoning (how your solution works and why it is better than the original) so that the author and other readers can learn from your thought process.
            – Zeta
            Apr 3 at 16:33










          • @Zeta "It is a good idea to separate pure and impure code" was the justification given for the rewrite.
            – 200_success
            Apr 3 at 16:58










          • @Zeta Your comment is justified, because initially I just wanted OP to know about infinite data types, because I think it's a cool concept. Since then I've added some of the more traditional code review.
            – Andrew
            Apr 3 at 21:53











          • If I had have time, I would have used the same concept :). That being said, zipWith func (init xs) (tail xs) is the same as zipWith func xs (tail xs), just as in fibs = 1 : 1 : zibWith (+) fibs (tail fibs).
            – Zeta
            Apr 4 at 4:03











          • @Zeta ah, yes, you are right, thanks. In fact I've used the same property when I rewrote justify. Oh well, I'll edit it in.
            – Andrew
            Apr 4 at 5:49

















          up vote
          4
          down vote













          The third case subsumes the second. All possible result lists are prefixes of the same infinite list - let's define that instead. iterate captures this pattern. A combination of zipWith and tail captures pascalCoeff.



          pascalList :: [[Int]]
          pascalList = flip iterate [1] $ pList -> 1 : zipWith (+) pList (tail pList) ++ [1]





          share|improve this answer





















            Your Answer




            StackExchange.ifUsing("editor", function ()
            return StackExchange.using("mathjaxEditing", function ()
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            );
            );
            , "mathjax-editing");

            StackExchange.ifUsing("editor", function ()
            StackExchange.using("externalEditor", function ()
            StackExchange.using("snippets", function ()
            StackExchange.snippets.init();
            );
            );
            , "code-snippets");

            StackExchange.ready(function()
            var channelOptions =
            tags: "".split(" "),
            id: "196"
            ;
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function()
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled)
            StackExchange.using("snippets", function()
            createEditor();
            );

            else
            createEditor();

            );

            function createEditor()
            StackExchange.prepareEditor(
            heartbeatType: 'answer',
            convertImagesToLinks: false,
            noModals: false,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            );



            );








             

            draft saved


            draft discarded


















            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f191159%2fpascal-triangle-in-haskell%23new-answer', 'question_page');

            );

            Post as a guest






























            2 Answers
            2






            active

            oldest

            votes








            2 Answers
            2






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes








            up vote
            6
            down vote













            I don't know if this is more idiomatic, but ever since I've seen an infinite list construction for the Fibonacci numbers, I've been in love with it, so here goes nothing.



            pascalTriangle :: [[Integer]]
            pascalTriangle = [1] : map newRow pascalTriangle
            where newRow y = 1 : zipWith (+) y (tail y) ++ [1]


            To understand this, perhaps first you should look at simpler examples, e.g. an infinite list of zeros,



            zeros = 0 : zeros


            or the list of natural numbers,



            nats = 0 : map (+1) nats


            or my favourite, the list of fibonacci numbers,



            fibs = 1 : 1 : zipWith (+) fibs (tail fibs)



            That being said...



            The line



            pascalList 1 = [[1], [1, 1]]


            is unnecessary, because you've already specified pascalList 0. The line



            [([1] ++ pascalCoeff (last pList) ++ [1])]


            is equivalent to



            [[1] ++ pascalCoeff (last pList) ++ [1]]


            which is equivalent to



            [ 1 : pascalCoeff (last pList) ++ [1]]


            Whenever possible, I try to use functions from the standard library instead of rolling my own using recursion, so for example instead of



            pascalCoeff (x:y:ys) = (x+y) : pascalCoeff (y:ys)
            pascalCoeff (x:) =


            I'd say that this function takes a list e.g. [1,2,3,4] and does the following:



             [1, 2, 3]
            [2, 3, 4]
            + ---------
            [3, 5, 7]


            so it takes the tail (all but the first element) of the list, and the init (all but the last element) of the list, and adds them together element-wise.



            There are built-in functions tail, and init that yield you these parts from a list, and luckily the function



            zipWith (+)


            does the adding part, so you can simply say



            pascalCoeff y = zipWith (+) (init y) (tail y)


            which is, due to the way zipWith works, the same as



            pascalCoeff y = zipWith (+) y (tail y)



            Similarly,



            listtoString = 
            listtoString [x] = show x
            listtoString (x:xs) = show x ++ " " ++ listtoString xs


            could simply be



            listToString = unwords . map show



            And



            justify :: Int -> [String] -> [String]
            justify n (x:xs) = (concat (replicate n " ") ++ x) : justify (n-1) xs
            justify _ =


            could be



            justify n ss = zipWith (++) padding ss
            where padding = [ replicate k ' ' | k <- [n, n-1 .. 1]]


            or equivalently, after eta-reduction



            justify n = zipWith (++) padding
            where padding = [ replicate k ' ' | k <- [n, n-1 .. 1]]



            Due to the fact that lists are linked lists, appending to a list is expensive, while prepending to it is cheap. So if you do decide to construct a list element-by-element, then I'd say you should prepend the new elements, and perhaps after the list is built, use a reverse.



            By the same logic, head is cheap, last is expensive.



            Here's a possible implementation that uses what I've just said.



            pascalList = reverse . pascalList'
            pascalList' 0 = [[1]]
            pascalList' n = new : old
            where new = 1 : pascalCoeff (head old) ++ [1]
            old = pascalList' (n-1)



            In my opinion it is a good idea to



            • separate pure and impure code,

            • keep your code as modular as possible,

            • write type signatures,

            • use a linter like hlint,

            • use functions from the standard library instead of writing explicit recursions.

            Keeping this in mind, here's how I'd write the printing part.



            listToString :: (Show a) => [a] -> String
            listToString = unwords . map show

            leftPadStrings :: [String] -> [String]
            leftPadStrings ss = map leftPad ss
            where maxlen = maximum $ map length ss
            leftPad s = replicate (div (maxlen - length s) 2) ' ' ++ s

            paddedPascalTriangle :: Int -> [String]
            paddedPascalTriangle n = leftPadStrings
            . map listToString
            . take n
            $ pascalTriangle

            printPascalTriangle :: Int -> IO ()
            printPascalTriangle = mapM_ putStrLn . paddedPascalTriangle


            Running this, you should get something like the following:



            *Main> printPascalTriangle 10
            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
            1 8 28 56 70 56 28 8 1
            1 9 36 84 126 126 84 36 9 1





            share|improve this answer























            • You have presented an alternative solution, but haven't reviewed the code. Please explain your reasoning (how your solution works and why it is better than the original) so that the author and other readers can learn from your thought process.
              – Zeta
              Apr 3 at 16:33










            • @Zeta "It is a good idea to separate pure and impure code" was the justification given for the rewrite.
              – 200_success
              Apr 3 at 16:58










            • @Zeta Your comment is justified, because initially I just wanted OP to know about infinite data types, because I think it's a cool concept. Since then I've added some of the more traditional code review.
              – Andrew
              Apr 3 at 21:53











            • If I had have time, I would have used the same concept :). That being said, zipWith func (init xs) (tail xs) is the same as zipWith func xs (tail xs), just as in fibs = 1 : 1 : zibWith (+) fibs (tail fibs).
              – Zeta
              Apr 4 at 4:03











            • @Zeta ah, yes, you are right, thanks. In fact I've used the same property when I rewrote justify. Oh well, I'll edit it in.
              – Andrew
              Apr 4 at 5:49














            up vote
            6
            down vote













            I don't know if this is more idiomatic, but ever since I've seen an infinite list construction for the Fibonacci numbers, I've been in love with it, so here goes nothing.



            pascalTriangle :: [[Integer]]
            pascalTriangle = [1] : map newRow pascalTriangle
            where newRow y = 1 : zipWith (+) y (tail y) ++ [1]


            To understand this, perhaps first you should look at simpler examples, e.g. an infinite list of zeros,



            zeros = 0 : zeros


            or the list of natural numbers,



            nats = 0 : map (+1) nats


            or my favourite, the list of fibonacci numbers,



            fibs = 1 : 1 : zipWith (+) fibs (tail fibs)



            That being said...



            The line



            pascalList 1 = [[1], [1, 1]]


            is unnecessary, because you've already specified pascalList 0. The line



            [([1] ++ pascalCoeff (last pList) ++ [1])]


            is equivalent to



            [[1] ++ pascalCoeff (last pList) ++ [1]]


            which is equivalent to



            [ 1 : pascalCoeff (last pList) ++ [1]]


            Whenever possible, I try to use functions from the standard library instead of rolling my own using recursion, so for example instead of



            pascalCoeff (x:y:ys) = (x+y) : pascalCoeff (y:ys)
            pascalCoeff (x:) =


            I'd say that this function takes a list e.g. [1,2,3,4] and does the following:



             [1, 2, 3]
            [2, 3, 4]
            + ---------
            [3, 5, 7]


            so it takes the tail (all but the first element) of the list, and the init (all but the last element) of the list, and adds them together element-wise.



            There are built-in functions tail, and init that yield you these parts from a list, and luckily the function



            zipWith (+)


            does the adding part, so you can simply say



            pascalCoeff y = zipWith (+) (init y) (tail y)


            which is, due to the way zipWith works, the same as



            pascalCoeff y = zipWith (+) y (tail y)



            Similarly,



            listtoString = 
            listtoString [x] = show x
            listtoString (x:xs) = show x ++ " " ++ listtoString xs


            could simply be



            listToString = unwords . map show



            And



            justify :: Int -> [String] -> [String]
            justify n (x:xs) = (concat (replicate n " ") ++ x) : justify (n-1) xs
            justify _ =


            could be



            justify n ss = zipWith (++) padding ss
            where padding = [ replicate k ' ' | k <- [n, n-1 .. 1]]


            or equivalently, after eta-reduction



            justify n = zipWith (++) padding
            where padding = [ replicate k ' ' | k <- [n, n-1 .. 1]]



            Due to the fact that lists are linked lists, appending to a list is expensive, while prepending to it is cheap. So if you do decide to construct a list element-by-element, then I'd say you should prepend the new elements, and perhaps after the list is built, use a reverse.



            By the same logic, head is cheap, last is expensive.



            Here's a possible implementation that uses what I've just said.



            pascalList = reverse . pascalList'
            pascalList' 0 = [[1]]
            pascalList' n = new : old
            where new = 1 : pascalCoeff (head old) ++ [1]
            old = pascalList' (n-1)



            In my opinion it is a good idea to



            • separate pure and impure code,

            • keep your code as modular as possible,

            • write type signatures,

            • use a linter like hlint,

            • use functions from the standard library instead of writing explicit recursions.

            Keeping this in mind, here's how I'd write the printing part.



            listToString :: (Show a) => [a] -> String
            listToString = unwords . map show

            leftPadStrings :: [String] -> [String]
            leftPadStrings ss = map leftPad ss
            where maxlen = maximum $ map length ss
            leftPad s = replicate (div (maxlen - length s) 2) ' ' ++ s

            paddedPascalTriangle :: Int -> [String]
            paddedPascalTriangle n = leftPadStrings
            . map listToString
            . take n
            $ pascalTriangle

            printPascalTriangle :: Int -> IO ()
            printPascalTriangle = mapM_ putStrLn . paddedPascalTriangle


            Running this, you should get something like the following:



            *Main> printPascalTriangle 10
            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
            1 8 28 56 70 56 28 8 1
            1 9 36 84 126 126 84 36 9 1





            share|improve this answer























            • You have presented an alternative solution, but haven't reviewed the code. Please explain your reasoning (how your solution works and why it is better than the original) so that the author and other readers can learn from your thought process.
              – Zeta
              Apr 3 at 16:33










            • @Zeta "It is a good idea to separate pure and impure code" was the justification given for the rewrite.
              – 200_success
              Apr 3 at 16:58










            • @Zeta Your comment is justified, because initially I just wanted OP to know about infinite data types, because I think it's a cool concept. Since then I've added some of the more traditional code review.
              – Andrew
              Apr 3 at 21:53











            • If I had have time, I would have used the same concept :). That being said, zipWith func (init xs) (tail xs) is the same as zipWith func xs (tail xs), just as in fibs = 1 : 1 : zibWith (+) fibs (tail fibs).
              – Zeta
              Apr 4 at 4:03











            • @Zeta ah, yes, you are right, thanks. In fact I've used the same property when I rewrote justify. Oh well, I'll edit it in.
              – Andrew
              Apr 4 at 5:49












            up vote
            6
            down vote










            up vote
            6
            down vote









            I don't know if this is more idiomatic, but ever since I've seen an infinite list construction for the Fibonacci numbers, I've been in love with it, so here goes nothing.



            pascalTriangle :: [[Integer]]
            pascalTriangle = [1] : map newRow pascalTriangle
            where newRow y = 1 : zipWith (+) y (tail y) ++ [1]


            To understand this, perhaps first you should look at simpler examples, e.g. an infinite list of zeros,



            zeros = 0 : zeros


            or the list of natural numbers,



            nats = 0 : map (+1) nats


            or my favourite, the list of fibonacci numbers,



            fibs = 1 : 1 : zipWith (+) fibs (tail fibs)



            That being said...



            The line



            pascalList 1 = [[1], [1, 1]]


            is unnecessary, because you've already specified pascalList 0. The line



            [([1] ++ pascalCoeff (last pList) ++ [1])]


            is equivalent to



            [[1] ++ pascalCoeff (last pList) ++ [1]]


            which is equivalent to



            [ 1 : pascalCoeff (last pList) ++ [1]]


            Whenever possible, I try to use functions from the standard library instead of rolling my own using recursion, so for example instead of



            pascalCoeff (x:y:ys) = (x+y) : pascalCoeff (y:ys)
            pascalCoeff (x:) =


            I'd say that this function takes a list e.g. [1,2,3,4] and does the following:



             [1, 2, 3]
            [2, 3, 4]
            + ---------
            [3, 5, 7]


            so it takes the tail (all but the first element) of the list, and the init (all but the last element) of the list, and adds them together element-wise.



            There are built-in functions tail, and init that yield you these parts from a list, and luckily the function



            zipWith (+)


            does the adding part, so you can simply say



            pascalCoeff y = zipWith (+) (init y) (tail y)


            which is, due to the way zipWith works, the same as



            pascalCoeff y = zipWith (+) y (tail y)



            Similarly,



            listtoString = 
            listtoString [x] = show x
            listtoString (x:xs) = show x ++ " " ++ listtoString xs


            could simply be



            listToString = unwords . map show



            And



            justify :: Int -> [String] -> [String]
            justify n (x:xs) = (concat (replicate n " ") ++ x) : justify (n-1) xs
            justify _ =


            could be



            justify n ss = zipWith (++) padding ss
            where padding = [ replicate k ' ' | k <- [n, n-1 .. 1]]


            or equivalently, after eta-reduction



            justify n = zipWith (++) padding
            where padding = [ replicate k ' ' | k <- [n, n-1 .. 1]]



            Due to the fact that lists are linked lists, appending to a list is expensive, while prepending to it is cheap. So if you do decide to construct a list element-by-element, then I'd say you should prepend the new elements, and perhaps after the list is built, use a reverse.



            By the same logic, head is cheap, last is expensive.



            Here's a possible implementation that uses what I've just said.



            pascalList = reverse . pascalList'
            pascalList' 0 = [[1]]
            pascalList' n = new : old
            where new = 1 : pascalCoeff (head old) ++ [1]
            old = pascalList' (n-1)



            In my opinion it is a good idea to



            • separate pure and impure code,

            • keep your code as modular as possible,

            • write type signatures,

            • use a linter like hlint,

            • use functions from the standard library instead of writing explicit recursions.

            Keeping this in mind, here's how I'd write the printing part.



            listToString :: (Show a) => [a] -> String
            listToString = unwords . map show

            leftPadStrings :: [String] -> [String]
            leftPadStrings ss = map leftPad ss
            where maxlen = maximum $ map length ss
            leftPad s = replicate (div (maxlen - length s) 2) ' ' ++ s

            paddedPascalTriangle :: Int -> [String]
            paddedPascalTriangle n = leftPadStrings
            . map listToString
            . take n
            $ pascalTriangle

            printPascalTriangle :: Int -> IO ()
            printPascalTriangle = mapM_ putStrLn . paddedPascalTriangle


            Running this, you should get something like the following:



            *Main> printPascalTriangle 10
            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
            1 8 28 56 70 56 28 8 1
            1 9 36 84 126 126 84 36 9 1





            share|improve this answer















            I don't know if this is more idiomatic, but ever since I've seen an infinite list construction for the Fibonacci numbers, I've been in love with it, so here goes nothing.



            pascalTriangle :: [[Integer]]
            pascalTriangle = [1] : map newRow pascalTriangle
            where newRow y = 1 : zipWith (+) y (tail y) ++ [1]


            To understand this, perhaps first you should look at simpler examples, e.g. an infinite list of zeros,



            zeros = 0 : zeros


            or the list of natural numbers,



            nats = 0 : map (+1) nats


            or my favourite, the list of fibonacci numbers,



            fibs = 1 : 1 : zipWith (+) fibs (tail fibs)



            That being said...



            The line



            pascalList 1 = [[1], [1, 1]]


            is unnecessary, because you've already specified pascalList 0. The line



            [([1] ++ pascalCoeff (last pList) ++ [1])]


            is equivalent to



            [[1] ++ pascalCoeff (last pList) ++ [1]]


            which is equivalent to



            [ 1 : pascalCoeff (last pList) ++ [1]]


            Whenever possible, I try to use functions from the standard library instead of rolling my own using recursion, so for example instead of



            pascalCoeff (x:y:ys) = (x+y) : pascalCoeff (y:ys)
            pascalCoeff (x:) =


            I'd say that this function takes a list e.g. [1,2,3,4] and does the following:



             [1, 2, 3]
            [2, 3, 4]
            + ---------
            [3, 5, 7]


            so it takes the tail (all but the first element) of the list, and the init (all but the last element) of the list, and adds them together element-wise.



            There are built-in functions tail, and init that yield you these parts from a list, and luckily the function



            zipWith (+)


            does the adding part, so you can simply say



            pascalCoeff y = zipWith (+) (init y) (tail y)


            which is, due to the way zipWith works, the same as



            pascalCoeff y = zipWith (+) y (tail y)



            Similarly,



            listtoString = 
            listtoString [x] = show x
            listtoString (x:xs) = show x ++ " " ++ listtoString xs


            could simply be



            listToString = unwords . map show



            And



            justify :: Int -> [String] -> [String]
            justify n (x:xs) = (concat (replicate n " ") ++ x) : justify (n-1) xs
            justify _ =


            could be



            justify n ss = zipWith (++) padding ss
            where padding = [ replicate k ' ' | k <- [n, n-1 .. 1]]


            or equivalently, after eta-reduction



            justify n = zipWith (++) padding
            where padding = [ replicate k ' ' | k <- [n, n-1 .. 1]]



            Due to the fact that lists are linked lists, appending to a list is expensive, while prepending to it is cheap. So if you do decide to construct a list element-by-element, then I'd say you should prepend the new elements, and perhaps after the list is built, use a reverse.



            By the same logic, head is cheap, last is expensive.



            Here's a possible implementation that uses what I've just said.



            pascalList = reverse . pascalList'
            pascalList' 0 = [[1]]
            pascalList' n = new : old
            where new = 1 : pascalCoeff (head old) ++ [1]
            old = pascalList' (n-1)



            In my opinion it is a good idea to



            • separate pure and impure code,

            • keep your code as modular as possible,

            • write type signatures,

            • use a linter like hlint,

            • use functions from the standard library instead of writing explicit recursions.

            Keeping this in mind, here's how I'd write the printing part.



            listToString :: (Show a) => [a] -> String
            listToString = unwords . map show

            leftPadStrings :: [String] -> [String]
            leftPadStrings ss = map leftPad ss
            where maxlen = maximum $ map length ss
            leftPad s = replicate (div (maxlen - length s) 2) ' ' ++ s

            paddedPascalTriangle :: Int -> [String]
            paddedPascalTriangle n = leftPadStrings
            . map listToString
            . take n
            $ pascalTriangle

            printPascalTriangle :: Int -> IO ()
            printPascalTriangle = mapM_ putStrLn . paddedPascalTriangle


            Running this, you should get something like the following:



            *Main> printPascalTriangle 10
            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
            1 8 28 56 70 56 28 8 1
            1 9 36 84 126 126 84 36 9 1






            share|improve this answer















            share|improve this answer



            share|improve this answer








            edited Apr 4 at 11:16


























            answered Apr 3 at 16:09









            Andrew

            491211




            491211











            • You have presented an alternative solution, but haven't reviewed the code. Please explain your reasoning (how your solution works and why it is better than the original) so that the author and other readers can learn from your thought process.
              – Zeta
              Apr 3 at 16:33










            • @Zeta "It is a good idea to separate pure and impure code" was the justification given for the rewrite.
              – 200_success
              Apr 3 at 16:58










            • @Zeta Your comment is justified, because initially I just wanted OP to know about infinite data types, because I think it's a cool concept. Since then I've added some of the more traditional code review.
              – Andrew
              Apr 3 at 21:53











            • If I had have time, I would have used the same concept :). That being said, zipWith func (init xs) (tail xs) is the same as zipWith func xs (tail xs), just as in fibs = 1 : 1 : zibWith (+) fibs (tail fibs).
              – Zeta
              Apr 4 at 4:03











            • @Zeta ah, yes, you are right, thanks. In fact I've used the same property when I rewrote justify. Oh well, I'll edit it in.
              – Andrew
              Apr 4 at 5:49
















            • You have presented an alternative solution, but haven't reviewed the code. Please explain your reasoning (how your solution works and why it is better than the original) so that the author and other readers can learn from your thought process.
              – Zeta
              Apr 3 at 16:33










            • @Zeta "It is a good idea to separate pure and impure code" was the justification given for the rewrite.
              – 200_success
              Apr 3 at 16:58










            • @Zeta Your comment is justified, because initially I just wanted OP to know about infinite data types, because I think it's a cool concept. Since then I've added some of the more traditional code review.
              – Andrew
              Apr 3 at 21:53











            • If I had have time, I would have used the same concept :). That being said, zipWith func (init xs) (tail xs) is the same as zipWith func xs (tail xs), just as in fibs = 1 : 1 : zibWith (+) fibs (tail fibs).
              – Zeta
              Apr 4 at 4:03











            • @Zeta ah, yes, you are right, thanks. In fact I've used the same property when I rewrote justify. Oh well, I'll edit it in.
              – Andrew
              Apr 4 at 5:49















            You have presented an alternative solution, but haven't reviewed the code. Please explain your reasoning (how your solution works and why it is better than the original) so that the author and other readers can learn from your thought process.
            – Zeta
            Apr 3 at 16:33




            You have presented an alternative solution, but haven't reviewed the code. Please explain your reasoning (how your solution works and why it is better than the original) so that the author and other readers can learn from your thought process.
            – Zeta
            Apr 3 at 16:33












            @Zeta "It is a good idea to separate pure and impure code" was the justification given for the rewrite.
            – 200_success
            Apr 3 at 16:58




            @Zeta "It is a good idea to separate pure and impure code" was the justification given for the rewrite.
            – 200_success
            Apr 3 at 16:58












            @Zeta Your comment is justified, because initially I just wanted OP to know about infinite data types, because I think it's a cool concept. Since then I've added some of the more traditional code review.
            – Andrew
            Apr 3 at 21:53





            @Zeta Your comment is justified, because initially I just wanted OP to know about infinite data types, because I think it's a cool concept. Since then I've added some of the more traditional code review.
            – Andrew
            Apr 3 at 21:53













            If I had have time, I would have used the same concept :). That being said, zipWith func (init xs) (tail xs) is the same as zipWith func xs (tail xs), just as in fibs = 1 : 1 : zibWith (+) fibs (tail fibs).
            – Zeta
            Apr 4 at 4:03





            If I had have time, I would have used the same concept :). That being said, zipWith func (init xs) (tail xs) is the same as zipWith func xs (tail xs), just as in fibs = 1 : 1 : zibWith (+) fibs (tail fibs).
            – Zeta
            Apr 4 at 4:03













            @Zeta ah, yes, you are right, thanks. In fact I've used the same property when I rewrote justify. Oh well, I'll edit it in.
            – Andrew
            Apr 4 at 5:49




            @Zeta ah, yes, you are right, thanks. In fact I've used the same property when I rewrote justify. Oh well, I'll edit it in.
            – Andrew
            Apr 4 at 5:49












            up vote
            4
            down vote













            The third case subsumes the second. All possible result lists are prefixes of the same infinite list - let's define that instead. iterate captures this pattern. A combination of zipWith and tail captures pascalCoeff.



            pascalList :: [[Int]]
            pascalList = flip iterate [1] $ pList -> 1 : zipWith (+) pList (tail pList) ++ [1]





            share|improve this answer

























              up vote
              4
              down vote













              The third case subsumes the second. All possible result lists are prefixes of the same infinite list - let's define that instead. iterate captures this pattern. A combination of zipWith and tail captures pascalCoeff.



              pascalList :: [[Int]]
              pascalList = flip iterate [1] $ pList -> 1 : zipWith (+) pList (tail pList) ++ [1]





              share|improve this answer























                up vote
                4
                down vote










                up vote
                4
                down vote









                The third case subsumes the second. All possible result lists are prefixes of the same infinite list - let's define that instead. iterate captures this pattern. A combination of zipWith and tail captures pascalCoeff.



                pascalList :: [[Int]]
                pascalList = flip iterate [1] $ pList -> 1 : zipWith (+) pList (tail pList) ++ [1]





                share|improve this answer













                The third case subsumes the second. All possible result lists are prefixes of the same infinite list - let's define that instead. iterate captures this pattern. A combination of zipWith and tail captures pascalCoeff.



                pascalList :: [[Int]]
                pascalList = flip iterate [1] $ pList -> 1 : zipWith (+) pList (tail pList) ++ [1]






                share|improve this answer













                share|improve this answer



                share|improve this answer











                answered Apr 3 at 20:31









                Gurkenglas

                2,658411




                2,658411






















                     

                    draft saved


                    draft discarded


























                     


                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function ()
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f191159%2fpascal-triangle-in-haskell%23new-answer', 'question_page');

                    );

                    Post as a guest













































































                    Popular posts from this blog

                    Chat program with C++ and SFML

                    Function to Return a JSON Like Objects Using VBA Collections and Arrays

                    Will my employers contract hold up in court?