Pascal Triangle in Haskell
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
3
down vote
favorite
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
haskell
add a comment |Â
up vote
3
down vote
favorite
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
haskell
add a comment |Â
up vote
3
down vote
favorite
up vote
3
down vote
favorite
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
haskell
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
haskell
edited Apr 3 at 16:57
200_success
123k14142399
123k14142399
asked Apr 3 at 13:06
progrookie
362
362
add a comment |Â
add a comment |Â
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
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 aszipWith func xs (tail xs)
, just as infibs = 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 rewrotejustify
. Oh well, I'll edit it in.
â Andrew
Apr 4 at 5:49
add a comment |Â
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]
add a comment |Â
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
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 aszipWith func xs (tail xs)
, just as infibs = 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 rewrotejustify
. Oh well, I'll edit it in.
â Andrew
Apr 4 at 5:49
add a comment |Â
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
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 aszipWith func xs (tail xs)
, just as infibs = 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 rewrotejustify
. Oh well, I'll edit it in.
â Andrew
Apr 4 at 5:49
add a comment |Â
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
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
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 aszipWith func xs (tail xs)
, just as infibs = 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 rewrotejustify
. Oh well, I'll edit it in.
â Andrew
Apr 4 at 5:49
add a comment |Â
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 aszipWith func xs (tail xs)
, just as infibs = 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 rewrotejustify
. 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
add a comment |Â
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]
add a comment |Â
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]
add a comment |Â
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]
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]
answered Apr 3 at 20:31
Gurkenglas
2,658411
2,658411
add a comment |Â
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
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
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password