Skip to content

Commit

Permalink
Improve naming, tests, and solutions of factorization-related exercis…
Browse files Browse the repository at this point in the history
…es and examples (#387)

* Ch4 rename factorize to primeFactors, improve solution and tests

* Ch4 improve factors tests

* Ch4 rename fact to factorial

* Ch4 rename factTailRec to factorialTailRec

* Ch4 test more primeFactors corner cases
  • Loading branch information
milesfrain authored Sep 6, 2021
1 parent b967cae commit 12b82aa
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 51 deletions.
22 changes: 11 additions & 11 deletions exercises/chapter4/test/Examples.purs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import Data.Foldable (product)
import Data.Maybe (fromMaybe)
import Data.Path (Path, ls)

-- ANCHOR: fact
fact :: Int -> Int
fact n =
-- ANCHOR: factorial
factorial :: Int -> Int
factorial n =
if n == 0 then
1
else
n * fact (n - 1)
-- ANCHOR_END: fact
n * factorial (n - 1)
-- ANCHOR_END: factorial

-- ANCHOR: fib
fib :: Int -> Int
Expand Down Expand Up @@ -61,21 +61,21 @@ factorsV3 n = do
pure [ i, j ]
-- ANCHOR_END: factorsV3

-- ANCHOR: factTailRec
factTailRec :: Int -> Int -> Int
factTailRec n acc =
-- ANCHOR: factorialTailRec
factorialTailRec :: Int -> Int -> Int
factorialTailRec n acc =
if n == 0
then acc
else factTailRec (n - 1) (acc * n)
-- ANCHOR_END: factTailRec
else factorialTailRec (n - 1) (acc * n)
-- ANCHOR_END: factorialTailRec

-- ANCHOR: lengthTailRec
lengthTailRec :: forall a. Array a -> Int
lengthTailRec arr = length' arr 0
where
length' :: Array a -> Int -> Int
length' arr' acc =
if null arr'
if null arr'
then acc
else length' (fromMaybe [] $ tail arr') (acc + 1)
-- ANCHOR_END: lengthTailRec
Expand Down
59 changes: 38 additions & 21 deletions exercises/chapter4/test/Main.purs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import Test.Examples
import Test.MySolutions
import Test.NoPeeking.Solutions -- This line should have been automatically deleted by resetSolutions.sh. See Chapter 2 for instructions.
import Data.Array (sort)
import Data.Foldable (sequence_)
import Data.Maybe (Maybe(..))
import Data.Path (Path(..), filename, root)
import Data.Tuple (fst)
import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Test.Unit (TestSuite, suite, test)
import Test.Unit.Assert (assert, assertFalse)
Expand Down Expand Up @@ -122,13 +123,21 @@ This line should have been automatically deleted by resetSolutions.sh. See Chapt
Assert.equal (sort [ [ 3, 4, 5 ], [ 5, 12, 13 ], [ 6, 8, 10 ] ])
$ sort
$ triples 13
suite "Exercise - factorize" do
test "Test small non-prime number" do
Assert.equal [ 3, 2 ]
$ factorize 6
test "Test number that uses the prime numbers less than 10" do
Assert.equal [ 7, 5, 3, 2 ]
$ factorize 210
suite "Exercise - primeFactors" do
let
primeFactorsTest :: Int -> Array Int -> _
primeFactorsTest n xs =
test (show n) do
Assert.equal (sort xs)
$ sort
$ primeFactors n
primeFactorsTest 1 []
primeFactorsTest 2 [2]
primeFactorsTest 3 [3]
primeFactorsTest 4 [2, 2]
primeFactorsTest 6 [3, 2]
primeFactorsTest 18 [3, 3, 2]
primeFactorsTest 210 [ 7, 5, 3, 2 ]
suite "Exercise Group - Folds and Tail Recursion" do
test "Exercise - allTrue" do
assert "all elements true"
Expand Down Expand Up @@ -198,27 +207,35 @@ This line should have been automatically deleted by resetSolutions.sh. See Chapt
runChapterExamples :: TestSuite
runChapterExamples =
suite "Chapter Examples" do
test "fact" do
test "factorial" do
Assert.equal 120
$ fact 5
$ factorial 5
test "fib" do
Assert.equal 34
$ fib 9
test "length" do
Assert.equal 3
$ length [ 0, 0, 0 ]
test "factors" do
Assert.equal [ [ 1, 10 ], [ 2, 5 ] ]
$ factors 10
test "factorsV2" do
Assert.equal [ [ 1, 10 ], [ 2, 5 ] ]
$ factorsV2 10
test "factorsV3" do
Assert.equal [ [ 1, 10 ], [ 2, 5 ] ]
$ factorsV3 10
test "factTailRec" do
sequence_ do
name /\ f <-
[ "factors" /\ factors
, "factorsV2" /\ factorsV2
, "factorsV3" /\ factorsV3
]
n /\ xs <-
[ 1 /\ [[1,1]]
, 2 /\ [[1,2]]
, 3 /\ [[1,3]]
, 4 /\ [[1,4],[2,2]]
, 10 /\ [[1,10],[2,5]]
, 100 /\ [[1,100],[2,50],[4,25],[5,20],[10,10]]
]
pure $ test (name <> " " <> show n) do
Assert.equal (sort $ map sort xs)
$ sort $ map sort f n
test "factorialTailRec" do
Assert.equal 120
$ factTailRec 5 1
$ factorialTailRec 5 1
test "lengthTailRec" do
Assert.equal 3
$ lengthTailRec [ 0, 0, 0 ]
Expand Down
22 changes: 9 additions & 13 deletions exercises/chapter4/test/no-peeking/Solutions.purs
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,16 @@ triples n = do
pure [ i, j, k ]

-- | Provide the prime numbers that, multiplied together, make the argument.
factorize :: Int -> Array Int
factorize n = factorize' 2 n []
primeFactors :: Int -> Array Int
primeFactors n = factorize 2 n
where
factorize' :: Int -> Int -> Array Int -> Array Int
factorize' _ 1 result = result

factorize' divisor dividend result =
let
remainder = rem dividend divisor
in
if remainder == 0 then
factorize' (divisor) (quot dividend divisor) (cons divisor result)
else
factorize' (divisor + 1) dividend result
factorize :: Int -> Int -> Array Int
factorize _ 1 = []
factorize divisor dividend =
if dividend `mod` divisor == 0 then
cons divisor $ factorize (divisor) (dividend / divisor)
else
factorize (divisor + 1) dividend

allTrue :: Array Boolean -> Boolean
allTrue bools = foldl (\acc bool -> acc && bool) true bools
Expand Down
12 changes: 6 additions & 6 deletions text/chapter4.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Let's see some simple examples of recursion in PureScript.
Here is the usual _factorial function_ example:

```haskell
{{#include ../exercises/chapter4/test/Examples.purs:fact}}
{{#include ../exercises/chapter4/test/Examples.purs:factorial}}
```

Here, we can see how the factorial function is computed by reducing the problem to a subproblem - that of computing the factorial of a smaller integer. When we reach zero, the answer is immediate.
Expand All @@ -44,7 +44,7 @@ Here is another common example, which computes the _Fibonacci function_:

Again, this problem is solved by considering the solutions to subproblems. In this case, there are two subproblems, corresponding to the expressions `fib (n - 1)` and `fib (n - 2)`. When these two subproblems are solved, we assemble the result by adding the partial results.

Note that, while the above examples of `fact` and `fib` work as intended, a more idiomatic implementation would use pattern matching instead of `if`/`then`/`else`. Pattern matching techniques are discussed in a later chapter.
Note that, while the above examples of `factorial` and `fib` work as intended, a more idiomatic implementation would use pattern matching instead of `if`/`then`/`else`. Pattern matching techniques are discussed in a later chapter.

## Recursion on Arrays

Expand Down Expand Up @@ -365,7 +365,7 @@ This means that if the guard fails, then the current branch of the array compreh
1. (Easy) Write a function `isPrime` which tests if its integer argument is prime or not. _Hint_: Use the `factors` function.
1. (Medium) Write a function `cartesianProduct` which uses do notation to find the _cartesian product_ of two arrays, i.e. the set of all pairs of elements `a`, `b`, where `a` is an element of the first array, and `b` is an element of the second.
1. (Medium) Write a function `triples :: Int -> Array (Array Int)` which takes a number `n` and returns all Pythagorean triples whose components (the `a`, `b` and `c` values) are each less than or equal to `n`. A _Pythagorean triple_ is an array of numbers `[a, b, c]` such that `a² + b² = c²`. _Hint_: Use the `guard` function in an array comprehension.
1. (Difficult) Write a function `factorize` which produces the [prime factorization](https://www.mathsisfun.com/prime-factorization.html) of `n`, i.e. the array of prime integers whose product is `n`. _Hint_: for an integer greater than 1, break the problem down into two subproblems: finding the first factor, and finding the remaining factors.
1. (Difficult) Write a function `primeFactors` which produces the [prime factorization](https://www.mathsisfun.com/prime-factorization.html) of `n`, i.e. the array of prime integers whose product is `n`. _Hint_: for an integer greater than 1, break the problem down into two subproblems: finding the first factor, and finding the remaining factors.

## Folds

Expand Down Expand Up @@ -441,7 +441,7 @@ It is easy to verify this problem, with the following code in PSCi:

```text
> :paste
… f n =
… f n =
… if n == 0
… then 0
… else 1 + f (n - 1)
Expand All @@ -467,10 +467,10 @@ In practice, the PureScript compiler does not replace the recursive call with a
Here is an example of a recursive function with all recursive calls in tail position:

```haskell
{{#include ../exercises/chapter4/test/Examples.purs:factTailRec}}
{{#include ../exercises/chapter4/test/Examples.purs:factorialTailRec}}
```

Notice that the recursive call to `factTailRec` is the last thing that happens in this function - it is in tail position.
Notice that the recursive call to `factorialTailRec` is the last thing that happens in this function - it is in tail position.

## Accumulators

Expand Down

0 comments on commit 12b82aa

Please sign in to comment.