diff --git a/all-your-base/.exercism/config.json b/all-your-base/.exercism/config.json new file mode 100644 index 0000000..1a73a06 --- /dev/null +++ b/all-your-base/.exercism/config.json @@ -0,0 +1,17 @@ +{ + "authors": [ + "serialhex" + ], + "files": { + "solution": [ + "all-your-base.lisp" + ], + "test": [ + "all-your-base-test.lisp" + ], + "example": [ + ".meta/example.lisp" + ] + }, + "blurb": "Convert a number, represented as a sequence of digits in one base, to any other base." +} diff --git a/all-your-base/.exercism/metadata.json b/all-your-base/.exercism/metadata.json new file mode 100644 index 0000000..b9580d5 --- /dev/null +++ b/all-your-base/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"common-lisp","exercise":"all-your-base","id":"1e82af39c5db432cafceb16825541ea0","url":"https://exercism.org/tracks/common-lisp/exercises/all-your-base","handle":"amcooper","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/all-your-base/HELP.md b/all-your-base/HELP.md new file mode 100644 index 0000000..a17c339 --- /dev/null +++ b/all-your-base/HELP.md @@ -0,0 +1,104 @@ +# Help + +## Running the tests + +## Testing interactively + +Start the REPL from the directory that you downloaded the exercise to. + +You can run the tests by loading the test file into the REPL with `(load "exercise-test")` (replacing "exercise" with appropriate name). Then evaluate `(exercise-test:run-tests)` to run all the tests. + +If you write your code directly in the REPL then simply evaluate `(exercise-test:run-tests)`. + +If you write your code in the exercise lisp file then load it with `(load "exercise")` then evaluate `(exercise-test:run-tests)`. + +## Testing from the command line + +You can launch the tests with this command line invocation (again, replace "exercise" with the appropriate name in two places) + +```sh +ros run --load exercise-test.lisp --eval '(uiop:quit (if (exercise-test:run-tests) 0 1))' +``` + +## Submitting your solution + +You can submit your solution using the `exercism submit all-your-base.lisp` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Common Lisp track's documentation](https://exercism.org/docs/tracks/common-lisp) +- The [Common Lisp track's programming category on the forum](https://forum.exercism.org/c/programming/common-lisp) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Setup + +Check out [Installing Common +Lisp][track-install] for +instructions to get started or take a look at the guides available in +the [track's documentation pages][track-docs]. + +## Where to look for help + +Any of the resources listed in the list of [Useful Common Lisp Resources][track-resources] are good places to look for information. + +There are also some [Online Communities][awesome-cl-communities] which may be good places to go for help. + +## 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. +Exercism.org'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.org 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][slime] which offers tight integration +with the REPL; making iterative coding and testing very easy. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can ask for mentoring to help you come to the correct solution. + +[awesome-cl-communities]: https://github.com/GustavBertram/awesome-common-lisp-learning#online-community +[slime]: https://github.com/slime/slime +[track-docs]: /docs/tracks/common-lisp +[track-install]: /docs/tracks/common-lisp/installation +[track-resources]: /docs/tracks/common-lisp/resources \ No newline at end of file diff --git a/all-your-base/README.md b/all-your-base/README.md new file mode 100644 index 0000000..90ec4d6 --- /dev/null +++ b/all-your-base/README.md @@ -0,0 +1,48 @@ +# All Your Base + +Welcome to All Your Base on Exercism's Common Lisp Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Introduction + +You've just been hired as professor of mathematics. +Your first week went well, but something is off in your second week. +The problem is that every answer given by your students is wrong! +Luckily, your math skills have allowed you to identify the problem: the student answers _are_ correct, but they're all in base 2 (binary)! +Amazingly, it turns out that each week, the students use a different base. +To help you quickly verify the student answers, you'll be building a tool to translate between bases. + +## Instructions + +Convert a sequence of digits in one base, representing a number, into a sequence of digits in another base, representing the same number. + +~~~~exercism/note +Try to implement the conversion yourself. +Do not use something else to perform the conversion for you. +~~~~ + +## About [Positional Notation][positional-notation] + +In positional notation, a number in base **b** can be understood as a linear combination of powers of **b**. + +The number 42, _in base 10_, means: + +`(4 × 10¹) + (2 × 10⁰)` + +The number 101010, _in base 2_, means: + +`(1 × 2⁵) + (0 × 2⁴) + (1 × 2³) + (0 × 2²) + (1 × 2¹) + (0 × 2⁰)` + +The number 1120, _in base 3_, means: + +`(1 × 3³) + (1 × 3²) + (2 × 3¹) + (0 × 3⁰)` + +_Yes. Those three numbers above are exactly the same. Congratulations!_ + +[positional-notation]: https://en.wikipedia.org/wiki/Positional_notation + +## Source + +### Created by + +- @serialhex \ No newline at end of file diff --git a/all-your-base/all-your-base-test.lisp b/all-your-base/all-your-base-test.lisp new file mode 100644 index 0000000..ef8cc3b --- /dev/null +++ b/all-your-base/all-your-base-test.lisp @@ -0,0 +1,71 @@ +;; Ensures that all-your-base.lisp and the testing library are always loaded +(eval-when (:compile-toplevel :load-toplevel :execute) + (load "all-your-base") + (quicklisp-client:quickload :fiveam)) + +;; Defines the testing package with symbols from all-your-base and FiveAM in scope +;; The `run-tests` function is exported for use by both the user and test-runner +(defpackage :all-your-base-test + (:use :cl :fiveam) + (:export :run-tests)) + +;; Enter the testing package +(in-package :all-your-base-test) + +;; Define and enter a new FiveAM test-suite +(def-suite* all-your-base-suite) + +(test single-bit-one-to-decimal + (is (equal '(1) (all-your-base:rebase '(1) 2 10)))) + +(test binary-to-single-decimal + (is (equal '(5) (all-your-base:rebase '(1 0 1) 2 10)))) + +(test single-decimal-to-binary + (is (equal '(1 0 1) (all-your-base:rebase '(5) 10 2)))) + +(test binary-to-multiple-decimal + (is (equal '(4 2) (all-your-base:rebase '(1 0 1 0 1 0) 2 10)))) + +(test decimal-to-binary + (is (equal '(1 0 1 0 1 0) (all-your-base:rebase '(4 2) 10 2)))) + +(test trinary-to-hexadecimal + (is (equal '(2 10) (all-your-base:rebase '(1 1 2 0) 3 16)))) + +(test hexadecimal-to-trinary + (is (equal '(1 1 2 0) (all-your-base:rebase '(2 10) 16 3)))) + +(test number-15-bit-integer + (is (equal '(6 10 45) (all-your-base:rebase '(3 46 60) 97 73)))) + +(test empty-list (is (equal '(0) (all-your-base:rebase 'nil 2 10)))) + +(test single-zero (is (equal '(0) (all-your-base:rebase '(0) 10 2)))) + +(test multiple-zeros (is (equal '(0) (all-your-base:rebase '(0 0 0) 10 2)))) + +(test leading-zeros (is (equal '(4 2) (all-your-base:rebase '(0 6 0) 7 10)))) + +(test input-base-is-one (is (not (all-your-base:rebase '(0) 1 10)))) + +(test input-base-is-zero (is (not (all-your-base:rebase 'nil 0 10)))) + +(test input-base-is-negative (is (not (all-your-base:rebase '(1) -2 10)))) + +(test negative-digit (is (not (all-your-base:rebase '(1 -1 1 0 1 0) 2 10)))) + +(test invalid-positive-digit + (is (not (all-your-base:rebase '(1 2 1 0 1 0) 2 10)))) + +(test output-base-is-one (is (not (all-your-base:rebase '(1 0 1 0 1 0) 2 1)))) + +(test output-base-is-zero (is (not (all-your-base:rebase '(7) 10 0)))) + +(test output-base-is-negative (is (not (all-your-base:rebase '(1) 2 -7)))) + +(test both-bases-are-negative (is (not (all-your-base:rebase '(1) -2 -7)))) + +(defun run-tests (&optional (test-or-suite 'all-your-base-suite)) + "Provides human readable results of test run. Default to entire suite." + (run! test-or-suite)) diff --git a/all-your-base/all-your-base.fasl b/all-your-base/all-your-base.fasl new file mode 100644 index 0000000..c51c292 Binary files /dev/null and b/all-your-base/all-your-base.fasl differ diff --git a/all-your-base/all-your-base.lisp b/all-your-base/all-your-base.lisp new file mode 100644 index 0000000..72aeb13 --- /dev/null +++ b/all-your-base/all-your-base.lisp @@ -0,0 +1,18 @@ +(defpackage :all-your-base + (:use :cl) + (:export :rebase)) + +(in-package :all-your-base) + +; conv converts a list representation to base 10 number +; This is complete but incorrect +(defun conv (l base) + (if (null l) + 0 + (+ (* (first l) (expt base (- (length l) 1)) (conv (rest l) base))))) + +(defun unconv (number base exponent) + ; (if (eq exponent -1) (calc-start-exponent number base) exponent) + ; Slip the above into the following (or assign to new variable): + ; (append (list (/ number base**exponent)) (unconv (mod number base**exponent) base (- exponent 1)) + ) diff --git a/hello-world/.exercism/config.json b/hello-world/.exercism/config.json new file mode 100644 index 0000000..df83006 --- /dev/null +++ b/hello-world/.exercism/config.json @@ -0,0 +1,22 @@ +{ + "authors": [ + "serialhex" + ], + "contributors": [ + "verdammelt" + ], + "files": { + "solution": [ + "hello-world.lisp" + ], + "test": [ + "hello-world-test.lisp" + ], + "example": [ + ".meta/example.lisp" + ] + }, + "blurb": "Exercism's classic introductory exercise. Just say \"Hello, World!\".", + "source": "This is an exercise to introduce users to using Exercism", + "source_url": "https://en.wikipedia.org/wiki/%22Hello,_world!%22_program" +} diff --git a/hello-world/.exercism/metadata.json b/hello-world/.exercism/metadata.json new file mode 100644 index 0000000..bbc5afe --- /dev/null +++ b/hello-world/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"common-lisp","exercise":"hello-world","id":"63cebf596eaf44ceb8439728931a28f3","url":"https://exercism.org/tracks/common-lisp/exercises/hello-world","handle":"amcooper","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/hello-world/HELP.md b/hello-world/HELP.md new file mode 100644 index 0000000..92020c5 --- /dev/null +++ b/hello-world/HELP.md @@ -0,0 +1,104 @@ +# Help + +## Running the tests + +## Testing interactively + +Start the REPL from the directory that you downloaded the exercise to. + +You can run the tests by loading the test file into the REPL with `(load "exercise-test")` (replacing "exercise" with appropriate name). Then evaluate `(exercise-test:run-tests)` to run all the tests. + +If you write your code directly in the REPL then simply evaluate `(exercise-test:run-tests)`. + +If you write your code in the exercise lisp file then load it with `(load "exercise")` then evaluate `(exercise-test:run-tests)`. + +## Testing from the command line + +You can launch the tests with this command line invocation (again, replace "exercise" with the appropriate name in two places) + +```sh +ros run --load exercise-test.lisp --eval '(uiop:quit (if (exercise-test:run-tests) 0 1))' +``` + +## Submitting your solution + +You can submit your solution using the `exercism submit hello-world.lisp` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Common Lisp track's documentation](https://exercism.org/docs/tracks/common-lisp) +- The [Common Lisp track's programming category on the forum](https://forum.exercism.org/c/programming/common-lisp) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Setup + +Check out [Installing Common +Lisp][track-install] for +instructions to get started or take a look at the guides available in +the [track's documentation pages][track-docs]. + +## Where to look for help + +Any of the resources listed in the list of [Useful Common Lisp Resources][track-resources] are good places to look for information. + +There are also some [Online Communities][awesome-cl-communities] which may be good places to go for help. + +## 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. +Exercism.org'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.org 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][slime] which offers tight integration +with the REPL; making iterative coding and testing very easy. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can ask for mentoring to help you come to the correct solution. + +[awesome-cl-communities]: https://github.com/GustavBertram/awesome-common-lisp-learning#online-community +[slime]: https://github.com/slime/slime +[track-docs]: /docs/tracks/common-lisp +[track-install]: /docs/tracks/common-lisp/installation +[track-resources]: /docs/tracks/common-lisp/resources \ No newline at end of file diff --git a/hello-world/README.md b/hello-world/README.md new file mode 100644 index 0000000..da86bb6 --- /dev/null +++ b/hello-world/README.md @@ -0,0 +1,35 @@ +# Hello World + +Welcome to Hello World on Exercism's Common Lisp Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Instructions + +The classical introductory exercise. +Just say "Hello, World!". + +["Hello, World!"][hello-world] is the traditional first program for beginning programming in a new language or environment. + +The objectives are simple: + +- Modify the provided code so that it produces the string "Hello, World!". +- Run the test suite and make sure that it succeeds. +- Submit your solution and check it at the website. + +If everything goes well, you will be ready to fetch your first real exercise. + +[hello-world]: https://en.wikipedia.org/wiki/%22Hello,_world!%22_program + +## Source + +### Created by + +- @serialhex + +### Contributed to by + +- @verdammelt + +### Based on + +This is an exercise to introduce users to using Exercism - https://en.wikipedia.org/wiki/%22Hello,_world!%22_program \ No newline at end of file diff --git a/hello-world/hello-world-test.lisp b/hello-world/hello-world-test.lisp new file mode 100644 index 0000000..9095e61 --- /dev/null +++ b/hello-world/hello-world-test.lisp @@ -0,0 +1,22 @@ +;; Ensures that hello-world.lisp and the testing library are always loaded +(eval-when (:compile-toplevel :load-toplevel :execute) + (load "hello-world") + (quicklisp-client:quickload :fiveam)) + +;; Defines the testing package with symbols from hello-world and FiveAM in scope +;; The `run-tests` function is exported for use by both the user and test-runner +(defpackage :hello-world-test + (:use :cl :fiveam) + (:export :run-tests)) + +;; Enter the testing package +(in-package :hello-world-test) + +;; Define and enter a new FiveAM test-suite +(def-suite* hello-world-suite) + +(test say-hi! (is (equal "Hello, World!" (hello-world:hello)))) + +(defun run-tests (&optional (test-or-suite 'hello-world-suite)) + "Provides human readable results of test run. Default to entire suite." + (run! test-or-suite)) diff --git a/hello-world/hello-world.lisp b/hello-world/hello-world.lisp new file mode 100644 index 0000000..16da206 --- /dev/null +++ b/hello-world/hello-world.lisp @@ -0,0 +1,7 @@ +(defpackage :hello-world + (:use :cl) + (:export :hello)) + +(in-package :hello-world) + +(defun hello () "Hello, World!") diff --git a/leslies-lists/.exercism/config.json b/leslies-lists/.exercism/config.json new file mode 100644 index 0000000..ac40033 --- /dev/null +++ b/leslies-lists/.exercism/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "verdammelt" + ], + "files": { + "solution": [ + "leslies-lists.lisp" + ], + "test": [ + "leslies-lists-test.lisp" + ], + "exemplar": [ + ".meta/exemplar.lisp" + ] + }, + "icon": "sublist", + "blurb": "Learn about Lists by helping to keep track of a shopping list." +} diff --git a/leslies-lists/.exercism/metadata.json b/leslies-lists/.exercism/metadata.json new file mode 100644 index 0000000..76fcaa3 --- /dev/null +++ b/leslies-lists/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"common-lisp","exercise":"leslies-lists","id":"b3a3cb2020e84519aec536be4f21395e","url":"https://exercism.org/tracks/common-lisp/exercises/leslies-lists","handle":"amcooper","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/leslies-lists/HELP.md b/leslies-lists/HELP.md new file mode 100644 index 0000000..fd78267 --- /dev/null +++ b/leslies-lists/HELP.md @@ -0,0 +1,104 @@ +# Help + +## Running the tests + +## Testing interactively + +Start the REPL from the directory that you downloaded the exercise to. + +You can run the tests by loading the test file into the REPL with `(load "exercise-test")` (replacing "exercise" with appropriate name). Then evaluate `(exercise-test:run-tests)` to run all the tests. + +If you write your code directly in the REPL then simply evaluate `(exercise-test:run-tests)`. + +If you write your code in the exercise lisp file then load it with `(load "exercise")` then evaluate `(exercise-test:run-tests)`. + +## Testing from the command line + +You can launch the tests with this command line invocation (again, replace "exercise" with the appropriate name in two places) + +```sh +ros run --load exercise-test.lisp --eval '(uiop:quit (if (exercise-test:run-tests) 0 1))' +``` + +## Submitting your solution + +You can submit your solution using the `exercism submit leslies-lists.lisp` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Common Lisp track's documentation](https://exercism.org/docs/tracks/common-lisp) +- The [Common Lisp track's programming category on the forum](https://forum.exercism.org/c/programming/common-lisp) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Setup + +Check out [Installing Common +Lisp][track-install] for +instructions to get started or take a look at the guides available in +the [track's documentation pages][track-docs]. + +## Where to look for help + +Any of the resources listed in the list of [Useful Common Lisp Resources][track-resources] are good places to look for information. + +There are also some [Online Communities][awesome-cl-communities] which may be good places to go for help. + +## 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. +Exercism.org'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.org 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][slime] which offers tight integration +with the REPL; making iterative coding and testing very easy. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can ask for mentoring to help you come to the correct solution. + +[awesome-cl-communities]: https://github.com/GustavBertram/awesome-common-lisp-learning#online-community +[slime]: https://github.com/slime/slime +[track-docs]: /docs/tracks/common-lisp +[track-install]: /docs/tracks/common-lisp/installation +[track-resources]: /docs/tracks/common-lisp/resources \ No newline at end of file diff --git a/leslies-lists/HINTS.md b/leslies-lists/HINTS.md new file mode 100644 index 0000000..0808550 --- /dev/null +++ b/leslies-lists/HINTS.md @@ -0,0 +1,35 @@ +# Hints + +## 1. Making a new list + +- Common Lisp has a [rather descriptively named function][hyper-list] for making a _LIST_. + +## 2. Add things to the list. + +- To add something to the beginning of a list it is idiomatic to *CONS*truct a new list with the item as the `car` and the list as the `cdr`. In the Hyperspec can be found a [page][hyper-cons] that describes this function. + +## 3. What's next thing(s) on the list? + +- Common Lisp has a general purpose function to get an item [from any index of a list][hyper-nth]. +- It also has [several helpers][hyper-first-tenth] for accessing indexes under 11 +- List indexes are zero based. + +## 4. Removing a thing from the list + +- An idiomatic way to remove the first item of a list is to use a function which can separate the `cdr` of the list from the `car` of the list. In the Hyperspec one can find a [page][hyper-rest] describing one of the functions to do this. + +## 5. Bigger lists out of smaller lists + +- Common Lisp has a function [specifically for adding one list to front of another][hyper-append] + +## 6. How much longer? + +- Common Lisp has an [obviously named][hyper-length] function to get the length of a list. + +[hyper-append]: http://www.lispworks.com/documentation/HyperSpec/Body/f_append.htm +[hyper-cons]: http://www.lispworks.com/documentation/HyperSpec/Body/f_cons.htm +[hyper-first-tenth]: http://www.lispworks.com/documentation/HyperSpec/Body/f_firstc.htm +[hyper-length]: http://www.lispworks.com/documentation/HyperSpec/Body/f_length.htm +[hyper-list]: http://www.lispworks.com/documentation/HyperSpec/Body/f_list_.htm +[hyper-nth]: http://www.lispworks.com/documentation/HyperSpec/Body/f_nth.htm +[hyper-rest]: http://www.lispworks.com/documentation/HyperSpec/Body/f_rest.htm \ No newline at end of file diff --git a/leslies-lists/README.md b/leslies-lists/README.md new file mode 100644 index 0000000..8289076 --- /dev/null +++ b/leslies-lists/README.md @@ -0,0 +1,144 @@ +# Leslie's Lengthy Lists + +Welcome to Leslie's Lengthy Lists on Exercism's Common Lisp Track. +If you need help running the tests or submitting your code, check out `HELP.md`. +If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :) + +## Introduction + +## Lists + +Given that the name of the language is Lisp which stands of _LISt Processing_ one might assume that the language has facilities for handling lists of items, and you'd be correct! + +While Common Lisp has other data structures as well as lists, lists are still heavily used. + +A list in Common Lisp is a sequence of items. +The items themselves do not have to be the same type. +For example you can have a list of `1`, `two`, `"III"`. + +### Creating Lists + +One can simply type in a quoted list like this: `'(1 two "III")` and that will cause a list to be created and evaluated (it evaluates to: `(1 two "III")`. + +There are also two main functions used to create lists: `list` and `cons`. + +`list` takes zero or more arguments and evaluates to a list created with those values: + +```lisp +(list 1 'two "III") ; => (1 two "III") +``` + +`cons` takes two items and creates a list which has as its `car` the first item and as its `cdr` the second item: + +```lisp +(cons 1 2) ; => (1 . 2) ;; (a list without `nil` as its `cdr` is printed in this way.) +(cons 1 nil) ; => (1) +(cons 1 (cons 2 nil)) ; => (1 2) +``` + +`car` and `cdr` can be used to access the `car` and `cdr` respectively. + +(`first` and `rest` are synonyms of `car` and `cdr` and work exactly the same.) + +### Length & Random Access + +The length of a list can be determined by the use of `length`. +An empty list has length zero. + +An arbitrary item can be accessed with `nth` (note that lists are zero-indexed). + +It is _not_ an error to request an index that is more than the length. +Instead it evaluates to `nil`: + +```lisp +(nth 23 '(short list))` ; => nil +``` + +There are also 10 helper methods for accessing the first 10 items of a list, they are named: `first`, `second`, `third`, `fourth`, `fifth`, `sixth`, `seventh`, `eighth`, `ninth`, and `tenth`. + +### Combining lists + +Two, or more, lists can be combined with `append`: + +```lisp +(append '(a b c) '(1 2 3)) ; => (A B C 1 2 3) +(append '(a b c) '())) ; => (A B C) +``` + +Each argument given to `append` needs to be a list. + +## Instructions + +Leslie the Lisp Alien needs to do some shopping. It is very important to have a shopping list. One needs to add things to it, and remove things from it. + +Of course simple pen and paper will not do for a Lisp Alien. "List" is most of the word "Lisp" even!. There must be some functions written to help keep track of the shopping. + +Can you help Leslie keep track of the shopping list? + +## 1. Making a new list + +First thing is that Leslie needs to create a empty list. A function called `new-list` would be perfect for that. + +```lisp +(new-list) ; => () +``` + +Oh no... Leslie has a few things in mind already, so they need a function that takes three items (luckily Leslie only creates a list of three items. Nothing more, nothing less!) and creates a new shopping list with those things. Write a function, `list-of-things` which takes three items and returns a list of them. + +```lisp +(list-of-things 'bread 'milk 'butter) ; => '(bread milk butter) +``` + +## 2. Add things to the list. + +Before going to the store Leslie looks in the pantry to see what they need. Help them by writing the function `add-to-list` which adds an item to the beginning of a list. + +```lisp +(add-to-list 'butter '(bread)) ; => '(butter bread) +``` + +## 3. What's next thing(s) on the list? + +When shopping, Leslie wants to know what to look for next. They also like to peek ahead at the list to see the second, third, or even the 23rd item (their lucky number). + +- `first-thing` will evaluate to the first thing on the list +- `second-thing` will evaluate to the second thing +- `third-thing` will evaluate to the third thing +- `twenty-third-thing` will evaluate to the twenty-third thing + +```lisp +(first-thing '(bread butter milk)) ; => 'bread +(second-thing '(bread butter milk)) ; => 'butter +(third-thing '(bread butter milk)) ; => 'milk +``` + +## 4. Removing a thing from the list + +Leslie wants to find the first item of the list and remove the it from the shelf. +Help them out by writing a function `remove-first-item` which evaluates to a list with everything but the first item on the input list. + +```lisp +(remove-first-item '(bread butter milk)) ; => '(butter milk) +``` + +## 5. Bigger lists out of smaller lists + +Leslie realized they accidentally made two shopping lists, not one! Write a function, `list-append` which returns a list that with the elements from the first list followed by the ones from the second list. + +```lisp +(list-append '(bread salt) '(butter milk)) ; => '(bread salt butter milk) +``` + +## 6. How much longer? + +Leslie is getting worried that this shopping trip will take a while. Just how many things are on this list? Write a function `just-how-long` to tell them how long their list is. + +```lisp +(just-how-long '(bread milk butter salt)) ; => 4 +``` + +## Source + +### Created by + +- @verdammelt \ No newline at end of file diff --git a/leslies-lists/leslies-lists-test.lisp b/leslies-lists/leslies-lists-test.lisp new file mode 100644 index 0000000..4289c0a --- /dev/null +++ b/leslies-lists/leslies-lists-test.lisp @@ -0,0 +1,66 @@ +;; Ensures that leslies-lists.lisp and the testing library are always loaded +(eval-when (:compile-toplevel :load-toplevel :execute) + (load "leslies-lists") + (ql:quickload :fiveam)) + +;; Defines the testing package with symbols from leslies-lists and FiveAM in scope +;; The `run-tests` function is exported for use by both the user and test-runner +(defpackage :leslies-lists-test + (:use :cl :fiveam :leslies-lists) + (:export :run-tests)) + +;; Enter the testing package +(in-package :leslies-lists-test) + +;; Define and enter a new FiveAM test-suite +(def-suite leslies-lists-suite) +(in-suite leslies-lists-suite) + +(test new-list "Leslie needs a way to make a new empty list" + (is (equal '() (new-list)))) + +(test list-of-things "Leslie needs to create a new list with three things on it" + (is (equal '(salt skquargzes butter) + (list-of-things 'salt 'skquargzes 'butter)))) + +(test adding-to-the-list "Leslie needs a way of adding items to a list" + (is (equal '(left-handed-frobz) + (add-to-list 'left-handed-frobz (new-list)))) + (is (equal '(left-handed-frobz butter) + (add-to-list 'left-handed-frobz '(butter))))) + +(test peeking-at-the-list "Leslie needs a way to see what items are coming up on the list" + (let ((shopping-list '(left-handed-frobz salt skquargzes butter sananab + motor-oil dilithium-crystals photonic-oscillators + digestive-biscuits marmalade jelly-babies + hazramfoobles crisps chips right-handed-macaroni + various-nozzles seedless-snozzberries ronopotolo + cran-apple apple-cran raisins dihydrogen-oxide + birthday-candles cupcakes))) + (is (equal 'left-handed-frobz (first-thing shopping-list))) + (is (equal 'salt (second-thing shopping-list))) + (is (equal 'skquargzes (third-thing shopping-list))) + (is (equal 'birthday-candles (twenty-third-thing shopping-list))))) + + +(test removing-a-item "Leslie needs to see the list after removing the first item" + (is (equal '(salt skquargzes butter sananab) + (remove-first-item + '(left-handed-frobz salt skquargzes butter sananab))))) + +(test put-two-lists-together "Leslie needs to add one list to another" + (is (equal '(left-handed-frobz salt skquargzes butter) + (list-append '(left-handed-frobz salt) + '(skquargzes butter))))) + +(test how-long-is-the-list "Leslie wants to know how much shopping is left" + (is (= 0 (just-how-long '()))) + (is (= 3 (just-how-long '(left-handed-frobz salt skquargzes)))) + (is (= 2 (just-how-long '(left-handed-frobz salt)))) + (is (= 11 (just-how-long '(left-handed-frobz salt skquargzes butter sananab + motor-oil dilithium-crystals photonic-oscillators + digestive-biscuits marmalade jelly-babies))))) + +(defun run-tests (&optional (test-or-suite 'leslies-lists-suite)) + "Provides human readable results of test run. Default to entire suite." + (run! test-or-suite)) diff --git a/leslies-lists/leslies-lists.lisp b/leslies-lists/leslies-lists.lisp new file mode 100644 index 0000000..761b08b --- /dev/null +++ b/leslies-lists/leslies-lists.lisp @@ -0,0 +1,46 @@ +(defpackage :leslies-lists + (:use :cl) + (:export :new-list + :list-of-things + :add-to-list + :first-thing + :second-thing + :third-thing + :twenty-third-thing + :remove-first-item + :on-the-list-p + :list-append + :just-how-long + :part-of-list + :list-reverse)) + +(in-package :leslies-lists) + +(defun new-list () '()) + +(defun list-of-things (thing1 thing2 thing3) + (list thing1 thing2 thing3)) + +(defun add-to-list (item lst) + (cons item lst)) + +(defun first-thing (list) + (car list)) + +(defun second-thing (list) + (second list)) + +(defun third-thing (list) + (third list)) + +(defun twenty-third-thing (list) + (nth 22 list)) + +(defun remove-first-item (list) + (cdr list)) + +(defun list-append (list1 list2) + (append list1 list2)) + +(defun just-how-long (list) + (length list)) diff --git a/pal-picker/.exercism/config.json b/pal-picker/.exercism/config.json new file mode 100644 index 0000000..b90a512 --- /dev/null +++ b/pal-picker/.exercism/config.json @@ -0,0 +1,17 @@ +{ + "authors": [ + "TheLostLambda" + ], + "files": { + "solution": [ + "pal-picker.lisp" + ], + "test": [ + "pal-picker-test.lisp" + ], + "exemplar": [ + ".meta/exemplar.lisp" + ] + }, + "blurb": "Learn about conditionals and truth and falseness by helping to choose and take care of a pet." +} diff --git a/pal-picker/.exercism/metadata.json b/pal-picker/.exercism/metadata.json new file mode 100644 index 0000000..3d5080a --- /dev/null +++ b/pal-picker/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"common-lisp","exercise":"pal-picker","id":"06acd5f7878c4b4baccd065e01161af6","url":"https://exercism.org/tracks/common-lisp/exercises/pal-picker","handle":"amcooper","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/pal-picker/HELP.md b/pal-picker/HELP.md new file mode 100644 index 0000000..70b6d45 --- /dev/null +++ b/pal-picker/HELP.md @@ -0,0 +1,104 @@ +# Help + +## Running the tests + +## Testing interactively + +Start the REPL from the directory that you downloaded the exercise to. + +You can run the tests by loading the test file into the REPL with `(load "exercise-test")` (replacing "exercise" with appropriate name). Then evaluate `(exercise-test:run-tests)` to run all the tests. + +If you write your code directly in the REPL then simply evaluate `(exercise-test:run-tests)`. + +If you write your code in the exercise lisp file then load it with `(load "exercise")` then evaluate `(exercise-test:run-tests)`. + +## Testing from the command line + +You can launch the tests with this command line invocation (again, replace "exercise" with the appropriate name in two places) + +```sh +ros run --load exercise-test.lisp --eval '(uiop:quit (if (exercise-test:run-tests) 0 1))' +``` + +## Submitting your solution + +You can submit your solution using the `exercism submit pal-picker.lisp` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Common Lisp track's documentation](https://exercism.org/docs/tracks/common-lisp) +- The [Common Lisp track's programming category on the forum](https://forum.exercism.org/c/programming/common-lisp) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Setup + +Check out [Installing Common +Lisp][track-install] for +instructions to get started or take a look at the guides available in +the [track's documentation pages][track-docs]. + +## Where to look for help + +Any of the resources listed in the list of [Useful Common Lisp Resources][track-resources] are good places to look for information. + +There are also some [Online Communities][awesome-cl-communities] which may be good places to go for help. + +## 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. +Exercism.org'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.org 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][slime] which offers tight integration +with the REPL; making iterative coding and testing very easy. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can ask for mentoring to help you come to the correct solution. + +[awesome-cl-communities]: https://github.com/GustavBertram/awesome-common-lisp-learning#online-community +[slime]: https://github.com/slime/slime +[track-docs]: /docs/tracks/common-lisp +[track-install]: /docs/tracks/common-lisp/installation +[track-resources]: /docs/tracks/common-lisp/resources \ No newline at end of file diff --git a/pal-picker/HINTS.md b/pal-picker/HINTS.md new file mode 100644 index 0000000..b3c54db --- /dev/null +++ b/pal-picker/HINTS.md @@ -0,0 +1,36 @@ +# Hints + +## General + +- If you're not sure where to start, try counting the number of "branches" that + are needed by any given conditional +- Check out [this + tutorial](https://riptutorial.com/common-lisp/example/11082/conditional-constructs) + on most Common Lisp conditionals (excluding `case`) +- [This page](https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node84.html) + provides even more detail and also covers `case` + +## 1. Picking a Pal + +- This task requires a multi-branch conditional +- Remember that keywords are just special symbols always equal to themselves + +## 2. In Their Natural Habitat + +- This task requires a multi-branch conditional +- It may be worth looking into the order in which branches of [this + expression](http://www.lispworks.com/documentation/HyperSpec/Body/m_cond.htm) are evaluated. + +## 3. And Now, We Feast + +- This task requires a two-branch conditional + +## 4. A Code of Conduct + +- This task requires single-branch conditionals +- One action is unfitting when `pet` is a particular pet +- The other action is unfitting unless `pet` is a particular pet +- For comparing two strings for equality, the `equal` or `string=` functions can be used (you'll see more about this in the [Equality] and [Strings] concepts) + +[Equality]: /tracks/common-lisp/concepts/equality +[Strings]: /tracks/common-lisp/concepts/strings \ No newline at end of file diff --git a/pal-picker/README.md b/pal-picker/README.md new file mode 100644 index 0000000..8ef9c8d --- /dev/null +++ b/pal-picker/README.md @@ -0,0 +1,164 @@ +# Pal Picker + +Welcome to Pal Picker on Exercism's Common Lisp Track. +If you need help running the tests or submitting your code, check out `HELP.md`. +If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :) + +## Introduction + +## Truthy And Falsy + +In Common Lisp all values are "true" except for `()` which is "false". +There are two special constant symbols `t` and `nil` whose values are true and false respectively. + +## Conditionals + +Common lisp provides several different conditional expressions, the main difference being the number of branches they support. + +- `when` and `unless` allow for a single branch: + +```lisp +(when (= 2 2) "All is well") ; => "All is well" +(unless (= 2 2) "Time to panic!") ; => NIL +``` + +The section after the test expression may be more than one expression. + +- `if` provides the classic if-then-else construct: + +```lisp +(if (= 2 2) 'how-honest 'you-liar) ; => HOW-HONEST +``` + +Note that both the then and else clauses can only be a single expression. + +- `cond` provides a way to have multiple branches without nesting `if` expressions: + +```lisp +(cond ((= 0 2) 'nope) + ((= 1 2) 'try-again) + ((= 2 2) 'quite-true) + ((= 3 2) 'too-far) + (t 'something-else)) +; => QUITE-TRUE +``` + +Note that there is no limit to the number of expressions after the test expression. + +- `case` provides a classic 'switch' style construct: It checks a single value against a number of branches: + +```lisp +(case 'elder-beast + (cat "Meow") + (bird "Chirp") + (dog "Bark") + (otherwise "???")) +; => "???" +``` + +Note that like `cond` there is no limit to the number of expressions after the test expression. + +## Instructions + +When the Lisp aliens first came to Earth, they encountered countless weird and +wonderful things, but one of their favorite discoveries was that of +"pets". Let's be honest, what sort of alien wouldn't gush over a puppy or +kitten? + +Ludwig, after their recent holiday to Earth, has decided they want a pet of their +own! The only issue? Far too many choices! Perhaps you could write a program to +help Ludwig find and care for their next cuddly friend? + +## 1. Picking a Pal + +Though Ludwig is set on getting a pet, that's about as far as they've gotten in +thinking things through. The first step, then, is deciding what sort of pet to +get! + +Ludwig wants to find a pal with a personality that complements their own. Could +you write Ludwig a function called `pal-picker` that takes some personality +trait (a keyword) and returns the type of pet (a string) with the given trait? + +In this particular case: + +- `:lazy` -> `"Cat"` +- `:energetic` -> `"Dog"` +- `:quiet` -> `"Fish"` +- `:hungry` -> `"Rabbit"` +- `:talkative` -> `"Bird"` + +For other, unknown personalities, your function should evaluate to: `"I don't know... A dragon?"` + +```lisp +(pal-picker :quiet) ; => "Fish" +``` + +## 2. In Their Natural Habitat + +Now that Ludwig has a new friend, they'll need a place to stay! Can you help +Ludwig select the right size bed / tank / cage for their pet? + +Here, Ludwig needs a function called `habitat-fitter` for selecting the proper +habitat size (a keyword) from their pet's weight in kilograms (an integer). + +The habitats needed by each size pet are: + +- More than or equal to 40kg -> `:massive` +- 20kg to 39kg inclusive -> `:large` +- 10kg to 19kg inclusive -> `:medium` +- 1kg to 9kg inclusive -> `:small` +- Less than or equal to 0kg -> `:just-your-imagination` + +```lisp +(habitat-fitter 42) ; => :MASSIVE +``` + +## 3. And Now, We Feast + +One thing all earthling pets have in common is their need for food! This concept +is somewhat alien to Ludwig, however, as they are prone to forgetting to refill their +pet's food-bowl. + +Ludwig could use a simple function called `feeding-time-p` to alert them when the +bowl needs refilling. The function would take a percent fullness (an integer) +and return a message in the form of a string. + +If the food level is: + +- Above 20% -> `"All is well."` +- 20% or below -> `"It's feeding time!"` + +```lisp +(feeding-time-p 15) ; => "It's feeding time!" +``` + +## 4. A Code of Conduct + +With all of the basics sorted, Ludwig is looking forward to trying out a number +of exciting things like "petting" and playing "fetch". With that being said, not +every pet is suitable for these activities. + +Ludwig would like a pair of functions – `pet` and `play-fetch` – that take the +type of pet (as a string) and return either nothing or a message like: `"Maybe not with this pet..."` if the action is unfitting. + +Assume that only `"Dog"`s will play fetch and that all pets except `"Fish"` can +be pet. + +```lisp +(pet "Dog") ; => NIL +(play-fetch "Fish") ; => "Maybe not with this pet..." +``` + +~~~~exercism/note +This task requires string comparisons. This can be done using the +[`string=`][string-eq] function. We'll learn more about this later in the +syllabus in the Strings lesson. + +[string-eq]: https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node166.html +~~~~ + +## Source + +### Created by + +- @TheLostLambda \ No newline at end of file diff --git a/pal-picker/pal-picker-test.lisp b/pal-picker/pal-picker-test.lisp new file mode 100644 index 0000000..a7e4e7e --- /dev/null +++ b/pal-picker/pal-picker-test.lisp @@ -0,0 +1,61 @@ +;; Ensures that pal-picker.lisp and the testing library are always loaded +(eval-when (:compile-toplevel :load-toplevel :execute) + (load "pal-picker") + (ql:quickload :fiveam)) + +;; Defines the testing package with symbols from pal-picker and FiveAM in scope +;; The `run-tests` function is exported for use by both the user and test-runner +(defpackage :pal-picker-test + (:use :cl :fiveam :pal-picker) + (:export :run-tests)) + +;; Enter the testing package +(in-package :pal-picker-test) + +;; Define and enter a new FiveAM test-suite +(def-suite pal-picker-suite) +(in-suite pal-picker-suite) + +(test pick-a-pal "Maps personality traits to fitting pets" + (is (string= "Cat" (pal-picker :lazy))) + (is (string= "Dog" (pal-picker :energetic))) + (is (string= "Fish" (pal-picker :quiet))) + (is (string= "Rabbit" (pal-picker :hungry))) + (is (string= "Bird" (pal-picker :talkative))) + (is (string= "I don't know... A dragon?" (pal-picker :fireproof)))) + +(test natural-habitat "Maps pet weights to habitat sizes" + (is (eql :massive (habitat-fitter 100))) + (is (eql :massive (habitat-fitter 40))) + (is (eql :large (habitat-fitter 39))) + (is (eql :large (habitat-fitter 20))) + (is (eql :medium (habitat-fitter 19))) + (is (eql :medium (habitat-fitter 10))) + (is (eql :small (habitat-fitter 9))) + (is (eql :small (habitat-fitter 1))) + (is (eql :just-your-imagination (habitat-fitter 0))) + (is (eql :just-your-imagination (habitat-fitter -5)))) + +(test we-feast "Determines whether the food-bowl needs refilling from its fullness" + (is (string= "It's feeding time!" (feeding-time-p 10))) + (is (string= "All is well." (feeding-time-p 36))) + (is (string= "All is well." (feeding-time-p 74))) + (is (string= "It's feeding time!" (feeding-time-p 3))) + (is (string= "All is well." (feeding-time-p 90)))) + +(test code-of-conduct "Is the given action unsuitable for the given pet?" + (is-false (pet "Cat")) + (is-false (pet "Dog")) + (is-true (pet "Fish")) + (is-false (pet "Rabbit")) + (is-false (pet "Bird")) + + (is-true (play-fetch "Cat")) + (is-false (play-fetch "Dog")) + (is-true (play-fetch "Fish")) + (is-true (play-fetch "Rabbit")) + (is-true (play-fetch "Bird"))) + +(defun run-tests (&optional (test-or-suite 'pal-picker-suite)) + "Provides human readable results of test run. Default to entire suite." + (run! test-or-suite)) diff --git a/pal-picker/pal-picker.lisp b/pal-picker/pal-picker.lisp new file mode 100644 index 0000000..610580e --- /dev/null +++ b/pal-picker/pal-picker.lisp @@ -0,0 +1,31 @@ +(defpackage :pal-picker + (:use :cl) + (:export :pal-picker :habitat-fitter :feeding-time-p + :pet :play-fetch)) + +(in-package :pal-picker) + +(defun pal-picker (personality) + (case personality + (:lazy "Cat") + (:energetic "Dog") + (:quiet "Fish") + (:hungry "Rabbit") + (:talkative "Bird") + (otherwise "I don't know... A dragon?"))) + +(defun habitat-fitter (weight) + (cond ((>= weight 40) :massive) + ((>= weight 20) :large) + ((>= weight 10) :medium) + ((>= weight 1) :small) + ((<= weight 0) :just-your-imagination))) + +(defun feeding-time-p (fullness) + (if (> fullness 20) "All is well." "It's feeding time!")) + +(defun pet (pet) + (when (string= pet "Fish") "Maybe not with this pet...")) + +(defun play-fetch (pet) + (unless (string= pet "Dog") "Maybe not with this pet...")) diff --git a/pizza-pi/.exercism/config.json b/pizza-pi/.exercism/config.json new file mode 100644 index 0000000..88e5ee3 --- /dev/null +++ b/pizza-pi/.exercism/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "TheLostLambda", + "verdammelt" + ], + "files": { + "solution": [ + "pizza-pi.lisp" + ], + "test": [ + "pizza-pi-test.lisp" + ], + "exemplar": [ + ".meta/exemplar.lisp" + ] + }, + "icon": "lasagna", + "blurb": "Learn about arithmetic with integers and floating-point numbers by helping to make pizza." +} diff --git a/pizza-pi/.exercism/metadata.json b/pizza-pi/.exercism/metadata.json new file mode 100644 index 0000000..32796e8 --- /dev/null +++ b/pizza-pi/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"common-lisp","exercise":"pizza-pi","id":"821d10e381c342d6aff56ae250ea4617","url":"https://exercism.org/tracks/common-lisp/exercises/pizza-pi","handle":"amcooper","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/pizza-pi/HELP.md b/pizza-pi/HELP.md new file mode 100644 index 0000000..075ecf0 --- /dev/null +++ b/pizza-pi/HELP.md @@ -0,0 +1,104 @@ +# Help + +## Running the tests + +## Testing interactively + +Start the REPL from the directory that you downloaded the exercise to. + +You can run the tests by loading the test file into the REPL with `(load "exercise-test")` (replacing "exercise" with appropriate name). Then evaluate `(exercise-test:run-tests)` to run all the tests. + +If you write your code directly in the REPL then simply evaluate `(exercise-test:run-tests)`. + +If you write your code in the exercise lisp file then load it with `(load "exercise")` then evaluate `(exercise-test:run-tests)`. + +## Testing from the command line + +You can launch the tests with this command line invocation (again, replace "exercise" with the appropriate name in two places) + +```sh +ros run --load exercise-test.lisp --eval '(uiop:quit (if (exercise-test:run-tests) 0 1))' +``` + +## Submitting your solution + +You can submit your solution using the `exercism submit pizza-pi.lisp` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Common Lisp track's documentation](https://exercism.org/docs/tracks/common-lisp) +- The [Common Lisp track's programming category on the forum](https://forum.exercism.org/c/programming/common-lisp) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Setup + +Check out [Installing Common +Lisp][track-install] for +instructions to get started or take a look at the guides available in +the [track's documentation pages][track-docs]. + +## Where to look for help + +Any of the resources listed in the list of [Useful Common Lisp Resources][track-resources] are good places to look for information. + +There are also some [Online Communities][awesome-cl-communities] which may be good places to go for help. + +## 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. +Exercism.org'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.org 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][slime] which offers tight integration +with the REPL; making iterative coding and testing very easy. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can ask for mentoring to help you come to the correct solution. + +[awesome-cl-communities]: https://github.com/GustavBertram/awesome-common-lisp-learning#online-community +[slime]: https://github.com/slime/slime +[track-docs]: /docs/tracks/common-lisp +[track-install]: /docs/tracks/common-lisp/installation +[track-resources]: /docs/tracks/common-lisp/resources \ No newline at end of file diff --git a/pizza-pi/HINTS.md b/pizza-pi/HINTS.md new file mode 100644 index 0000000..da5701c --- /dev/null +++ b/pizza-pi/HINTS.md @@ -0,0 +1,27 @@ +# Hints + +## General + +- It might be useful to have a list of all of the numeric operations in Lisp. +– you can find a [full list here](http://www.lispworks.com/documentation/HyperSpec/Body/12_aa.htm) + +## 1. A Dough Ratio + +- Common Lisp has [a built-in constant](http://www.lispworks.com/documentation/HyperSpec/Body/v_pi.htm) for π (pi) +- When it comes to rounding, the CL spec provides [a smattering of + functions](http://www.lispworks.com/documentation/HyperSpec/Body/f_floorc.htm) + +## 2. A Splash of Sauce + +- There is [a built-in operator](http://www.lispworks.com/documentation/HyperSpec/Body/f_sqrt_.htm) that might help you take the square-root of a number + +## 3. Some Cheese, Please + +- For squaring or cubing numbers, there is a [built-in exponentiation function](http://www.lispworks.com/documentation/HyperSpec/Body/f_exp_e.htm) +- You might want to take a look at [this page of rounding operations](http://www.lispworks.com/documentation/HyperSpec/Body/f_floorc.htm) and check out some of the alternatives to `round`. + +## 4. A Fair Share + +- This part requires using a function you may not have come across before, the [modulo +function](https://en.wikipedia.org/wiki/Modulo_operation) (`mod`). This function gives you the remainder of the division between two numbers. +- For comparing the result of `mod` to another number, [this page of comparison operators](http://www.lispworks.com/documentation/HyperSpec/Body/f_eq_sle.htm). \ No newline at end of file diff --git a/pizza-pi/README.md b/pizza-pi/README.md new file mode 100644 index 0000000..dea7ad3 --- /dev/null +++ b/pizza-pi/README.md @@ -0,0 +1,143 @@ +# Pizza Pi + +Welcome to Pizza Pi on Exercism's Common Lisp Track. +If you need help running the tests or submitting your code, check out `HELP.md`. +If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :) + +## Introduction + +In Common Lisp, like many languages, numbers come in a few of types – two of the most basic are: + +## Integers + +Like many languages Common Lisp contains integers. +These are whole numbers without a decimal point (like `-6`, `0`, `25`, `1234`, etc.) + +Common Lisp defines no limits on the magnitude of integers. +Integers can be arbitrarily large (or small if negative). + +In general, if you are working with only whole numbers, you should prefer integers as they don't suffer from the same loss of precision as floating-point numbers do over many calculations. + +## Floating Point Numbers + +Like many languages, Common Lisp contains floating point numbers. +These are fractional or whole numbers including a decimal point (like `3.14`, `-1.7`, `99.99`, `2048.0`). + +## Arithmetic + +Common Lisp uses the standard arithmetic operators for most operations but is somewhat unique in using a "prefix-notation" as opposed to the more familiar "infix-notion". + +More visually: + +```lisp +;; Infix-notation (non-lisp languages) +1 + 2 + 3 + 4 + 5 ; => 15 +;; Prefix-notation (lisp languages) +(+ 1 2 3 4 5) ; => 15 +``` + +While prefix notion turns some operations like `2 + 2` into the somewhat unfamiliar `(+ 2 2)` form, it makes it much easier to operate on more than one number at a time. + +### Basic Math + +Common Lisp has all the typical functions: `+`, `-`, `*`, `/` for addition, subtraction, multiplication and division. + +When considering division one may want to know the remainder of a division, that is done by the `mod` (for modulo) function which takes a number and a divisor as arguments: + +```lisp +(mod 4 2) ; => 0 +(mod 2 4) ; => 2 +(mod 10 3) ; => 1 +``` + +### Comparing Numbers + +Finally, you may find it useful to compare different numbers using functions like `=` (equal), `/=` (not equal to), and `>=` (greater than or equal to). +When these comparisons are true (as in `(= 1 1)`), they return `T` and when they aren't (as in `(> 0 1)`), they return `NIL`. + +## Instructions + +Lilly the Lisp Alien loves cooking. She wants to throw a pizza party with her friends but, to make the most of her ingredients, she'd like to know exactly how much of each ingredient she'll need before starting. + +To solve this, Lilly has drafted a program to automate some of the planning but needs your help finishing it. +Will you help Lilly throw the proportionally perfect pizza party? + +## 1. A Dough Ratio + +First things first, every great pizza starts with a delicious dough! + +Lilly is a fan of thin, crispy pizzas with a thicker crust. +The dough needed for the middle is a constant 200g, but every 20cm of crust requires 45g of dough. + +Thankfully, Lilly calculated the equation for grams of dough (g) from the number of pizzas (n) and their diameter (d): + +`g = n * (((45 * pi * d) / 20) + 200)` + +Lilly needs a function that: +* Takes the number of pizzas (n), their diameter (d) +* And returns the dough needed to the nearest gram (g). + +For example, to make 4 pizzas 30cm in diameter: + +```lisp +(dough-calculator 4 30) ; => 1648 +``` + +## 2. A Splash of Sauce + +Lilly is meticulous when applying her sauce, but the size of her pizzas can be inconsistent. + +To figure out the diameter (d), she uses the below equation with sauce applied (s) as a variable: + +`d = square-root of ((40 * s) / (3 * pi))` + +Lilly needs a function that calculates the pizza diameter (d) from every milliliter of sauce applied (s). + +For example, given Lilly has used 250ml of sauce: + +```lisp +(size-from-sauce 250) ; => 32.57 +``` + +## 3. Some Cheese, Please + +On Lilly's planet, all cheese comes in perfect cubes and is sold by size. +(What were you expecting? This is an alien planet after all...) + +Once again, Lilly calculated an equation for the number of pizzas (n) of some diameter (d) made from a cheese cube of side-length (l): + +`n = (2 * (l^3)) / (3* pi * (d^2))` + +Create a function that: +* Takes a side-length of a cheese cube (l) and the pizzas' diameter (d), +* And returns the number of pizzas (n) made while rounding down. + +For example, given a 25x25x25cm cheese cube and pizzas 30cm in diameter: + +```lisp +(pizzas-per-cube 25 30) ; => 3 +``` + +## 4. A Fair Share + +Finally, Lilly wants her pizzas to divide into 8 slices each and distributed evenly among her friends. + +Create a function that: +* Takes a number of pizzas and number of friends +* and returns `T` if the pizzas will evenly distribute among friends and `NIL` otherwise. + +For example: + +```lisp +;; For splitting 3 pizzas between 4 friends +(fair-share-p 3 4) ; => T +;; For splitting 2 pizzas between 3 friends +(fair-share-p 2 3) ; => NIL +``` + +## Source + +### Created by + +- @TheLostLambda +- @verdammelt \ No newline at end of file diff --git a/pizza-pi/pizza-pi-test.lisp b/pizza-pi/pizza-pi-test.lisp new file mode 100644 index 0000000..f130465 --- /dev/null +++ b/pizza-pi/pizza-pi-test.lisp @@ -0,0 +1,58 @@ +;; Ensures that pizza-pi.lisp and the testing library are always loaded +(eval-when (:compile-toplevel :load-toplevel :execute) + (load "pizza-pi") + (ql:quickload :fiveam)) + +;; Defines the testing package with symbols from pizza-pi and FiveAM in scope +;; The `run-tests` function is exported for use by both the user and test-runner +(defpackage :pizza-pi-test + (:use :cl :fiveam :pizza-pi) + (:export :run-tests)) + +;; Enter the testing package +(in-package :pizza-pi-test) + +;; Define and enter a new FiveAM test-suite +(def-suite pizza-pi-suite) +(in-suite pizza-pi-suite) + +(test dough-ratio "Calculate the grams of dough needed for given number and size of pizzas" + (is (= 1648 (dough-calculator 4 30))) + (is (= 895 (dough-calculator 2 35))) + (is (= 2048 (dough-calculator 6 20))) + (is (= 306 (dough-calculator 1 15))) + (is (= 1353 (dough-calculator 5 10)))) + +(defun rounds-to (expected actual) + (flet ((to-2-places (n) (/ (round (* 100 n)) 100.0))) + (is (= (to-2-places expected) (to-2-places actual))))) + +(test splash-of-sauces "Calculates the diameter of a pizza from the amount of sauce applied" + (is (rounds-to 32.57 (size-from-sauce 250))) + (is (rounds-to 20.60 (size-from-sauce 100))) + (is (rounds-to 37.42 (size-from-sauce 330))) + (is (rounds-to 46.52 (size-from-sauce 510))) + (is (rounds-to 53.72 (size-from-sauce 680)))) + +(test cheese-please "Calculates the number of pizzas of a certain size that can be made from an amount of cheese" + (is (= 3 (pizzas-per-cube 25 30))) + (is (= 1 (pizzas-per-cube 15 20))) + (is (= 132 (pizzas-per-cube 100 40))) + (is (= 0 (pizzas-per-cube 5 10))) + (is (= 85 (pizzas-per-cube 45 15)))) + +(test fair-share "Calculates if some number of pizzas can be evenly divided between friends" + (is-true (fair-share-p 3 4)) + (is-false (fair-share-p 2 3)) + (is-false (fair-share-p 4 5)) + (is-true (fair-share-p 4 8)) + (is-true (fair-share-p 1 4)) + (is-true (fair-share-p 21 7)) + (is-false (fair-share-p 11 10)) + (is-true (fair-share-p 0 5)) + (is-false (fair-share-p 17 5)) + (is-true (fair-share-p 16 64))) + +(defun run-tests (&optional (test-or-suite 'pizza-pi-suite)) + "Provides human readable results of test run. Default to entire suite." + (run! test-or-suite)) diff --git a/pizza-pi/pizza-pi.lisp b/pizza-pi/pizza-pi.lisp new file mode 100644 index 0000000..8ac682e --- /dev/null +++ b/pizza-pi/pizza-pi.lisp @@ -0,0 +1,18 @@ +(defpackage :pizza-pi + (:use :cl) + (:export :dough-calculator :pizzas-per-cube + :size-from-sauce :fair-share-p)) + +(in-package :pizza-pi) + +(defun dough-calculator (pizzas diameter) + (round (* pizzas (+ 200 (/ (* 45 3.14159 diameter) 20))))) + +(defun size-from-sauce (sauce) + (sqrt (/ (* 40 sauce) (* 3 3.14159)))) + +(defun pizzas-per-cube (cube-size diameter) + (floor (* (expt cube-size 3) 2) (* 3 3.14159 (expt diameter 2)))) + +(defun fair-share-p (pizzas friends) + (multiple-value-bind (quotient remainder) (floor (* 8 pizzas) friends) (zerop remainder))) diff --git a/socks-and-sexprs/.exercism/config.json b/socks-and-sexprs/.exercism/config.json new file mode 100644 index 0000000..c9d1b62 --- /dev/null +++ b/socks-and-sexprs/.exercism/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "verdammelt", + "TheLostLambda" + ], + "files": { + "solution": [ + "socks-and-sexprs.lisp" + ], + "test": [ + "socks-and-sexprs-test.lisp" + ], + "exemplar": [ + ".meta/exemplar.lisp" + ] + }, + "blurb": "Learn the basics of Common Lisp by helping your friend clean their room so they can go out and have some fun." +} diff --git a/socks-and-sexprs/.exercism/metadata.json b/socks-and-sexprs/.exercism/metadata.json new file mode 100644 index 0000000..16f599a --- /dev/null +++ b/socks-and-sexprs/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"common-lisp","exercise":"socks-and-sexprs","id":"3db9257db97647a9be395a78b4a76f78","url":"https://exercism.org/tracks/common-lisp/exercises/socks-and-sexprs","handle":"amcooper","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/socks-and-sexprs/HELP.md b/socks-and-sexprs/HELP.md new file mode 100644 index 0000000..ed9b043 --- /dev/null +++ b/socks-and-sexprs/HELP.md @@ -0,0 +1,104 @@ +# Help + +## Running the tests + +## Testing interactively + +Start the REPL from the directory that you downloaded the exercise to. + +You can run the tests by loading the test file into the REPL with `(load "exercise-test")` (replacing "exercise" with appropriate name). Then evaluate `(exercise-test:run-tests)` to run all the tests. + +If you write your code directly in the REPL then simply evaluate `(exercise-test:run-tests)`. + +If you write your code in the exercise lisp file then load it with `(load "exercise")` then evaluate `(exercise-test:run-tests)`. + +## Testing from the command line + +You can launch the tests with this command line invocation (again, replace "exercise" with the appropriate name in two places) + +```sh +ros run --load exercise-test.lisp --eval '(uiop:quit (if (exercise-test:run-tests) 0 1))' +``` + +## Submitting your solution + +You can submit your solution using the `exercism submit socks-and-sexprs.lisp` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Common Lisp track's documentation](https://exercism.org/docs/tracks/common-lisp) +- The [Common Lisp track's programming category on the forum](https://forum.exercism.org/c/programming/common-lisp) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Setup + +Check out [Installing Common +Lisp][track-install] for +instructions to get started or take a look at the guides available in +the [track's documentation pages][track-docs]. + +## Where to look for help + +Any of the resources listed in the list of [Useful Common Lisp Resources][track-resources] are good places to look for information. + +There are also some [Online Communities][awesome-cl-communities] which may be good places to go for help. + +## 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. +Exercism.org'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.org 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][slime] which offers tight integration +with the REPL; making iterative coding and testing very easy. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can ask for mentoring to help you come to the correct solution. + +[awesome-cl-communities]: https://github.com/GustavBertram/awesome-common-lisp-learning#online-community +[slime]: https://github.com/slime/slime +[track-docs]: /docs/tracks/common-lisp +[track-install]: /docs/tracks/common-lisp/installation +[track-resources]: /docs/tracks/common-lisp/resources \ No newline at end of file diff --git a/socks-and-sexprs/HINTS.md b/socks-and-sexprs/HINTS.md new file mode 100644 index 0000000..5729ba0 --- /dev/null +++ b/socks-and-sexprs/HINTS.md @@ -0,0 +1,27 @@ +# Hints + +## 1. Refresh Lenny on symbols + +- If you are having trouble returning a symbol from a function, you might need + to take a look at [quoting][so-quoting]! +- Recall that keywords are slightly different from regular symbols, and [must + be preceded by a different character][clhs-keywordp]. + +## 2. Help Lenny distinguish between atoms and conses + +- Common Lisp contains predicate functions for determining many things. In + particular there are two that are very relevant [here][clhs-conses]. Look for + functions with "atom" or "cons" in the name. +- Note: in general predicate functions end with `p` or `-p`. The naming of the + atom predicate is an unfortunate inconsistency. + +## 3. Help Lenny split up their conses + +- When it comes to getting the first element and the rest of an sexpr, there + are a couple of options [here][clhs-conses]. +- Look specifically for _Accessors_ as opposed to functions. +- A couple options might have more intuitive names than you'd expect! + +[so-quoting]: https://stackoverflow.com/questions/134887/when-to-use-or-quote-in-lisp +[clhs-keywordp]: http://www.lispworks.com/documentation/HyperSpec/Body/f_kwdp.htm#keywordp +[clhs-conses]: http://www.lispworks.com/documentation/HyperSpec/Body/c_conses.htm \ No newline at end of file diff --git a/socks-and-sexprs/README.md b/socks-and-sexprs/README.md new file mode 100644 index 0000000..b2c49c2 --- /dev/null +++ b/socks-and-sexprs/README.md @@ -0,0 +1,141 @@ +# Sorting Socks and Sexprs + +Welcome to Sorting Socks and Sexprs on Exercism's Common Lisp Track. +If you need help running the tests or submitting your code, check out `HELP.md`. +If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :) + +## Introduction + +## Comments + +Common Lisp allows the programmer to write "comments" that are ignored by the computer. +Single-line comments begin with one or more semi-colons (`;`) and, occasionally, you may see the following: + +```lisp +(code...) ; => value +``` + +Where the comment is being used to indicate what value is returned by Common Lisp after running the code on that line. + +It is idiomatic to use a single semi-colon for a short comment at the end of a line, two for a longer comment above a section of code, three for long comment describing something such as a function and four for a comment such as a header at the top of a source file. + +## Cons + +All Common Lisp objects are either "atoms" (single, indivisible values) or "conses" (built by the construct function "cons"). +A cons is made up of two parts: the first element and the rest of the elements. +For historical reasons these two parts are called the `car` and the `cdr`. +When these conses are evaluated as code, the first element (`car`) represents the function being called while the rest of the elements +(`cdr`) represent the arguments to that function: + +```lisp +( ... ) +; ^ car ^ | ^ cdr ^ +``` + +## Expressions + +All Common Lisp code is made of S-Expressions (Symbolic Expressions). They are called sexprs for short. Every sexpr is either an atom or a cons. When S-Expressions are evaluated, they automatically return some value which takes the place of the expression. When writing your own functions (using `defun`), the last value within the body of the `defun` is automatically returned: + +```lisp +;; Defining a new function +(defun gimme-foo () 'foo) +;; Calling the function as an S-Expression +(gimme-foo) ; => FOO +``` + +## Symbols + +Symbols in Common Lisp are special values that can point to other values or, in the case of _keywords_, themselves. +When symbols are evaluated by Lisp, they are replaced with the values they point to: + +```lisp +foo ; => +:foo ; => :FOO +``` + +Note that keywords are denoted by a leading colon (`:`). + +Quoting – the addition of `'` before an S-expression – tells Lisp to not evaluate that expression. +By quoting `'foo` in our `defun` example, we avoided Lisp attempting to look up (and failing to find) whatever `FOO` was supposed to point to, instead, returning the value `FOO` itself. +If `FOO` has not been defined anywhere in our program: + +```lisp +foo ; => +'foo ; => FOO +``` + +For now, you can consider this just as a way to return symbols from a function, but we will revisit quoting and further explore its implications in future concept exercises. + +## Instructions + +You! Yes you, brave lisper! [Lenny the lisp alien][alien] needs your help! Lenny +wants to go hang out with all the other lisp aliens, but their parents have told Lenny +that they're not going anywhere until their room is clean. + +The issue is, Lenny can hardly see the floor! Lenny can't tell their atoms from their +conses, their symbols from their keywords, their `car`s from their `cdr`s! + +Can you help Lenny sort this mess out? Don't forget [your parentheses][xkcd], +you'll be needing them! + +## 1. Refresh Lenny on symbols + +Before Lenny can get started, they need a refresher on what symbols and keywords +look like – it's been a while since they last tidied up... Help Lenny out by +defining two functions: `lennys-favorite-food` which evaluates to some symbol, +and `lennys-secret-keyword` which evaluates to some keyword. For example: + +```lisp +(lennys-favorite-food) ; => LASAGNA +``` + +```lisp +(lennys-secret-keyword) ; => :ALIENS-ARE-REAL +``` + +Note that you can return any symbol or keyword you'd like (if you're not a fan +of lasagna or aren't a believer in aliens). + +## 2. Help Lenny distinguish between atoms and conses + +Once Lenny has had a refresher on symbols and keywords, it's time to get +cleaning! To sort through all of the S-Expressions laying around their room, Lenny +needs help determining what is a cons and what is an atom. You can help by +implementing two functions, `is-an-atom-p` and `is-a-cons-p`: + +```lisp +(is-an-atom-p 'one-thing) ; => T +(is-an-atom-p '(two things)) ; => NIL +``` + +```lisp +(is-a-cons-p 'one-thing) ; => NIL +(is-a-cons-p '(two things)) ; => T +``` + +For this section, there are a number of built-in lisp functions that you may +find useful. Additionally, for now, you can simply consider `T` as true and +`NIL` as false. + +## 3. Help Lenny split up their conses + +Finally, Lenny's conses are too bulky to put away neatly, so they need to split +them up into smaller parts. Can you help by defining two more functions to break +up their conses (`first-thing` and `rest-of-it`)? + +```lisp +(first-thing '(first second third)) ; => FIRST +(rest-of-it '(first second third)) ; => (SECOND THIRD) +``` + +Again, some of Common Lisp's built-in functions might come in handy here. + +[alien]: http://www.lisperati.com/logo.html +[xkcd]: https://xkcd.com/297/ + +## Source + +### Created by + +- @verdammelt +- @TheLostLambda \ No newline at end of file diff --git a/socks-and-sexprs/socks-and-sexprs-test.lisp b/socks-and-sexprs/socks-and-sexprs-test.lisp new file mode 100644 index 0000000..9c69590 --- /dev/null +++ b/socks-and-sexprs/socks-and-sexprs-test.lisp @@ -0,0 +1,69 @@ +;; Ensures that socks-and-sexprs.lisp and the testing library are always loaded +(eval-when (:compile-toplevel :load-toplevel :execute) + (load "socks-and-sexprs") + (ql:quickload :fiveam)) + +;; Defines the testing package with symbols from socks-and-sexprs and FiveAM in scope +;; The `run-tests` function is exported for use by both the user and test-runner +(defpackage :socks-and-sexprs-test + (:use :cl :fiveam :socks-and-sexprs) + (:export :run-tests)) + +;; Enter the testing package +(in-package :socks-and-sexprs-test) + +;; Define and enter a new FiveAM test-suite +(def-suite socks-and-sexprs-suite) +(in-suite socks-and-sexprs-suite) + +(test symbols "Lenny's favorite food is a non-null symbol" + (is-true (symbolp (lennys-favorite-food))) + (is-false (null (lennys-favorite-food))) + (is-false (keywordp (lennys-favorite-food)))) + +(test keywords "Lenny's secret keyword is a keyword" + (is-true (keywordp (lennys-secret-keyword)))) + +(test atoms "Lenny can recognise atoms" + (is-true (is-an-atom-p 5)) + (is-true (is-an-atom-p 5.5)) + (is-true (is-an-atom-p 'a)) + (is-true (is-an-atom-p "hello")) + (is-true (is-an-atom-p t)) + (is-true (is-an-atom-p :a-keyword)) + (is-true (is-an-atom-p #'atom)) + (is-true (is-an-atom-p #'consp)) + (is-true (is-an-atom-p nil)) + (is-false (is-an-atom-p '(1 2 3))) + (is-false (is-an-atom-p '(a . b))) + (is-false (is-an-atom-p (list 1 2 3))) + (is-false (is-an-atom-p (cons 'a 'b)))) + +(test conses "Lenny can recognise conses" + (is-true (is-a-cons-p '(a . b))) + (is-true (is-a-cons-p (cons 'a 'b))) + (is-true (is-a-cons-p (list 1 2 3))) + (is-false (is-a-cons-p 5)) + (is-false (is-a-cons-p 5.5)) + (is-false (is-a-cons-p 'a)) + (is-false (is-a-cons-p "hello")) + (is-false (is-a-cons-p t)) + (is-false (is-a-cons-p :a-keyword)) + (is-false (is-a-cons-p #'atom)) + (is-false (is-a-cons-p #'consp)) + (is-false (is-a-cons-p nil))) + +(test first "Lenny can get the first item of a cons" + (is (equal 'a (first-thing (cons 'a 'b)))) + (is (equal 'a (first-thing (list 'a 'b))))) + +(test rest "Lenny can get the rest of a cons" + (is (equal 'b (rest-of-it (cons 'a 'b)))) + (is (equal '(b) (rest-of-it (cons 'a (cons 'b nil))))) + (is (equal '(b) (rest-of-it (list 'a 'b)))) + (is (equal nil (rest-of-it (cons 'a nil)))) + (is (equal nil (rest-of-it (list 'a))))) + +(defun run-tests (&optional (test-or-suite 'socks-and-sexprs-suite)) + "Provides human readable results of test run. Default to entire suite." + (run! test-or-suite)) diff --git a/socks-and-sexprs/socks-and-sexprs.lisp b/socks-and-sexprs/socks-and-sexprs.lisp new file mode 100644 index 0000000..f41f305 --- /dev/null +++ b/socks-and-sexprs/socks-and-sexprs.lisp @@ -0,0 +1,43 @@ +(defpackage :socks-and-sexprs + (:use :cl) + (:export :lennys-favorite-food :lennys-secret-keyword + :is-an-atom-p :is-a-cons-p :first-thing :rest-of-it)) + +(in-package :socks-and-sexprs) + +;; Evaluates to some symbol (not a keyword) +(defun lennys-favorite-food () + ;; put your symbol here + 'bibimbap + ) + +;; Evaluates to some keyword +(defun lennys-secret-keyword () + ;; put your keyword here + :nil + ) + +;; Evaluates to T if THING is an atom, NIL otherwise +(defun is-an-atom-p (thing) + ;; put the code to determine if THING is an atom here + ; (atom thing) + (not (consp thing)) + ) + +;; Evaluates to T if THING is a cons, NIL otherwise +(defun is-a-cons-p (thing) + ;; put the code to determine if THING is a CONS here + (consp thing) + ) + +;; Evaluates to the first part of CONS +(defun first-thing (cons) + ;; put the code to get the first part of CONS here + (car cons) + ) + +;; Evaluates to the 'rest' of the CONS +(defun rest-of-it (cons) + ;; put the code to get the rest of CONS here + (cdr cons) + )