diff --git a/common-lisp/roman-numerals/README.md b/common-lisp/roman-numerals/README.md new file mode 100644 index 0000000000000000000000000000000000000000..4ce9b78f1a116ca12090c8f4fedc6cc3f215bc83 --- /dev/null +++ b/common-lisp/roman-numerals/README.md @@ -0,0 +1,97 @@ +# Roman Numerals + +Write a function to convert from normal numbers to Roman Numerals. + +The Romans were a clever bunch. They conquered most of Europe and ruled +it for hundreds of years. They invented concrete and straight roads and +even bikinis. One thing they never discovered though was the number +zero. This made writing and dating extensive histories of their exploits +slightly more challenging, but the system of numbers they came up with +is still in use today. For example the BBC uses Roman numerals to date +their programmes. + +The Romans wrote numbers using letters - I, V, X, L, C, D, M. (notice +these letters have lots of straight lines and are hence easy to hack +into stone tablets). + +```text + 1 => I +10 => X + 7 => VII +``` + +There is no need to be able to convert numbers larger than about 3000. +(The Romans themselves didn't tend to go any higher) + +Wikipedia says: Modern Roman numerals ... are written by expressing each +digit separately starting with the left most digit and skipping any +digit with a value of zero. + +To see this in practice, consider the example of 1990. + +In Roman numerals 1990 is MCMXC: + +1000=M +900=CM +90=XC + +2008 is written as MMVIII: + +2000=MM +8=VIII + +See also: http://www.novaroma.org/via_romana/numbers.html + +## Setup + +Check out [Installing Common +Lisp](https://exercism.io/tracks/common-lisp/installation) for +instructions to get started or take a look at the guides available in +the [track's side bar](https://exercism.io/my/tracks/common-lisp). + +## Formatting + +While Common Lisp doesn't care about indentation and layout of code, +nor whether you use spaces or tabs, this is an important consideration +for submissions to exercism.io. Excercism.io's code widget cannot +handle mixing of tab and space characters well so using only spaces is recommended to make +the code more readable to the human reviewers. Please review your +editors settings on how to accomplish this. Below are instructions for +popular editors for Common Lisp. + +### VIM + +Use the following commands to ensure VIM uses only spaces for +indentation: + +```vimscript +:set tabstop=2 +:set shiftwidth=2 +:set expandtab +``` + +(or as a oneliner `:set tabstop=2 shiftwidth=2 expandtab`). This can +be added to your `~/.vimrc` file to use it all the time. + +### Emacs + +Emacs is very well suited for editing Common Lisp and has many +powerful add-on packages available. The only thing that one needs to +do with a stock emacs to make it work well with exercism.io is to +evaluate the following code: + +`(setq-default indent-tabs-mode nil)` + +This can be placed in your `~/.emacs` (or `~/.emacs.d/init.el`) in +order to have it set whenever Emacs is launched. + +One suggested add-on for Emacs and Common Lisp is +[SLIME](https://github.com/slime/slime) which offers tight integration +with the REPL; making iterative coding and testing very easy. + +## Source + +The Roman Numeral Kata [http://codingdojo.org/cgi-bin/index.pl?KataRomanNumerals](http://codingdojo.org/cgi-bin/index.pl?KataRomanNumerals) + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/common-lisp/roman-numerals/roman-numerals-test.lisp b/common-lisp/roman-numerals/roman-numerals-test.lisp new file mode 100644 index 0000000000000000000000000000000000000000..bb2c0dcdd8c135fcac57e74a5dbc836f6cebcdf8 --- /dev/null +++ b/common-lisp/roman-numerals/roman-numerals-test.lisp @@ -0,0 +1,66 @@ +(ql:quickload "lisp-unit") +#-xlisp-test (load "roman-numerals") + +(defpackage #:roman-test + (:use #:cl #:lisp-unit)) + +(in-package #:roman-test) + +(define-test test-1 + (assert-equal "I" (roman:romanize 1))) + +(define-test test-2 + (assert-equal "II" (roman:romanize 2))) + +(define-test test-3 + (assert-equal "III" (roman:romanize 3))) + +(define-test test-4 + (assert-equal "IV" (roman:romanize 4))) + +(define-test test-5 + (assert-equal "V" (roman:romanize 5))) + +(define-test test-6 + (assert-equal "VI" (roman:romanize 6))) + +(define-test test-9 + (assert-equal "IX" (roman:romanize 9))) + +(define-test test-27 + (assert-equal "XXVII" (roman:romanize 27))) + +(define-test test-48 + (assert-equal "XLVIII" (roman:romanize 48))) + +(define-test test-59 + (assert-equal "LIX" (roman:romanize 59))) + +(define-test test-93 + (assert-equal "XCIII" (roman:romanize 93))) + +(define-test test-141 + (assert-equal "CXLI" (roman:romanize 141))) + +(define-test test-163 + (assert-equal "CLXIII" (roman:romanize 163))) + +(define-test test-402 + (assert-equal "CDII" (roman:romanize 402))) + +(define-test test-575 + (assert-equal "DLXXV" (roman:romanize 575))) + +(define-test test-911 + (assert-equal "CMXI" (roman:romanize 911))) + +(define-test test-1024 + (assert-equal "MXXIV" (roman:romanize 1024))) + +(define-test test-3000 + (assert-equal "MMM" (roman:romanize 3000))) + +#-xlisp-test +(let ((*print-errors* t) + (*print-failures* t)) + (run-tests :all :roman-test)) diff --git a/common-lisp/roman-numerals/roman-numerals.lisp b/common-lisp/roman-numerals/roman-numerals.lisp new file mode 100644 index 0000000000000000000000000000000000000000..de6645f3e393334d82cb19eeea9acda6ebfd911b --- /dev/null +++ b/common-lisp/roman-numerals/roman-numerals.lisp @@ -0,0 +1,8 @@ +(defpackage #:roman + (:use #:cl) + (:export #:romanize)) + +(in-package #:roman) + +(defun romanize (num) + (format nil "~@r" num)) diff --git a/common-lisp/word-count/#word-count.lisp# b/common-lisp/word-count/#word-count.lisp# new file mode 100644 index 0000000000000000000000000000000000000000..5f2a0d3ec1e2d45bbf5063cbcb83552cc15414b4 --- /dev/null +++ b/common-lisp/word-count/#word-count.lisp# @@ -0,0 +1,17 @@ +(in-package #:cl-user) +(defpackage #:word-count + (:use #:cl) + (:export #:count-words)) +(in-package #:word-count) + +(defun count-words (sentence) + (loop + :with res-list = '() + :for x :in (uiop:split-string sentence :separator '(#\Space #\, #\Newline #\Return )) + :do (if (assoc (string-trim "'." x) res-list :test #'string-equal) + (incf (cdr (assoc (string-trim "'!@#$%^&*()_+:;{}|,.-=`~<>?/" x) + res-list :test #'string-equal))) + (if (/= 0 (length (string-trim "'!@#$%^&*()_+:;{}|,.-=`~<>?/" x))) + (setq res-list (acons (string-trim "'!@#$%^&*()_+:;{}|,.-=`~<>?/" + (string-downcase x)) 1 res-list)))) + :finally (return res-list))) diff --git a/common-lisp/word-count/.#word-count.lisp b/common-lisp/word-count/.#word-count.lisp new file mode 120000 index 0000000000000000000000000000000000000000..195cb7d4845cf36062c5b5e2e561e5b78e501012 --- /dev/null +++ b/common-lisp/word-count/.#word-count.lisp @@ -0,0 +1 @@ +bradleysmall@penguin.9875:1582430321 \ No newline at end of file diff --git a/common-lisp/word-count/README.md b/common-lisp/word-count/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c3391b0ae4fcf9d32149442346988a4662ae9645 --- /dev/null +++ b/common-lisp/word-count/README.md @@ -0,0 +1,85 @@ +# Word Count + +Given a phrase, count the occurrences of each _word_ in that phrase. + +For the purposes of this exercise you can expect that a _word_ will always be one of: + +1. A _number_ composed of one or more ASCII digits (ie "0" or "1234") OR +2. A _simple word_ composed of one or more ASCII letters (ie "a" or "they") OR +3. A _contraction_ of two _simple words_ joined by a single apostrophe (ie "it's" or "they're") + +When counting words you can assume the following rules: + +1. The count is _case insensitive_ (ie "You", "you", and "YOU" are 3 uses of the same word) +2. The count is _unordered_; the tests will ignore how words and counts are ordered +3. Other than the apostrophe in a _contraction_ all forms of _punctuation_ are ignored +4. The words can be separated by _any_ form of whitespace (ie "\t", "\n", " ") + +For example, for the phrase `"That's the password: 'PASSWORD 123'!", cried the Special Agent.\nSo I fled.` the count would be: + +```text +that's: 1 +the: 2 +password: 2 +123: 1 +cried: 1 +special: 1 +agent: 1 +so: 1 +i: 1 +fled: 1 +``` + +## Setup + +Check out [Installing Common +Lisp](https://exercism.io/tracks/common-lisp/installation) for +instructions to get started or take a look at the guides available in +the [track's side bar](https://exercism.io/my/tracks/common-lisp). + +## Formatting + +While Common Lisp doesn't care about indentation and layout of code, +nor whether you use spaces or tabs, this is an important consideration +for submissions to exercism.io. Excercism.io's code widget cannot +handle mixing of tab and space characters well so using only spaces is recommended to make +the code more readable to the human reviewers. Please review your +editors settings on how to accomplish this. Below are instructions for +popular editors for Common Lisp. + +### VIM + +Use the following commands to ensure VIM uses only spaces for +indentation: + +```vimscript +:set tabstop=2 +:set shiftwidth=2 +:set expandtab +``` + +(or as a oneliner `:set tabstop=2 shiftwidth=2 expandtab`). This can +be added to your `~/.vimrc` file to use it all the time. + +### Emacs + +Emacs is very well suited for editing Common Lisp and has many +powerful add-on packages available. The only thing that one needs to +do with a stock emacs to make it work well with exercism.io is to +evaluate the following code: + +`(setq-default indent-tabs-mode nil)` + +This can be placed in your `~/.emacs` (or `~/.emacs.d/init.el`) in +order to have it set whenever Emacs is launched. + +One suggested add-on for Emacs and Common Lisp is +[SLIME](https://github.com/slime/slime) which offers tight integration +with the REPL; making iterative coding and testing very easy. + +## Source + +This is a classic toy problem, but we were reminded of it by seeing it in the Go Tour. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/common-lisp/word-count/word-count-test.lisp b/common-lisp/word-count/word-count-test.lisp new file mode 100644 index 0000000000000000000000000000000000000000..fc334f68e3292251d0f0725824b9adb97ac93f33 --- /dev/null +++ b/common-lisp/word-count/word-count-test.lisp @@ -0,0 +1,122 @@ +;;; +;;; word-count v1.4.0 +;;; +;;; For each word in the input, count the number of times it appears in the +;;; entire sentence. +;;; +(ql:quickload "lisp-unit") +#-xlisp-test (load "word-count") + +(defpackage #:word-count-test + (:use #:common-lisp #:lisp-unit)) +(in-package #:word-count-test) + +(defun assert-alist-equal (expected actual) + "The association lists must have the same length and the keys and + values of the items must match. But the order is not + important. Equality is tested with equal" + (assert-equal + (sort (copy-seq expected) #'string< :key #'car) + (sort (copy-seq actual) #'string< :key #'car))) + +(define-test + count-one-word + (assert-alist-equal + '(("word" . 1)) + (word-count:count-words "word"))) + + +(define-test + count-one-of-each-word + (assert-alist-equal + '(("one" . 1) ("of" . 1) ("each" . 1)) + (word-count:count-words "one of each"))) + + +(define-test + multiple-occurrences-of-a-word + (assert-alist-equal + '(("one" . 1) ("fish" . 4) ("two" . 1) ("red" . 1) ("blue" . 1)) + (word-count:count-words "one fish two fish red fish blue fish"))) + + +(define-test + handles-cramped-lists + (assert-alist-equal + '(("one" . 1) ("two" . 1) ("three" . 1)) + (word-count:count-words "one,two,three"))) + + +(define-test + handles-expanded-lists + (assert-alist-equal + '(("one" . 1) ("two" . 1) ("three" . 1)) + (word-count:count-words "one, +two, +three"))) + + +(define-test + ignore-punctuation + (assert-alist-equal + '(("car" . 1) ("carpet" . 1) ("as" . 1) ("java" . 1) ("javascript" . 1)) + (word-count:count-words "car: carpet as java: javascript!!&@$%^&"))) + + +(define-test + include-numbers + (assert-alist-equal + '(("testing" . 2) ("1" . 1) ("2" . 1)) + (word-count:count-words "testing, 1, 2 testing"))) + + +(define-test + normalize-case + (assert-alist-equal + '(("go" . 3) ("stop" . 2)) + (word-count:count-words "go Go GO Stop stop"))) + + +(define-test + with-apostrophes + (assert-alist-equal + '(("first" . 1) ("don't" . 2) ("laugh" . 1) ("then" . 1) ("cry" . 1)) + (word-count:count-words "First: don't laugh. Then: don't cry."))) + + +(define-test + with-quotations + (assert-alist-equal + '(("joe" . 1) ("can't" . 1) ("tell" . 1) ("between" . 1) ("large" . 2) + ("and" . 1)) + (word-count:count-words "Joe can't tell between 'large' and large."))) + + +(define-test + substrings-from-the-beginning + (assert-alist-equal + '(("joe" . 1) ("can't" . 1) ("tell" . 1) ("between" . 1) ("app" . 1) + ("apple" . 1) ("and" . 1) ("a" . 1)) + (word-count:count-words "Joe can't tell between app, apple and a."))) + + +(define-test + multiple-spaces-not-detected-as-a-word + (assert-alist-equal + '(("multiple" . 1) ("whitespaces" . 1)) + (word-count:count-words " multiple whitespaces"))) + + +(define-test + alternating-word-separators-not-detected-as-a-word + (assert-alist-equal + '(("one" . 1) ("two" . 1) ("three" . 1)) + (word-count:count-words ", +,one, + ,two + 'three'"))) + +#-xlisp-test +(let ((*print-errors* t) + (*print-failures* t)) + (run-tests :all)) diff --git a/common-lisp/word-count/word-count.lisp b/common-lisp/word-count/word-count.lisp new file mode 100644 index 0000000000000000000000000000000000000000..43773133aba6a2c09aff6109bdb56817714dfb1a --- /dev/null +++ b/common-lisp/word-count/word-count.lisp @@ -0,0 +1,14 @@ +(in-package #:cl-user) +(defpackage #:word-count + (:use #:cl) + (:export #:count-words)) +(in-package #:word-count) + +(defun count-words (sentence) + (loop + :with res-list = '() + :for x :in (uiop:split-string sentence :separator '(#\Space #\, #\Newline #\Return )) + :do (if (assoc (string-trim "'." x) res-list :test #'string-equal) + (incf (cdr (assoc (string-trim "'!@#$%^&*()_+:;{}|,.-=`~<>?/" x) res-list :test #'string-equal))) + (if (/= 0 (length (string-trim "'!@#$%^&*()_+:;{}|,.-=`~<>?/" x))) (setq res-list (acons (string-trim "'!@#$%^&*()_+:;{}|,.-=`~<>?/" (string-downcase x)) 1 res-list)))) + :finally (return res-list)))