Add prime-factors exercise (#220)

This commit is contained in:
Erik Schierboom
2025-02-10 16:40:27 +01:00
committed by GitHub
parent d12ce471dd
commit 20e61ccc23
8 changed files with 378 additions and 0 deletions

View File

@@ -557,6 +557,14 @@
"prerequisites": [],
"difficulty": 5
},
{
"slug": "prime-factors",
"name": "Prime Factors",
"uuid": "48187959-1ed3-4327-94e9-aac4013f223c",
"practices": [],
"prerequisites": [],
"difficulty": 5
},
{
"slug": "run-length-encoding",
"name": "Run-Length Encoding",

View File

@@ -0,0 +1,36 @@
# Instructions
Compute the prime factors of a given natural number.
A prime number is only evenly divisible by itself and 1.
Note that 1 is not a prime number.
## Example
What are the prime factors of 60?
- Our first divisor is 2.
2 goes into 60, leaving 30.
- 2 goes into 30, leaving 15.
- 2 doesn't go cleanly into 15.
So let's move on to our next divisor, 3.
- 3 goes cleanly into 15, leaving 5.
- 3 does not go cleanly into 5.
The next possible factor is 4.
- 4 does not go cleanly into 5.
The next possible factor is 5.
- 5 does go cleanly into 5.
- We're left only with 1, so now, we're done.
Our successful divisors in that computation represent the list of prime factors of 60: 2, 2, 3, and 5.
You can check this yourself:
```text
2 * 2 * 3 * 5
= 4 * 15
= 60
```
Success!

View File

@@ -0,0 +1,19 @@
{
"authors": [
"erikschierboom"
],
"files": {
"solution": [
"prime-factors.8th"
],
"test": [
"test.8th"
],
"example": [
".meta/example.8th"
]
},
"blurb": "Compute the prime factors of a given natural number.",
"source": "The Prime Factors Kata by Uncle Bob",
"source_url": "https://web.archive.org/web/20221026171801/http://butunclebob.com/ArticleS.UncleBob.ThePrimeFactorsKata"
}

View File

@@ -0,0 +1,23 @@
: increment-factor \ n n -- n n
repeat
2dup n:mod !if ;then
dup 2 n:= !if n:1+ then n:1+
again
;
: factors \ n -- a
a:new >r 2
repeat
over 1 n:>
!if
2drop break
else
increment-factor
swap over n:/ swap
dup r> a:push >r
then
again
r>
;

View File

@@ -0,0 +1,46 @@
# This is an auto-generated file.
#
# Regenerating this file via `configlet sync` will:
# - Recreate every `description` key/value pair
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
# - Preserve any other key/value pair
#
# As user-added comments (using the # character) will be removed when this file
# is regenerated, comments can be added via a `comment` key.
[924fc966-a8f5-4288-82f2-6b9224819ccd]
description = "no factors"
[17e30670-b105-4305-af53-ddde182cb6ad]
description = "prime number"
[238d57c8-4c12-42ef-af34-ae4929f94789]
description = "another prime number"
[f59b8350-a180-495a-8fb1-1712fbee1158]
description = "square of a prime"
[756949d3-3158-4e3d-91f2-c4f9f043ee70]
description = "product of first prime"
[bc8c113f-9580-4516-8669-c5fc29512ceb]
description = "cube of a prime"
[7d6a3300-a4cb-4065-bd33-0ced1de6cb44]
description = "product of second prime"
[073ac0b2-c915-4362-929d-fc45f7b9a9e4]
description = "product of third prime"
[6e0e4912-7fb6-47f3-a9ad-dbcd79340c75]
description = "product of first and second prime"
[00485cd3-a3fe-4fbe-a64a-a4308fc1f870]
description = "product of primes and non-primes"
[02251d54-3ca1-4a9b-85e1-b38f4b0ccb91]
description = "product of primes"
[070cf8dc-e202-4285-aa37-8d775c9cd473]
description = "factors include a large prime"

View File

@@ -0,0 +1,174 @@
needs console/loaded
needs utils/latebind
\ -----------------------------------------------------------------
ns: test
-1 var, test-count
var tests-passed
var tests-failed
var tests-skipped
true var, run-test
\ Some utility words
: test-passed \ s x x -- \\ test name, expected value, actual value
2drop
1 tests-passed n:+!
con:green con:onBlack . space " ... OK" . con:white con:onBlack cr
;
: test-skipped \ s --
1 tests-skipped n:+!
con:cyan con:onBlack . space " ... SKIPPED" . con:white con:onBlack cr
;
: test-failed \ s x x -- \\ test name, expected value, actual value
1 tests-failed n:+!
rot
con:red con:onBlack . space " ... FAIL" . con:white con:onBlack cr
" Actual: «" . . "»" . cr
" Expected: «" . . "»" . cr cr
;
: isword? \ x -- x f
dup >kind ns:w n:=
;
: run-test? \ -- T
run-test @ if true else "RUN_ALL_TESTS" getenv n:>bool then
;
\ Num passed + num skipped + num failed should == num tests
: all-tests-run? \ -- T
tests-passed @ tests-skipped @ tests-failed @ n:+ n:+
test-count @ n:=
;
\ returns true if x is a date, false otherwise
: date? \ x -- x T
dup >kind ns:d n:=
;
\ adapted from 8th forum -- https://8th-dev.com/forum/index.php/topic,2745.0.html
: eq? \ x x -- T
\ are the items the same kind?
2dup >kind swap >kind n:=
!if 2drop false ;then
\ same kind: try different comparators
number? if n:= ;then
string? if s:= ;then
array? if ' eq? a:= 2nip ;then
map? if ' eq? m:= 2nip ;then
date? if d:= ;then
\ otherwise fall back to 'lazy evaluation'
G:l: =
;
: eps_eq? \ n x x -- T
\ are the items the same kind?
2dup >kind swap >kind n:=
!if 2drop false ;then
number? !if 2drop false ;then
rot n:~=
;
: check-depth \ ... n -- ...
dup>r
n:1+ depth n:=
!if
con:red con:onBlack
"PANIC: expected stack depth to be " . r> . cr
"Stack is:" . cr
.s cr
255 die
then
rdrop
;
\ -----------------------------------------------------------------
\ status report at end of run
( all-tests-run?
!if con:red con:onBlack "... FAIL - not all tests completed" . con:white con:onBlack cr then
) onexit
\ Print a summary of the tests run
( con:white con:onBlack
test-count @ . space "tests planned - " .
tests-passed @ . space "passed - " .
tests-skipped @ . space "skipped - " .
tests-failed @ . space "failed" . cr
) onexit
\ -----------------------------------------------------------------
\ The public-facing words
\ -----------------------------------------------------------------
: equal? \ s x w -- | s w x --
run-test? !if 2drop test-skipped ;; then
isword? !if swap then
w:exec
3 check-depth
2dup \ so test-failed can show actual and expected
eq? if test-passed else test-failed then
;
: approx_equal? \ s x w n -- | s w x n --
run-test? !if 3drop test-skipped ;; then
-rot isword? !if swap then
w:exec
4 check-depth
3dup \ so test-failed can show actual and expected
eps_eq?
if rot drop test-passed else rot drop test-failed then
;
: true? \ s w --
run-test? !if drop test-skipped ;; then
w:exec
2 check-depth
true swap dup \ so test-failed can show actual and expected
if test-passed else test-failed then
;
: false? \ s w --
run-test? !if drop test-skipped ;; then
w:exec
2 check-depth
false swap dup \ so test-failed can show actual and expected
!if test-passed else test-failed then
;
: null? \ s w --
run-test? !if drop test-skipped ;; then
w:exec
2 check-depth
null swap dup \ so test-failed can show actual and expected
G:null? nip if test-passed else test-failed then
;
: SKIP-REST-OF-TESTS false run-test ! ;
: tests \ n --
test-count !
;
\ Set the exit status:
\ 0 = all OK
\ 1 = not all tests were run (some error occurred)
\ 2 = some tests failed
: end-of-tests \ --
all-tests-run?
if
tests-failed @ 0 n:= if 0 else 2 then
else
1
then
die
;

View File

@@ -0,0 +1,3 @@
: factors \ n -- a
;

View File

@@ -0,0 +1,69 @@
"prime-factors.8th" f:include
needs exercism/test
with: test
12 tests
"No factors"
( 1 factors )
[]
equal?
SKIP-REST-OF-TESTS
"Prime number"
( 2 factors )
[2]
equal?
"Another prime number"
( 3 factors )
[3]
equal?
"Square of a prime"
( 9 factors )
[3, 3]
equal?
"Product of first prime"
( 4 factors )
[2, 2]
equal?
"Cube of a prime"
( 8 factors )
[2, 2, 2]
equal?
"Product of second prime"
( 27 factors )
[3, 3, 3]
equal?
"Product of third prime"
( 625 factors )
[5, 5, 5, 5]
equal?
"Product of first and second prime"
( 6 factors )
[2, 3]
equal?
"Product of primes and non-primes"
( 12 factors )
[2, 2, 3]
equal?
"Product of primes"
( 901255 factors )
[5, 17, 23, 461]
equal?
"Factors include a large prime"
( 93819012551 factors )
[11, 9539, 894119]
equal?
end-of-tests
;with