Compare commits

...

142 commits

Author SHA1 Message Date
Sam Baumann
8d2b6ecc98 Switch 'AT&T Park' to 'Oracle Park' on answer sheet 2024-05-28 15:23:52 -05:00
Sergey Makarichev
76b279a8a8 Fix some typos and style non-uniformities 2023-01-04 15:20:56 -06:00
Ivan Bilous
3ddfa8c241 Fix grammar 2022-09-17 10:58:07 -05:00
William Vaughn
9ed29b7922 change Giants ballpark from AT&T to Oracle 2022-01-29 16:39:10 -06:00
Nick Reilingh
a9c22921a5 27_multimethods.clj - fixed typo?
The word "do" seems to be missing in the final koan.
2020-07-08 10:10:08 -05:00
neta-kedem
d749795c31 fixing typo
missing c in the word function
2019-12-02 13:48:27 -06:00
neta-kedem
040070bbed text styling
"A multimethod takes one or more arguments" sound better than previous version
2019-12-02 13:48:01 -06:00
Alex Lynham
eb73f39c9e Add some simple examples for multimethods 2019-10-28 16:37:33 -05:00
Spenser Truex
9c115996f9 Fixed whitespace: trailing and indent. 2019-10-28 16:35:08 -05:00
Alex Lynham
3afe01adc7 Address changes to PR adding threading forms 2019-09-23 10:50:43 -05:00
Alex Lynham
ba141d0fc2 Add some examples for threading macros and a couple of simple transducer koans 2019-09-23 10:50:43 -05:00
Jonathan Chen
9481906390 docs: fix spelling (#142) 2019-06-10 10:10:03 -05:00
Colin Jones
3cc7b91c23
Back to snapshot 2018-12-31 09:39:05 -06:00
Colin Jones
ba02e4b118
Bump version to 0.5.1 2018-12-31 09:38:34 -06:00
Colin Jones
44ccc0ee90
Bump to latest Clojure 2018-12-31 09:38:13 -06:00
Colin Jones
809af93e35
Focus the problem and clean up naming in macros
Fixes #134
2018-04-09 10:12:47 -05:00
Colin Jones
faa02b5fae
Bump lein-koan dependency 2017-10-24 11:22:42 -05:00
Colin Jones
ad5c0e6081
Bump koan-engine to remove old jline 2017-10-24 11:13:26 -05:00
Colin Jones
7974120ec3
Use an explicit function argument for iterate
Also replace :hello with "hello" to avoid potential confusion since
:hello is also a function.

refs #75
2017-10-09 13:17:12 -05:00
Colin Jones
e74a2664f0
Make it clearer what %2 is about 2017-05-10 08:32:25 -05:00
Colin Jones
9b3efbb026
Remove presumptuous "easy", "just", "simple" 2017-05-05 07:54:11 -05:00
Colin Jones
0e31a9daeb
Modernize & simplify README 2017-05-03 15:27:23 -05:00
Colin Jones
6fc09456ac
Organize README installation/running options 2017-02-10 09:02:31 -06:00
Colin Jones
d59a8c4659
Merge remote-tracking branch 'origin/pr/119' 2017-02-10 09:02:20 -06:00
Colin Jones
ca192bfd83
Add note about running from a REPL
h/t to Josko (via email) for the documentation request
2017-02-07 12:21:40 -06:00
Jeroen De Dauw
a3fb3b5bc8 Added Vagrant support (#123)
This makes installation easier, esp for those that do not know
the toolchain.
2017-02-07 12:19:25 -06:00
Sara Inés Calderón
247e5d0351 Spelling fix occurrence (#121)
Updated spelling of "occurrence"
2016-11-29 22:48:54 -06:00
Fernando Pradas
009a0bd657 Add Docker information in README 2016-11-12 22:24:45 +01:00
Ken Smith
e0d037d529 Correct closing paren for meditations (#118)
The closing paren for meditations occurred after the second-to-last
koan instead of the last one, so the test on the last one would never
be executed.
2016-10-26 10:16:11 -05:00
Colin Jones
570fb3b08b
Clean up whitespace a bit 2016-10-06 08:38:28 -05:00
Rafik Naccache
60f1d1ecd3 Maps - add merge-with and map entries as seq 2016-10-06 08:37:18 -05:00
Steve George
a4556887b4 Create a 'merge' test to join maps together. (#113) 2016-09-25 08:36:39 -05:00
Colin Jones
b31227c0a8
Prefer profiles.clj :repl-options/:init to ours 2016-05-23 11:17:31 -05:00
Colin Jones
007cbae060
Reverse order of quoting & macros koans
The macros lesson depends on the quoting one.
2016-05-12 10:43:41 -05:00
Colin Jones
6c00a3e358
Improve some of the quote descriptions 2016-05-12 10:41:09 -05:00
qc1iu
bfeaa7cf07
Create quote koan
Covers
- quote
- syntax-quote
- unquote
2016-05-12 10:26:07 -05:00
fordjm
4fe86ecc74 Fixed a bug in 14_recursion.clj meditations (#105)
* Fixes a bug that allows recursive-reverse to return '(1) for the base case.
2016-05-12 10:23:12 -05:00
Colin Jones
e8526b704c Merge pull request #107 from shtukas/introducing-nil
First encounter with `nil`
2016-05-12 10:22:21 -05:00
Pascal Honoré
ed23b910fa Updated description
Thanks to @Strikingwolf for the suggestion!
2016-03-25 21:53:55 +00:00
Pascal Honoré
4d4cb3b93a First encounter with nil
In 02_strings.clj, the answer to

   (= __ (string/index-of "hello world" "bob"))

is `nil` but `nil` is not obvious for somebody who
never saw it before. This step introduces it.
2016-03-25 18:12:04 +00:00
Damian Niemczyk
a8fe71e26f Update java string methods to clojure functions
With Clojure 1.8.0 finally those string methods now have corresponding
Clojure functions.

Of note here is that the clojure.string/index-of function responds
with nil if nothing is found and not -1 as .IndexOf previously did.
2016-01-25 16:50:34 -06:00
Damian Niemczyk
52fe0cd615 Update clojure version 2016-01-25 16:48:48 -06:00
Colin Jones
ec9d5cd66f Emphasize the leftovers included by partition-all 2016-01-25 16:45:53 -06:00
Michael Perez
d30e324bcd src/koans/21_partition.clj: correct meditation doc
The meditation was misleading because calling `(partition-all 3 (range 5))` returns `((0 1 2) (3 4))` results in sequences having <=3 elements , not strictly less than.
2016-01-14 20:36:43 -05:00
Colin Jones
50f942cf7d Add a few more ideas 2016-01-13 20:27:08 -06:00
Colin Jones
44f37b5c16 Merge pull request #100 from SuperStevenZ/master
Update 23_meta.clj
2015-10-18 10:12:31 -05:00
SuperStevenZ
51176084a4 Update 23_meta.clj
Fixed a spelling mistake:
23_meta.clj 12:18
*succintly* -> succinctly
2015-10-17 08:48:49 -05:00
Colin Jones
b64b17fc51 Merge pull request #97 from Strikingwolf/string-koan
String koan
2015-10-02 19:08:41 -05:00
William Jackson
3e3b2d3a94 Fix mispellings 2015-10-02 18:23:29 -05:00
William Jackson
4c3ab6f57a Remove duplicates 2015-10-02 18:21:18 -05:00
Striking
6def6d2392 Update ideaboard 2015-09-13 01:21:15 -05:00
Striking
5a836be75c Create the meditations of a string koan
Covers
* String creation
* String concatenation
* Counting strings characters and getting them
* Difference between chars and strings
* Substrings
* string/join w/ and w/o separators
* string/split-lines
* .indexOf and .lastIndexOf
* string/trim
* char? and string?
* blank?
2015-09-13 01:17:47 -05:00
Striking
e2a9f9556b Add more to .gitignore 2015-09-13 00:42:18 -05:00
Striking
7498707bc0 Change file/ns names for everything and add 02_strings 2015-09-13 00:28:39 -05:00
Striking
13d95a4f50 Update clojure version 2015-09-13 00:27:53 -05:00
Colin Jones
11de6bfb07 Update ideaboard based on new metadata koan 2015-07-20 08:39:58 -05:00
Colin Jones
cba0d3733f Merge remote-tracking branch 'brymaven/meta-koan' 2015-07-20 08:26:42 -05:00
Colin Jones
071e5a215d Merge pull request #94 from ode79/patch-2
Remove extra space in 10_lazy_sequences.clj
2015-07-20 08:25:06 -05:00
Colin Jones
391d847146 Merge pull request #93 from ode79/patch-1
Alignment on line in 09_runtime_polymorphism.clj
2015-07-20 08:24:51 -05:00
ode79
fed56b1cdd Extra whitespace in 10_lazy_sequences.clj 2015-07-19 23:55:37 +01:00
ode79
f25c3ef62f Alignment on line in 09_runtime_polymorphism.clj
1 missing space.

Most inconsequential pull request ever :)
2015-07-19 23:41:27 +01:00
Colin Jones
8e846cf07a Merge pull request #92 from Realtin/91-readme-links-to-releases
change download link to releases
2015-07-14 08:38:44 -05:00
realtin
0e74118eb8 change download link to releases 2015-07-14 13:43:50 +02:00
Colin Jones
c4639a2c38 Merge pull request #90 from brymaven/group-by-formatting
Fix grammar and formatting in group by koan
2015-07-12 20:42:37 -05:00
Bryant
93e76cb56a Fix grammar and formatting in group by koan 2015-07-12 17:43:16 -07:00
Bryant
173dd560d7 Add koan for metadata 2015-07-12 17:17:48 -07:00
Colin Jones
b1ea905c57 Update answer sheet for ClojureBridge-MN changes 2015-07-06 07:28:21 -05:00
Colin Jones
37cc4ce04a Merge remote-tracking branch 'clojurebridge-minneapolis/master' 2015-07-06 07:13:05 -05:00
Elena Machkasova
c7df597d8f Merge pull request #5 from tmarble/tmarble/wednesday
Replaced Playground song with Oxford comma
2015-06-24 14:32:12 -05:00
Tom Marble
356844ba0a Replaced Playground song with Oxford comma 2015-06-24 14:11:00 -05:00
Tom Marble
2a619dbe73 cherry pick upstream delta from 6/14/2015 2015-06-24 08:15:45 -05:00
Tom Marble
690f171ccb removed PullRequests.md 2015-06-24 08:01:42 -05:00
Tom Marble
f30feb25fe Merge branch 'friendlier-koans' 2015-06-24 07:58:49 -05:00
Tom Marble
f678ac204d Merge pull request #3 from elenam/master
changed another foo to hello
2015-06-24 07:44:13 -05:00
Tom Marble
260ecd06d8 Merge pull request #4 from tmarble/tmarble/test-local-pr
Tmarble/test local pr
2015-06-22 18:43:22 -05:00
Tom Marble
3df3f8999a Minor typo 2015-06-22 18:40:31 -05:00
Tom Marble
13262d30b3 Added PullRequests.md 2015-06-22 18:38:36 -05:00
Elena Machkasova
6866ccd933 changed another foo to hello 2015-06-14 23:35:01 -05:00
Tom Marble
54d4b1a999 refer to clojure.set in 04 2015-06-14 21:38:41 -05:00
Tom Marble
6d5b226307 attempt to make koans more inclusive 2015-06-14 21:06:04 -05:00
Tom Marble
c4958a9263 Merge pull request #1 from clojurebridge-minneapolis/brian/fix-koans-2
Made conditionals exercises more PC.
2015-06-14 20:56:50 -05:00
Tom Marble
8297fc8b69 Merge pull request #2 from elenam/patch-1
replaced "foo" examples with "hello"
2015-06-14 20:56:43 -05:00
Colin Jones
614562b053 Merge pull request #85 from hatemogi/master
fix an error on the answer sheet
2015-06-14 17:26:16 -05:00
Daehyun Kim
b6c2a9c142 fix the answer sheet error on winter olympic-years 2015-06-15 03:02:12 +09:00
Brian Dawn
29aedc3a6a Made conditionals exercises more novice friendly. 2015-06-14 11:15:40 -05:00
Elena Machkasova
8a5303df8f replaced "foo" examples with "hello"
This is more novice-friendly
2015-06-14 11:11:39 -05:00
Colin Jones
3ddfe960b0 Merge pull request #82 from pbzdyl/patch-1
Simplified code.
2015-03-03 13:32:42 -06:00
Piotr Bzdyl
7f21443d6f Simplified code. 2015-03-03 20:12:17 +01:00
Colin Jones
ea8922daf1 Merge pull request #81 from mjansen401/master
update maps koans with 2018 Olympics
2014-12-06 13:40:27 -06:00
Mike Jansen
45ea6e3441 update maps koans with 2018 Olympics 2014-11-26 16:19:04 -06:00
Colin Jones
2c524896c9 Bump to SNAPSHOT for next iteration 2014-10-09 08:34:13 -05:00
Colin Jones
c4f708beeb Bump to 0.5.0 2014-10-09 08:31:38 -05:00
Colin Jones
c40309235d Add leading do to :init
This shouldn't be necessary; this is just a quickfix.
2014-06-27 14:10:05 -05:00
Colin Jones
b987ae9e5a Add random runner 2014-06-13 14:34:38 -05:00
Colin Jones
04b3c4a960 Bump koan-engine dependency
Also eliminates some ns forms that we can do without now.

Closes #71
2014-05-02 16:39:13 -05:00
David Kinzer
711925a043 Add name-spaces to the clojure koans files.
This commit adds two name-spaces to beginning of the clojure koan files
in order to fix an issue both #68 and #69.
2014-05-02 16:38:17 -05:00
Colin Jones
fd3972dd78 Merge pull request #74 from ajmccluskey/21-remove-partial
Remove unnecessary call to partial in second koan
2014-04-25 14:31:13 -05:00
Colin Jones
aec2d28748 Merge pull request #76 from mjansen401/master
fix spacing issue in 05_maps.clj
2014-04-25 14:30:16 -05:00
Mike Jansen
eaf6cd6f80 fix spacing issue in 05_maps.clj 2014-04-25 14:11:18 -05:00
Andrew McCluskey
569aa5e85b Remove unnecessary call to partial in second koan
juxt returns a function that takes a variable number of arguments, so a
partial taking one argument does not need to be created.
2014-03-19 21:59:40 +07:00
Colin Jones
c7bd27047a Bump koan-engine for doc/code alternation check 2014-02-23 21:13:11 -06:00
Colin Jones
645a5f820b Merge pull request #70 from gthb/patch-1
Trivial: fix Hawking's first name
2014-01-27 04:39:10 -08:00
Gunnlaugur Thor Briem
a1e659bc29 Trivial: fix Hawking's first name 2014-01-27 09:52:03 +00:00
Colin Jones
00b7e7c69f Discourage lein 1 use 2014-01-21 09:02:35 -06:00
Colin Jones
72af893f8f Remove group-by since that's been covered 2014-01-21 08:35:54 -06:00
Colin Jones
86a6790ae4 Clean up group-by descriptions & spelling 2014-01-21 08:09:02 -06:00
Takayuki Goto
d6b1b57942 group-by function koan 2014-01-21 08:08:26 -06:00
Colin Jones
f35e258e7f Update answer key to reflect renaming 2014-01-21 07:43:05 -06:00
Kyle VanderBeek
e96ccc4494 Switch variable from index to x to avoid confusing syntax highlightig.
index is a clojure.set function and ended up highlighted for me, sending me
down a rabbit hole of confusion. Avoid this with a more generic variable name
consistent with other Koan files.
2014-01-21 07:35:12 -06:00
Attila Domokos
660998a79d The answer file's expression returns the argument provided
Although the answer's file entry worked, it ignored the provided
argument. With this change it returns what was passed to it.  I branched
from master, I hope it can be merged it in.
2014-01-21 07:30:06 -06:00
Attila Domokos
68dcde939e Make if-not example clearer, fix the answer keys
I felt the example wasn't very clear as the conditional returned the
quoted form of `doom` when it evaluated to true or false. This way the
user needs to find a value that satisfies the if-not conditional so it
returns `doom` and not `more-doom`.
2014-01-21 07:23:03 -06:00
Colin Jones
0cf4cd3121 Move herbivore assertions together 2014-01-21 07:14:27 -06:00
Tobias Pfeiffer
92c7f8b2f0 Added one test to the polymorph suite (test name usage)
* added one tests that verifies that the diet function also
  accesses the name value of the map (before it was possible
  to just return "Bambi eats veggies." for the herbivore diet
* since this is not the main purpose of these specs added just
  this one and not extra specs for default/carnivore as I didn't
  want to bloat it, but could add those as well if desired
2014-01-21 07:14:18 -06:00
Colin Jones
40505491f8 Merge pull request #66 from ChrisCummins/grammar-fix
Minor grammar fix
2014-01-09 03:36:11 -08:00
Chris Cummins
5c27ca25cc Minor grammar fix 2014-01-09 01:14:08 +00:00
Colin Jones
0675850ae7 Merge pull request #61 from drguildo/grammar-fixes
A few grammatical changes.
2013-11-18 10:44:34 -08:00
Simon Morgan
8c0d3f885c A few grammatical changes. 2013-11-18 17:29:13 +00:00
Laura Brown
7525df7f1b update answer key 2013-09-18 08:45:07 -05:00
Laura Brown
f60275e6e4 Update 05_maps.clj
"Often you will need to get the keys, but the order is undependable"
The keys value pairs are already ordered by key. Why not make the original map un-ordered to force the user to think about sorting them?

"You can get the values in a similar way"
Two of the three values are already provided in the answer, the answer to this challenge can be arrived at by providing the missing one through process of elimination without thinking about sorting.
2013-09-18 08:45:07 -05:00
Colin Jones
7718ae4d26 Merge pull request #57 from bobwilliams/master
rearranging map keys to prove sort function
2013-09-18 06:40:33 -07:00
Bob Williams
c5ef11690e rearranging map keys to prove sort function 2013-09-18 09:13:54 -04:00
Colin Jones
725abe45d5 Merge pull request #54 from dkinzer/Fix-spelling
Fix spelling.
2013-08-03 18:14:10 -07:00
David Kinzer
f9268e81bf Fix spelling. 2013-08-04 00:59:37 +00:00
Colin Jones
f0bdefe972 Improve grammatical flow
refs #51
2013-07-24 19:39:17 -05:00
Colin Jones
6adbead02c Merge pull request #50 from marshallshen/master
add one more problem in function exercise
2013-07-22 10:19:43 -07:00
Marshall Shen
e7f18eb4e6 add answer 2013-07-22 12:10:37 -05:00
Marshall Shen
6fb62d5c79 add one more problem in function exercise 2013-07-22 10:55:09 -05:00
Colin Jones
5e342c81bc Override :main ns for lein repl purposes
Fixes #47
2013-05-03 16:56:11 -05:00
Colin Jones
5207198857 Merge pull request #45 from wishpishh/master
Added missing word in code comments
2013-03-15 15:43:27 -07:00
Hannes Johansson
8cd9d64ad0 Added missing word in comment for koan 11 2013-03-15 23:35:41 +01:00
Colin Jones
dcd056addb Gitignore lein-repl-history 2013-03-04 17:53:16 -06:00
Micah Martin and Colin Jones
559f2e2c58 Take pity and clarify higher-order fns a bit 2013-03-04 17:51:31 -06:00
Micah Martin and Colin Jones
bef513721d Clarify wording in maps 2013-03-04 17:33:46 -06:00
Micah Martin and Colin Jones
28bf698566 Clarify keywords, symbols, maps, sets 2013-03-04 17:30:00 -06:00
Micah Martin and Colin Jones
d275ab804d Leave out vec nil for now 2013-03-04 17:10:04 -06:00
Micah Martin and Colin Jones
bd4bcad22a Add some nil exposition 2013-03-04 17:06:46 -06:00
Micah Martin and Colin Jones
25b2f11247 Clarify empty lists & other list operations 2013-03-04 16:59:01 -06:00
Micah Martin and Colin Jones
2e3fead03f Clarify equalities a bit 2013-03-04 16:45:55 -06:00
Colin Jones
7541c79f83 Merge pull request #43 from manaskarekar/cl-koans-readme
Updated README
2013-03-04 07:13:11 -08:00
Manas Karekar
31425d8a4e Updated README 2013-03-03 23:06:39 -05:00
Colin Jones
ca11a8c684 Bump koan-engine for bugfix
Thanks to Manas Karekar for reporting.
2013-03-03 17:21:35 -06:00
Colin Jones
784ff0166a Update deploy script to get lein1 deps and omit GH 2013-03-02 15:02:02 -06:00
37 changed files with 1073 additions and 305 deletions

207
.gitignore vendored
View file

@ -1,9 +1,206 @@
*~
bin bin
classes
lib
releases releases
target
*.jar *.jar
.DS_Store
# Created by https://www.gitignore.io/api/clojure,osx,linux,windows,leiningen,intellij,eclipse,sublimetext
### Clojure ###
pom.xml
pom.xml.asc
*jar
/lib/
/classes/
/target/
/checkouts/
.lein-deps-sum .lein-deps-sum
.lein-plugins .lein-repl-history
.lein-plugins/
.lein-failures
.nrepl-port
### OSX ###
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Linux ###
*~
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
### Windows ###
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
### Leiningen ###
pom.xml
pom.xml.asc
*jar
/lib/
/classes/
/target/
/checkouts/
.lein-deps-sum
.lein-repl-history
.lein-plugins/
.lein-failures
.nrepl-port
### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
*.iml
## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:
# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries
# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml
# Gradle:
# .idea/gradle.xml
# .idea/libraries
# Mongo Explorer plugin:
# .idea/mongoSettings.xml
## File-based project format:
*.ipr
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
### Eclipse ###
*.pydevproject
.metadata
.gradle
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
# Eclipse Core
.project
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
# JDT-specific (Eclipse Java Development Tools)
.classpath
# Java annotation processor (APT)
.factorypath
# PDT-specific
.buildpath
# sbteclipse plugin
.target
# TeXlipse plugin
.texlipse
### SublimeText ###
# cache files for sublime text
*.tmlanguage.cache
*.tmPreferences.cache
*.stTheme.cache
# workspace files are user-specific
*.sublime-workspace
# project files should be checked into the repository, unless a significant
# proportion of contributors will probably not be using SublimeText
# *.sublime-project
# sftp configuration file
sftp-config.json
# Vagrant
.vagrant/
ubuntu-xenial-16.04-cloudimg-console.log

146
README.md
View file

@ -1,60 +1,88 @@
# Clojure Koans # Clojure Koans
The Clojure Koans are a fun and easy way to get started with Clojure - no The Clojure Koans are a fun way to get started with Clojure - no experience
experience assumed or required. Just follow the instructions below to start assumed or required. Follow the instructions below to start making tests pass!
making tests pass!
### Getting Started ## Getting Started
The easiest and fastest way to get the koans up and running is to [download the I recommend starting from a cloned or forked repo. This way you'll be able to
latest zip file from Github](https://github.com/functional-koans/clojure-koans/downloads). track your progress in Git. You might want to create your own branch - that way
This way, you'll have all the dependencies you need, including Clojure itself if you pull back the latest koans from master, it'll be a bit easier to manage
and JLine, and you can skip the rest of this section (skip to "Running the the inevitable conflicts if we make changes to exercises you've already
Koans"). completed.
If you're starting from a cloned or forked repo, that's cool too. This way You have a few options for installation:
you'll be able to track your progress in Git, and see how your answers compare
to others, by checking out the project's Network tab. You might want to create - Install the dependencies for the koans (such as Clojure) on your machine
your own branch - that way if you pull back the latest koans from master, it'll - Use Vagrant and the configuration in this repository
be a bit easier to manage the inevitable conflicts if we make changes to - Use Docker
exercises you've already completed.
Instructions for each option are below!
### Installation on Your Machine
The only things you'll need to run the Clojure Koans are: The only things you'll need to run the Clojure Koans are:
- JRE 1.5 or higher - JDK (I suggest version 8, but anything 6 or above should work fine)
- [clojure-1.5.0.jar](http://repo1.maven.org/maven2/org/clojure/clojure/1.5.0/clojure-1.5.0.zip) - [Leiningen](http://github.com/technomancy/leiningen), a build tool for Clojure
You can use [Leiningen](http://github.com/technomancy/leiningen) to Once you've cloned this repo and installed the dependencies, you can run:
automatically install the Clojure jar in the right place. Leiningen will also
get you a couple more jarfiles, including JLine, which allows you some of the
functionality of readline (command-line history, for example).
### Installing dependencies ```
lein repl
```
Dependencies are installed automatically with lein 2, but if you are still to make sure all the dependencies get downloaded properly (and then `(exit)`
using lein 1 run when you want to quit). See below for details on the REPL.
`lein deps`
which will download all dependencies you need to run the Clojure koans.
### Running the Koans ### Installation with Vagrant
If you're running from the zipfile or using lein 1, simply run Make sure you have [Vagrant](https://www.vagrantup.com/) and
[VirtualBox](https://www.virtualbox.org) installed.
In the root directory of the project, execute:
`script/run` on Mac/\*nix ```
vagrant up
vagrant ssh
cd /vagrant
lein koan run
```
`script\run` on Windows
If you're running from a checkout using lein 2, run the koans via ### Installation with Docker
Once you've got [Docker](https://www.docker.com/) installed, you're basically
all set. You can run these commands to get started:
To run koans:
```
docker run --rm -it -v $(pwd):/app -w /app clojure lein koan run
```
To start up a REPL:
```
docker run --rm -it -v $(pwd):/app -w /app clojure lein repl
```
## Running the Koans
Run the koans via:
`lein koan run` `lein koan run`
It's an auto-runner, so as you save your files with the correct answers, it will If want to run directly from a REPL, once you are inside the `lein repl` prompt you can run the koans with
advance you to the next koan or file (conveniently, all files are prefixed with
the sequence that you should follow). `(exec "run")`
Either way, it's an auto-runner, so as you save your files with the correct
answers, it will advance you to the next koan or file (conveniently, all files
are prefixed with the sequence that you should follow).
You'll see something like this: You'll see something like this:
@ -65,31 +93,33 @@ You'll see something like this:
(= __ true) (= __ true)
The output is telling you that you have a failing test in the file named The output is telling you that you have a failing test in the file named
`01_equalities.clj`, on line 3. So you just need to open that file up and make `01_equalities.clj`, on line 3. So you need to open that file up and make
it pass! You'll always be filling in the blanks to make tests pass. it pass! You'll always be filling in the blanks to make tests pass.
Sometimes there could be several correct answers (or even an infinite number): Sometimes there could be several correct answers (or even an infinite number):
any of them will work in these cases. any of them will work in these cases. Some tests will pass even if you replace
the blanks with whitespace (or nothing) instead of the expected answer. Make sure
you give one correct expression to replace each blank.
The koans differ from normal TDD in that the tests are already written for you, The koans differ from normal TDD in that the tests are already written for you,
so you'll have to pay close attention to the failure messages, because up until so you'll have to pay close attention to the failure messages, because up until
the very end, making a test pass just means that the next failure message comes the very end, making a test pass means that the next failure message comes
up. up.
While it might be easy (especially at first) to just fill in the blanks making While it might be easy (especially at first) to fill in the blanks making
things pass, you should work thoughtfully, making sure you understand why the things pass, you should work thoughtfully, making sure you understand why the
answer is what it is. Enjoy your path to Clojure enlightenment! answer is what it is. Enjoy your path to Clojure enlightenment!
### Trying more things out ## Trying more things out
There's a REPL (Read-Evaluate-Print Loop) included in the Clojure Koans. Just It's very useful to try things out in a REPL (Read-Evaluate-Print Loop)
run: whenever you get stuck or curious. Run:
`script/repl` on Mac/\*nix ```
lein repl
```
`script\repl` on Windows and you'll be able to type expressions in, and see what output they produce.
If you're on lein 2, `lein repl` is what you want instead.
Here are some interesting commands you might try, once you're in a running REPL: Here are some interesting commands you might try, once you're in a running REPL:
@ -108,42 +138,46 @@ And if those still don't make sense:
will show you what those commands mean. will show you what those commands mean.
You can exit the REPL with `CTRL-d` on any OS. You can exit the REPL with `CTRL-d`, `(exit)`, or `(quit)`.
### Contributing ## Contributing
Patches are encouraged! Make sure the answer sheet still passes Patches are encouraged! Make sure the answer sheet still passes
(`script/test`, or `script\test` on Windows, or `lein koan test` on lein2), and (`lein koan test`), and send a pull request.
send a pull request.
The file ideaboard.txt has lots of good ideas for new koans to start, or things The file ideaboard.txt has lots of good ideas for new koans to start, or things
to add to existing koans. So write some fun exercises, add your answers to to add to existing koans. So write some fun exercises, add your answers to
`resources/koans.clj`, and we'll get them in there! `resources/koans.clj`, and we'll get them in there!
Please follow the guidelines in
http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html for
commit messages, and put your code in a feature branch (not master) before
making the pull request. This makes patches easier to review.
Feel free to contact me (Colin Jones / trptcolin) on Github or elsewhere if you Feel free to contact me (Colin Jones / trptcolin) on Github or elsewhere if you
have any questions or want more direction before you start pitching in. have any questions or want more direction before you start pitching in.
### Contributors ## Contributors
https://github.com/functional-koans/clojure-koans/contributors https://github.com/functional-koans/clojure-koans/contributors
### Credits ## Credits
These exercises were started by [Aaron Bedra](http://github.com/abedra) of These exercises were started by [Aaron Bedra](http://github.com/abedra) of
[Relevance, Inc.](http://github.com/relevance) in early 2010, as a learning [Relevance, Inc.](http://github.com/relevance) in early 2010, as a learning
tool for newcomers to functional programming. Aaron's macro-fu makes these tool for newcomers to functional programming. Aaron's macro-fu makes these
koans extremely simple and fun to use, and to improve upon, and without koans clear and fun to use and improve upon, and without Relevance's
Relevance's initiative, this project would not exist. initiative, this project would not exist.
Using the [koans](http://en.wikipedia.org/wiki/koan) metaphor as a tool for Using the [koans](http://en.wikipedia.org/wiki/koan) metaphor as a tool for
learning a programming language started with the learning a programming language started with the
[Ruby Koans](http://rubykoans.com) by [EdgeCase](http://github.com/edgecase). [Ruby Koans](http://rubykoans.com) by [EdgeCase](http://github.com/edgecase).
### License ## License
The use and distribution terms for this software are covered by the The use and distribution terms for this software are covered by the
Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)

31
Vagrantfile vendored Normal file
View file

@ -0,0 +1,31 @@
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/xenial64"
config.vm.network "private_network", ip: "192.168.33.33"
config.vm.provider "virtualbox" do |vb|
vb.memory = "1024"
end
config.vm.provision "shell", inline: <<-SHELL
set -x
#apt-get update
#apt-get upgrade -y
apt-get install -y openjdk-8-jdk
SHELL
config.vm.provision "shell", privileged: false, inline: <<-SHELL
pwd
mkdir bin
echo "PATH=\$PATH:~/bin" >> .bashrc
cd bin
wget https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein
chmod +x lein
./lein
cd /vagrant
~/bin/lein deps
SHELL
end

View file

@ -1,13 +1,16 @@
Concepts / Language Features Concepts / Language Features
===== =====
new record syntax new record syntax
Agents Agents
Vars Vars
state identity lifetime state identity lifetime
Metadata
immutability / side effects immutability / side effects
type hints type hints
Pre and Post conditions of functions Pre and Post conditions of functions
ex-info/ex-data
reducers (?)
transducers (?) - maybe just some basic ones, nothing too crazy
Particular Functions Particular Functions
===== =====
@ -18,11 +21,9 @@ flatten
frequencies frequencies
reductions reductions
group-by
keep keep
keep-indexed keep-indexed
map-indexed map-indexed
partition-all partition-all
partition-by partition-by
repeatedly repeatedly

View file

@ -1,8 +1,10 @@
(defproject clojure-koans "0.4.8" (defproject clojure-koans "0.5.2-SNAPSHOT"
:description "The Clojure koans." :description "The Clojure koans."
:dependencies [[org.clojure/clojure "1.5.0"] :dependencies [[org.clojure/clojure "1.10.0"]
[koan-engine "0.1.3"]] [koan-engine "0.2.5"]]
:dev-dependencies [[lein-koan "0.1.2"]] :dev-dependencies [[lein-koan "0.1.5"]]
:profiles {:dev {:dependencies [[lein-koan "0.1.2"]]}} :profiles {:dev {:dependencies [[lein-koan "0.1.5"]]}}
:plugins [[lein-koan "0.1.2"]] :repl-options {:init-ns koan-engine.runner
:init ^:displace (do (use '[koan-engine.core]))}
:plugins [[lein-koan "0.1.5"]]
:main koan-engine.runner/exec) :main koan-engine.runner/exec)

View file

@ -1,77 +1,111 @@
[["01_equalities" {"__" [true [["01_equalities" {"__" [true
2 2
7
5 5
4/2 true
false false
6/3 true
true
false
"hello"
"hello"
nil
3]}] 3]}]
["02_lists" {"__" [1 2 3 4 5 ["02_strings" {"__" ["hello"
"world"
"Cool "
"right?"
0
11
false
6 11
"123"
", "
"1" "2" "3"
"olleh"
"hello"
13
nil
"hello world"
true
false
false
"a"
true
true
false]}]
["03_lists" {"__" [1 2 3 4 5
1 1
[2 3 4 5] [2 3 4 5]
3
0
() ()
[:a :b :c :d :e] [:a :b :c :d :e]
[0 :a :b :c :d :e] [:e :a :b :c :d]
:a :a
[:b :c :d :e] [:b :c :d :e]
"No dice!" "No dice!"
()]}] ()]}]
["03_vectors" {"__" [1 ["04_vectors" {"__" [1
[]
[1] [1]
[nil] [nil nil]
2 2
[333] [111 222 333]
:peanut :peanut
:jelly :jelly
:jelly :jelly
[:butter :and] [:butter :and]
3]}] 3]}]
["04_sets" {"__" [nil ["05_sets" {"__" [[3]
3 3
#{1 2 3 4 5} #{1 2 3 4 5}
#{1 2 3 4 5} #{1 2 3 4 5}
#{2 3} #{2 3}
#{1 4}]}] #{1 4}]}]
["05_maps" {"__" [{} ["06_maps" {"__" [:b 2
0
1 1
2 2
2 2
1 1
1 1
"Vancouver" "Sochi"
nil nil
:key-not-found :key-not-found
true true
false false
"February" "February"
1 "January" 1 "January"
2006 2010 2014 :c 3
"Vancouver"]}] 2
2010 2014 2018
"PyeongChang" "Sochi" "Vancouver"
2 3]}]
["06_functions" {"__" [20 ["07_functions" {"__" [81
10 5 20
30 2 10
60
15 15
20 *] "AACC"]
"___" [(fn [f] (f 5)) "___" [+
*
(fn [f] (f 5))
(fn [f] (f 5))]}] (fn [f] (f 5))]}]
["07_conditionals" {"__" [:a ["08_conditionals" {"__" [:a
[] []
nil nil
:glory :glory
4 6 :your-road 4 6 :your-road
'doom 0 1
:cocked-pistol :bicycling
:say-what?]}] "is that even exercise?"]}]
["08_higher_order_functions" {"__" [4 8 12 ["09_higher_order_functions" {"__" [4 8 12
(* x x) (* x x)
[false false true false false] [false false true false false]
() ()
@ -82,29 +116,29 @@
100 100
(count a) (count b)]}] (count a) (count b)]}]
["09_runtime_polymorphism" {"__" [(str (:name a) " eats veggies.") ["10_runtime_polymorphism" {"__" [(str (:name a) " eats veggies.")
(str (:name a) " eats animals.") (str (:name a) " eats animals.")
(str "I don't know what " (:name a) " eats.") (str "I don't know what " (:name a) " eats.")
"Hello World!" "Hello World!"
"Hello, you silly world." "Hello, you silly world."
"Hello to this group: Peter, Paul, Mary!" ]}] "Hello to this group: Peter, Paul, Mary!" ]}]
["10_lazy_sequences" {"__" [[1 2 3 4] ["11_lazy_sequences" {"__" [[1 2 3 4]
[0 1 2 3 4] [0 1 2 3 4]
10 10
95 95
(range 20) [1 2 4 8 16 32 64 128]
:a] :a]
"___" [(fn [x] :foo)]}] "___" [(fn [x] x)]}]
["11_sequence_comprehensions" {"__" [[0 1 2 3 4 5] ["12_sequence_comprehensions" {"__" [[0 1 2 3 4 5]
(* index index) (* x x)
(range 10) (range 10)
(odd? index) (* index index) (odd? x) (* x x)
[row column] [row column]
]}] ]}]
["12_creating_functions" {"__" [true false true ["13_creating_functions" {"__" [true false true
4 4
:a :b :c :d :a :b :c :d
:c :d :c :d
@ -114,7 +148,7 @@
multiply-by-5 multiply-by-5
(comp dec square)]}] (comp dec square)]}]
["13_recursion" {"__" [true ["14_recursion" {"__" [true
acc acc
(loop [coll coll (loop [coll coll
acc ()] acc ()]
@ -128,10 +162,10 @@
(recur (dec n) (* acc n))))] (recur (dec n) (* acc n))))]
"___" [not]}] "___" [not]}]
["14_destructuring" {"__" [":bar:foo" ["15_destructuring" {"__" [":bar:foo"
(format (str "First comes %s, " (format (str "An Oxford comma list of %s, "
"then comes %s, " "%s, "
"then comes %s with the baby carriage") "and %s.")
a b c) a b c)
(apply str (apply str
(interpose " " (interpose " "
@ -150,7 +184,7 @@
street-address ", " city ", " state)) street-address ", " city ", " state))
]}] ]}]
["15_refs" {"__" ["hello" ["16_refs" {"__" ["hello"
"hello" "hello"
"better" "better"
"better!!!" "better!!!"
@ -159,7 +193,7 @@
] ]
"___" [(fn [x] (+ 20 x))]}] "___" [(fn [x] (+ 20 x))]}]
["16_atoms" {"__" [0 ["17_atoms" {"__" [0
1 1
(swap! atomic-clock (partial + 4)) (swap! atomic-clock (partial + 4))
20 20
@ -167,17 +201,15 @@
atomic-clock 20 :fin atomic-clock 20 :fin
]}] ]}]
["17_macros" {"__" [~(first form) ["18_quote" {"__" ['(1 2 3 4 5)
~(nth form 2) (1 2 3 4 5)
form 'age
(drop 2 form) quote
"Hello, Macros!" '(+ 2 3)
10 1 2 3
'(+ 9 1) 1 5]}]
'(* 10 2)
'(+ 10 (2 * 3))]}]
["18_datatypes" {"__" [(print ["19_datatypes" {"__" [(print
(str "You're really the " (str "You're really the "
(.category this) (.category this)
", " recipient "... sorry.")) ", " recipient "... sorry."))
@ -189,18 +221,68 @@
(str "Congratulations on your Best Picture Oscar, " (str "Congratulations on your Best Picture Oscar, "
"Evil Alien Conquerors!")]}] "Evil Alien Conquerors!")]}]
["19_java_interop" {"__" [java.lang.String ["20_java_interop" {"__" [java.lang.String
"SELECT * FROM" "SELECT * FROM"
10 10
1024 1024
] ]
"___" [#(.toUpperCase %) "___" [#(.toUpperCase %)
] ]}]
}]
["20_partition" {"__" [partition ["21_partition" {"__" [partition
[:a :b :c] [:a :b :c]
'((0 1 2) (3 4)) '((0 1 2) (3 4))
5 5
:hello :hello
(6 :this :are) (6 :these :are)
]}]] ]}]
["22_group_by" {"__" [odd?
{5 ["hello" "world"] 3 ["foo" "bar"]}
{1 [{:name "Bob" :id 1}
{:last-name "Smith" :id 1}]
2 [{:name "Jennifer" :id 2}]}
nil
{:naughty-list [{:name "Jimmy" :bad true}
{:name "Joe" :bad true}]
:nice-list [{:name "Jane" :bad false}]}]}]
["23_meta" {"__" [{:league "National League"}
{:division "West"}
"This doesn't implement the IObj interface"
{:foo :bar}
nil
\C
inc
:park "Oracle Park"
'Giants
"Giants"]}]
["24_macros" {"__" [~(first form)
~(nth form 2)
form
(drop 2 form)
"Hello, Macros!"
10
'(+ 9 1)]}]
["25_threading_macros" {"__" [{:a 1}
"Hello world, and moon, and stars"
"String with a trailing space"
6
1
[2 3 4]
12
[1 2 3]]}]
["26_transducers" {"__" ['(2 3 4)
[2 4]
[2 4]
[2 4]
6]}]
["27_multimethods" {"__" ["Hello, World!"
"Hello there"
1
6]}]
]

View file

@ -1,20 +1,18 @@
#!/bin/sh #!/bin/sh
mkdir -p releases mkdir -p releases
lein1 deps
zip -r releases/clojure-koans-`date +"%Y-%m-%d_%H-%M"`.zip \ zip -r releases/clojure-koans-`date +"%Y-%m-%d_%H-%M"`.zip \
. \ . \
-x "./.idea/*" \
-x "./.lein-plugins/*" \
-x "./.git/*" \ -x "./.git/*" \
-x "releases/*" -x "releases/*"
echo echo
echo "Don't forget to upload the zipfile" echo "Don't forget to upload the zipfile (somewhere...)"
echo " to https://github.com/functional-koans/clojure-koans/downloads"
echo `ls -t releases/clojure-koans-*.zip | head -n1` echo `ls -t releases/clojure-koans-*.zip | head -n1`
echo "git push" echo "git push"
echo "git push --tags" echo "git push --tags"
echo echo
# TODO: use http://developer.github.com/v3/repos/downloads/
# to do the upload automatically
# GET example:
# curl https://api.github.com/repos/functional-koans/clojure-koans/downloads

View file

@ -1,3 +1,6 @@
(ns koans.01-equalities
(:require [koan-engine.core :refer :all]))
(meditations (meditations
"We shall contemplate truth by testing reality, via equality" "We shall contemplate truth by testing reality, via equality"
(= __ true) (= __ true)
@ -6,16 +9,31 @@
(= __ (+ 1 1)) (= __ (+ 1 1))
"You can test equality of many things" "You can test equality of many things"
(= (+ 3 4) __ (+ 2 __)) (= (+ 3 4) 7 (+ 2 __))
"Some things may appear different, but be the same" "Some things may appear different, but be the same"
(= 2 2/1 __) (= __ (= 2 2/1))
"You cannot generally float to heavens of integers" "You cannot generally float to heavens of integers"
(= __ (= 2 2.0)) (= __ (= 2 2.0))
"But a looser equality is also possible" "But a looser equality is also possible"
(== 2.0 2 __) (= __ (== 2.0 2))
"Something is not equal to nothing"
(= __ (not (= 1 nil)))
"Strings, and keywords, and symbols: oh my!"
(= __ (= "hello" :hello 'hello))
"Make a keyword with your keyboard"
(= :hello (keyword __))
"Symbolism is all around us"
(= 'hello (symbol __))
"What could be equivalent to nothing?"
(= __ nil)
"When things cannot be equal, they must be different" "When things cannot be equal, they must be different"
(not= :fill-in-the-blank __)) (not= :fill-in-the-blank __))

70
src/koans/02_strings.clj Normal file
View file

@ -0,0 +1,70 @@
(ns koans.02-strings
(:require [koan-engine.core :refer :all]
[clojure.string :as string]))
(meditations
"A string is nothing more than text surrounded by double quotes"
(= __ "hello")
"But double quotes are just magic on top of something deeper"
(= __ (str 'world))
"You can do more than create strings, you can put them together"
(= "Cool right?" (str __ __))
"You can even get certain characters"
(= \C (get "Characters" __))
"Or even count the characters"
(= __ (count "Hello World"))
"But strings and characters are not the same"
(= __ (= \c "c"))
"What if you only wanted to get part of a string?"
(= "World" (subs "Hello World" __ __))
"How about joining together elements in a list?"
(= __ (string/join '(1 2 3)))
"What if you wanted to separate them out?"
(= "1, 2, 3" (string/join __ '(1 2 3)))
"Maybe you want to separate out all your lines"
(= [__ __ __] (string/split-lines "1\n2\n3"))
"You may want to make sure your words are backwards"
(= __ (string/reverse "hello"))
"Maybe you want to find the index of the first occurrence of a substring"
(= 0 (string/index-of "hello world" __))
"Or maybe the last index of the same substring"
(= __ (string/last-index-of "hello world, hello" "hello"))
"But when something doesn't exist, nothing is found"
(= __ (string/index-of "hello world" "bob"))
"Sometimes you don't want whitespace cluttering the front and back"
(= __ (string/trim " \nhello world \t \n"))
"You can check if something is a char"
(= __ (char? \c))
"But it may not be"
(= __ (char? "a"))
"But chars aren't strings"
(= __ (string? \b))
"Strings are strings"
(= true (string? __))
"Some strings may be blank"
(= __ (string/blank? ""))
"Even if at first glance they aren't"
(= __ (string/blank? " \n \t "))
"However, most strings aren't blank"
(= __ (string/blank? "hello?\nare you out there?")))

View file

@ -1,3 +1,6 @@
(ns koans.03-lists
(:require [koan-engine.core :refer :all]))
(meditations (meditations
"Lists can be expressed by function or a quoted form" "Lists can be expressed by function or a quoted form"
(= '(__ __ __ __ __) (list 1 2 3 4 5)) (= '(__ __ __ __ __) (list 1 2 3 4 5))
@ -8,14 +11,20 @@
"As well as the rest" "As well as the rest"
(= __ (rest '(1 2 3 4 5))) (= __ (rest '(1 2 3 4 5)))
"The rest when nothing is left is empty" "Count your blessings"
(= __ (count '(dracula dooku chocula)))
"Before they are gone"
(= __ (count '()))
"The rest, when nothing is left, is empty"
(= __ (rest '(100))) (= __ (rest '(100)))
"And construction by adding an element to the front is simple" "Construction by adding an element to the front is easy"
(= __ (cons :a '(:b :c :d :e))) (= __ (cons :a '(:b :c :d :e)))
"Conjoining an element to a list can be done in the reverse order" "Conjoining an element to a list isn't hard either"
(= __ (conj '(:a :b :c :d :e) 0)) (= __ (conj '(:a :b :c :d) :e))
"You can use a list like a stack to get the first element" "You can use a list like a stack to get the first element"
(= __ (peek '(:a :b :c :d :e))) (= __ (peek '(:a :b :c :d :e)))
@ -26,9 +35,11 @@
"But watch out if you try to pop nothing" "But watch out if you try to pop nothing"
(= __ (try (= __ (try
(pop '()) (pop '())
(catch IllegalStateException e "No dice!"))) (catch IllegalStateException e
"No dice!")))
"The rest of nothing isn't so strict" "The rest of nothing isn't so strict"
(= __ (try (= __ (try
(rest '()) (rest '())
(catch IllegalStateException e "No dice!")))) (catch IllegalStateException e
"No dice!"))))

View file

@ -1,18 +0,0 @@
(meditations
"You can create a set in two ways"
(= #{} (set __))
"They are another important data structure in clojure"
(= __ (count #{1 2 3}))
"Remember that a set is a 'set'"
(= __ (set '(1 1 2 2 3 3 4 4 5 5)))
"You can ask clojure for the union of two sets"
(= __ (clojure.set/union #{1 2 3 4} #{2 3 5}))
"And also the intersection"
(= __ (clojure.set/intersection #{1 2 3 4} #{2 3 5}))
"But don't forget about the difference"
(= __ (clojure.set/difference #{1 2 3 4 5} #{2 3 5})))

View file

@ -1,21 +1,21 @@
(ns koans.04-vectors
(:require [koan-engine.core :refer :all]))
(meditations (meditations
"You can use vectors in clojure to create an 'Array' like structure" "You can use vectors in clojure as array-like structures"
(= __ (count [42])) (= __ (count [42]))
"You can create a vector in several ways" "You can create a vector from a list"
(= __ (vec nil))
"And populate it in either of these ways"
(= __ (vec '(1))) (= __ (vec '(1)))
"There is another way as well" "Or from some elements"
(= __ (vector nil)) (= __ (vector nil nil))
"But you can populate it with any number of elements at once" "But you can populate it with any number of elements at once"
(= [1 __] (vec '(1 2))) (= [1 __] (vec '(1 2)))
"And add to it as well" "Conjoining to a vector is different than to a list"
(= __ (conj (vec nil) 333)) (= __ (conj [111 222] 333))
"You can get the first element of a vector like so" "You can get the first element of a vector like so"
(= __ (first [:peanut :butter :and :jelly])) (= __ (first [:peanut :butter :and :jelly]))

View file

@ -1,50 +0,0 @@
(meditations
"There are two ways to create maps"
(= __ (hash-map))
"Maps in clojure associate keys with values"
(= __ (count (hash-map)))
"A value must be supplied for each key"
(= {:a 1} (hash-map :a __))
"The size is the number of entries"
(= __ (count {:a 1 :b 2}))
"You can look up the value for a given key"
(= __ (get {:a 1 :b 2} :b))
"Maps can be used as lookup functions"
(= __ ({:a 1 :b 2} :a))
"And so can keywords"
(= __ (:a {:a 1 :b 2}))
"But map keys need not be keywords"
(= __ ({2006 "Torino" 2010 "Vancouver" 2014 "Sochi"} 2010))
"You may not be able to find an entry for a key"
(= __ (get {:a 1 :b 2} :c))
"But you can provide your own default"
(= __ (get {:a 1 :b 2} :c :key-not-found))
"You can find out if a key is present"
(= __ (contains? {:a nil :b nil} :b))
"Or if it is missing"
(= __ (contains? {:a nil :b nil} :c))
"Maps are immutable, but you can create a new, 'changed' version"
(= {1 "January" 2 __} (assoc {1 "January" } 2 "February"))
"You can also 'remove' an entry"
(= {__ __} (dissoc {1 "January" 2 "February"} 2))
"Often you will need to get the keys (which will be in hash order)"
(= (list __ __ __)
(sort (keys {2006 "Torino" 2010 "Vancouver" 2014 "Sochi"})))
"Or the values"
(= (list "Sochi" "Torino" __)
(sort (vals {2006 "Torino" 2010 "Vancouver" 2014 "Sochi"}))))

22
src/koans/05_sets.clj Normal file
View file

@ -0,0 +1,22 @@
(ns koans.05-sets
(:require [koan-engine.core :refer :all]
[clojure.set :as set]))
(meditations
"You can create a set by converting another collection"
(= #{3} (set __))
"Counting them is like counting other collections"
(= __ (count #{1 2 3}))
"Remember that a set is a *mathematical* set"
(= __ (set '(1 1 2 2 3 3 4 4 5 5)))
"You can ask Clojure for the union of two sets"
(= __ (set/union #{1 2 3 4} #{2 3 5}))
"And also the intersection"
(= __ (set/intersection #{1 2 3 4} #{2 3 5}))
"But don't forget about the difference"
(= __ (set/difference #{1 2 3 4 5} #{2 3 5})))

View file

@ -1,29 +0,0 @@
(defn multiply-by-ten [n]
(* 10 n))
(defn square [n] (* n n))
(meditations
"Functions are often defined before they are used"
(= __ (multiply-by-ten 2))
"But they can also be defined inline"
(= __ ((fn [n] (* __ n)) 2))
"Or using even shorter syntax"
(= __ (#(* 15 %) __))
"Short anonymous functions may take multiple arguments"
(= __ (#(+ %1 %2 %3) 4 5 6))
"One function can beget another"
(= __ ((fn []
((fn [a b] (__ a b))
4 5))))
"Higher-order functions take function arguments"
(= 25 (___
(fn [n] (* n n))))
"But they are often better written using the names of functions"
(= 25 (___ square)))

63
src/koans/06_maps.clj Normal file
View file

@ -0,0 +1,63 @@
(ns koans.06-maps
(:require [koan-engine.core :refer :all]))
(meditations
"Don't get lost when creating a map"
(= {:a 1 :b 2} (hash-map :a 1 __ __))
"A value must be supplied for each key"
(= {:a 1} (hash-map :a __))
"The size is the number of entries"
(= __ (count {:a 1 :b 2}))
"You can look up the value for a given key"
(= __ (get {:a 1 :b 2} :b))
"Maps can be used as functions to do lookups"
(= __ ({:a 1 :b 2} :a))
"And so can keywords"
(= __ (:a {:a 1 :b 2}))
"But map keys need not be keywords"
(= __ ({2010 "Vancouver" 2014 "Sochi" 2018 "PyeongChang"} 2014))
"You may not be able to find an entry for a key"
(= __ (get {:a 1 :b 2} :c))
"But you can provide your own default"
(= __ (get {:a 1 :b 2} :c :key-not-found))
"You can find out if a key is present"
(= __ (contains? {:a nil :b nil} :b))
"Or if it is missing"
(= __ (contains? {:a nil :b nil} :c))
"Maps are immutable, but you can create a new and improved version"
(= {1 "January" 2 __} (assoc {1 "January"} 2 "February"))
"You can also create a new version with an entry removed"
(= {__ __} (dissoc {1 "January" 2 "February"} 2))
"Create a new map by merging"
(= {:a 1 :b 2 __ __} (merge {:a 1 :b 2} {:c 3}))
"Specify how to handle entries with same keys when merging"
(= {:a 1 :b __ :c 3} (merge-with + {:a 1 :b 1} {:b 1 :c 3}))
"Often you will need to get the keys, but the order is undependable"
(= (list __ __ __)
(sort (keys { 2014 "Sochi" 2018 "PyeongChang" 2010 "Vancouver"})))
"You can get the values in a similar way"
(= (list __ __ __)
(sort (vals {2010 "Vancouver" 2014 "Sochi" 2018 "PyeongChang"})))
"You can even iterate over the map entries as a seq"
(= {:a __ :b __}
(into {}
(map
(fn [[k v]] [k (inc v)])
{:a 1 :b 2}))))

View file

@ -0,0 +1,40 @@
(ns koans.07-functions
(:require [koan-engine.core :refer :all]))
(defn multiply-by-ten [n]
(* 10 n))
(defn square [n] (* n n))
(meditations
"Calling a function is like giving it a hug with parentheses"
(= __ (square 9))
"Functions are usually defined before they are used"
(= __ (multiply-by-ten 2))
"But they can also be defined inline"
(= __ ((fn [n] (* 5 n)) 2))
"Or using an even shorter syntax"
(= __ (#(* 15 %) 4))
"Even anonymous functions may take multiple arguments"
(= __ (#(+ %1 %2 %3) 4 5 6))
"Arguments can also be skipped"
(= __ (#(str "AA" %2) "bb" "CC"))
"One function can beget another"
(= 9 (((fn [] ___)) 4 5))
"Functions can also take other functions as input"
(= 20 ((fn [f] (f 4 5))
___))
"Higher-order functions take function arguments"
(= 25 (___
(fn [n] (* n n))))
"But they are often better written using the names of functions"
(= 25 (___ square)))

View file

@ -1,11 +1,12 @@
(defn explain-defcon-level [exercise-term] (ns koans.08-conditionals
(:require [koan-engine.core :refer :all]))
(defn explain-exercise-velocity [exercise-term]
(case exercise-term (case exercise-term
:fade-out :you-and-what-army :bicycling "pretty fast"
:double-take :call-me-when-its-important :jogging "not super fast"
:round-house :o-rly :walking "not fast at all"
:fast-pace :thats-pretty-bad "is that even exercise?"))
:cocked-pistol :sirens
:say-what?))
(meditations (meditations
"You will face many decisions" "You will face many decisions"
@ -33,14 +34,14 @@
:else __))) :else __)))
"Or your fate may be sealed" "Or your fate may be sealed"
(= __ (if-not (zero? __) (= 'doom (if-not (zero? __)
'doom 'doom
'doom)) 'more-doom))
"In case of emergency, sound the alarms" "In case of emergency, go fast"
(= :sirens (= "pretty fast"
(explain-defcon-level __)) (explain-exercise-velocity __))
"But admit it when you don't know what to do" "But admit it when you don't know what to do"
(= __ (= __
(explain-defcon-level :yo-mama))) (explain-exercise-velocity :watching-tv)))

View file

@ -1,3 +1,6 @@
(ns koans.09-higher-order-functions
(:require [koan-engine.core :refer :all]))
(meditations (meditations
"The map function relates a sequence to another" "The map function relates a sequence to another"
(= [__ __ __] (map (fn [x] (* 4 x)) [1 2 3])) (= [__ __ __] (map (fn [x] (* 4 x)) [1 2 3]))

View file

@ -1,9 +1,12 @@
(ns koans.10-runtime-polymorphism
(:require [koan-engine.core :refer :all]))
(defn hello (defn hello
([] "Hello World!") ([] "Hello World!")
([a] (str "Hello, you silly " a ".")) ([a] (str "Hello, you silly " a "."))
([a & more] (str "Hello to this group: " ([a & more] (str "Hello to this group: "
(apply str (apply str
(interpose ", " (concat (list a) more))) (interpose ", " (cons a more)))
"!"))) "!")))
(defmulti diet (fn [x] (:eater x))) (defmulti diet (fn [x] (:eater x)))
@ -26,6 +29,10 @@
(= "Bambi eats veggies." (= "Bambi eats veggies."
(diet {:species "deer" :name "Bambi" :age 1 :eater :herbivore})) (diet {:species "deer" :name "Bambi" :age 1 :eater :herbivore}))
"Animals have different names"
(= "Thumper eats veggies."
(diet {:species "rabbit" :name "Thumper" :age 1 :eater :herbivore}))
"Different methods are used depending on the dispatch function result" "Different methods are used depending on the dispatch function result"
(= "Simba eats animals." (= "Simba eats animals."
(diet {:species "lion" :name "Simba" :age 1 :eater :carnivore})) (diet {:species "lion" :name "Simba" :age 1 :eater :carnivore}))

View file

@ -1,3 +1,6 @@
(ns koans.11-lazy-sequences
(:require [koan-engine.core :refer :all]))
(meditations (meditations
"There are many ways to generate a sequence" "There are many ways to generate a sequence"
(= __ (range 1 5)) (= __ (range 1 5))
@ -14,12 +17,12 @@
(drop __ (range 100))) (drop __ (range 100)))
"Iteration provides an infinite lazy sequence" "Iteration provides an infinite lazy sequence"
(= __ (take 20 (iterate inc 0))) (= __ (take 8 (iterate (fn [x] (* x 2)) 1)))
"Repetition is key" "Repetition is key"
(= [:a :a :a :a :a :a :a :a :a :a] (= [:a :a :a :a :a :a :a :a :a :a]
(repeat 10 __)) (repeat 10 __))
"Iteration can be used for repetition" "Iteration can be used for repetition"
(= (repeat 100 :foo) (= (repeat 100 "hello")
(take 100 (iterate ___ :foo)))) (take 100 (iterate ___ "hello"))))

View file

@ -1,27 +1,30 @@
(ns koans.12-sequence-comprehensions
(:require [koan-engine.core :refer :all]))
(meditations (meditations
"Sequence comprehensions can bind each element in turn to a symbol" "Sequence comprehensions can bind each element in turn to a symbol"
(= __ (= __
(for [index (range 6)] (for [x (range 6)]
index)) x))
"They can easily emulate mapping" "They can easily emulate mapping"
(= '(0 1 4 9 16 25) (= '(0 1 4 9 16 25)
(map (fn [index] (* index index)) (map (fn [x] (* x x))
(range 6)) (range 6))
(for [index (range 6)] (for [x (range 6)]
__)) __))
"And also filtering" "And also filtering"
(= '(1 3 5 7 9) (= '(1 3 5 7 9)
(filter odd? (range 10)) (filter odd? (range 10))
(for [index __ :when (odd? index)] (for [x __ :when (odd? x)]
index)) x))
"Combinations these transformations is trivial" "Combinations of these transformations are trivial"
(= '(1 9 25 49 81) (= '(1 9 25 49 81)
(map (fn [index] (* index index)) (map (fn [x] (* x x))
(filter odd? (range 10))) (filter odd? (range 10)))
(for [index (range 10) :when __] (for [x (range 10) :when __]
__)) __))
"More complex transformations simply take multiple binding forms" "More complex transformations simply take multiple binding forms"

View file

@ -1,3 +1,6 @@
(ns koans.13-creating-functions
(:require [koan-engine.core :refer :all]))
(defn square [x] (* x x)) (defn square [x] (* x x))
(meditations (meditations

View file

@ -1,3 +1,6 @@
(ns koans.14-recursion
(:require [koan-engine.core :refer :all]))
(defn is-even? [n] (defn is-even? [n]
(if (= n 0) (if (= n 0)
__ __
@ -29,13 +32,13 @@
"Reversing directions is easy when you have not gone far" "Reversing directions is easy when you have not gone far"
(= '(1) (recursive-reverse [1])) (= '(1) (recursive-reverse [1]))
"Yet more difficult the more steps you take" "Yet it becomes more difficult the more steps you take"
(= '(5 4 3 2 1) (recursive-reverse [1 2 3 4 5])) (= '(6 5 4 3 2) (recursive-reverse [2 3 4 5 6]))
"Simple things may appear simple." "Simple things may appear simple"
(= 1 (factorial 1)) (= 1 (factorial 1))
"They may require other simple steps." "They may require other simple steps"
(= 2 (factorial 2)) (= 2 (factorial 2))
"Sometimes a slightly bigger step is necessary" "Sometimes a slightly bigger step is necessary"

View file

@ -1,3 +1,6 @@
(ns koans.15-destructuring
(:require [koan-engine.core :refer :all]))
(def test-address (def test-address
{:street-address "123 Test Lane" {:street-address "123 Test Lane"
:city "Testerville" :city "Testerville"
@ -9,24 +12,24 @@
[:foo :bar])) [:foo :bar]))
"Whether in function definitions" "Whether in function definitions"
(= (str "First comes love, " (= (str "An Oxford comma list of apples, "
"then comes marriage, " "oranges, "
"then comes Clojure with the baby carriage") "and pears.")
((fn [[a b c]] __) ((fn [[a b c]] __)
["love" "marriage" "Clojure"])) ["apples" "oranges" "pears"]))
"Or in let expressions" "Or in let expressions"
(= "Rich Hickey aka The Clojurer aka Go Time aka Macro Killah" (= "Rich Hickey aka The Clojurer aka Go Time aka Lambda Guru"
(let [[first-name last-name & aliases] (let [[first-name last-name & aliases]
(list "Rich" "Hickey" "The Clojurer" "Go Time" "Macro Killah")] (list "Rich" "Hickey" "The Clojurer" "Go Time" "Lambda Guru")]
__)) __))
"You can regain the full argument if you like arguing" "You can regain the full argument if you like arguing"
(= {:original-parts ["Steven" "Hawking"] :named-parts {:first "Steven" :last "Hawking"}} (= {:original-parts ["Stephen" "Hawking"] :named-parts {:first "Stephen" :last "Hawking"}}
(let [[first-name last-name :as full-name] ["Steven" "Hawking"]] (let [[first-name last-name :as full-name] ["Stephen" "Hawking"]]
__)) __))
"Break up maps by key" "Break up maps by keys"
(= "123 Test Lane, Testerville, TX" (= "123 Test Lane, Testerville, TX"
(let [{street-address :street-address, city :city, state :state} test-address] (let [{street-address :street-address, city :city, state :state} test-address]
__)) __))

View file

@ -1,3 +1,6 @@
(ns koans.16-refs
(:require [koan-engine.core :refer :all]))
(def the-world (ref "hello")) (def the-world (ref "hello"))
(def bizarro-world (ref {})) (def bizarro-world (ref {}))

View file

@ -1,3 +1,6 @@
(ns koans.17-atoms
(:require [koan-engine.core :refer :all]))
(def atomic-clock (atom 0)) (def atomic-clock (atom 0))
(meditations (meditations
@ -24,7 +27,7 @@
(compare-and-set! atomic-clock 100 :fin) (compare-and-set! atomic-clock 100 :fin)
@atomic-clock)) @atomic-clock))
"When your expectations are aligned with reality things, proceed that way" "When your expectations are aligned with reality, things proceed that way"
(= :fin (do (= :fin (do
(compare-and-set! __ __ __) (compare-and-set! __ __ __)
@atomic-clock))) @atomic-clock)))

25
src/koans/18_quote.clj Normal file
View file

@ -0,0 +1,25 @@
(ns koans.18-quote
(:require [koan-engine.core :refer :all]))
(meditations
"Wrap a quote around a list to suppress evaluation"
(= (quote (1 2 3 4 5)) __)
"There is a shortcut too!"
(= (quote __) '(1 2 3 4 5))
"You can quote symbols as well as lists... without evaluation!"
(= __ (let [age 9] (quote age)))
"You can use a literal list as a data collection without having Clojure try to call a function"
(= (cons 1 (__ (2 3))) (list 1 2 3) (cons 1 [2 3]))
"The quote affects all of its arguments, not just the top level"
(= (list 1 __) '(1 (+ 2 3)))
"Syntax-quote (`) acts similarly to the normal quote"
(= (list __ __ __) `(1 2 3) '(1 2 3))
"Unquote (~) within a syntax-quoted expression lets you mark specific expressions as requiring evaluation"
(= (list __ __) `(1 ~(+ 2 3)) '(1 5)))

View file

@ -1,3 +1,6 @@
(ns koans.19-datatypes
(:require [koan-engine.core :refer :all]))
(defrecord Nobel [prize]) (defrecord Nobel [prize])
(deftype Pulitzer [prize]) (deftype Pulitzer [prize])

View file

@ -1,3 +1,6 @@
(ns koans.20-java-interop
(:require [koan-engine.core :refer :all]))
(meditations (meditations
"You may have done more with Java than you know" "You may have done more with Java than you know"
(= __ (class "warfare")) ; hint: try typing (javadoc "warfare") in the REPL (= __ (class "warfare")) ; hint: try typing (javadoc "warfare") in the REPL

View file

@ -1,3 +1,6 @@
(ns koans.21-partition
(:require [koan-engine.core :refer :all]))
(meditations (meditations
"To split a collection you can use the partition function" "To split a collection you can use the partition function"
(= '((0 1) (2 3)) (__ 2 (range 4))) (= '((0 1) (2 3)) (__ 2 (range 4)))
@ -5,14 +8,14 @@
"But watch out if there are not enough elements to form n sequences" "But watch out if there are not enough elements to form n sequences"
(= '(__) (partition 3 [:a :b :c :d :e])) (= '(__) (partition 3 [:a :b :c :d :e]))
"You can use partition-all to also get partitions with less then n elements" "You can use partition-all to include any leftovers too"
(= __ (partition-all 3 (range 5))) (= __ (partition-all 3 (range 5)))
"If you need to, you can start each sequence with an offset" "If you need to, you can start each sequence with an offset"
(= '((0 1 2) (5 6 7) (10 11 12)) (partition 3 __ (range 13))) (= '((0 1 2) (5 6 7) (10 11 12)) (partition 3 __ (range 13)))
"Consider padding the last sequence with some default values.." "Consider padding the last sequence with some default values"
(= '((0 1 2) (3 4 5) (6 :hello)) (partition 3 3 [__] (range 7))) (= '((0 1 2) (3 4 5) (6 :hello)) (partition 3 3 [__] (range 7)))
".. but notice that they will only pad up to given sequence length" "But notice that they will only pad up to the given sequence length"
(= '((0 1 2) (3 4 5) __) (partition 3 3 [:this :are "my" "words"] (range 7)))) (= '((0 1 2) (3 4 5) __) (partition 3 3 [:these :are "my" "words"] (range 7))))

36
src/koans/22_group_by.clj Normal file
View file

@ -0,0 +1,36 @@
(ns koans.22-group-by
(:require [koan-engine.core :refer :all]))
(defn get-odds-and-evens [coll]
(let [{odds true evens false} (group-by __ coll)]
[odds evens]))
(meditations
"To categorize a collection by some function, use group-by"
(= __ (group-by count ["hello" "world" "foo" "bar"]))
"You can simulate filter + remove in one pass"
(= (get-odds-and-evens [1 2 3 4 5])
((juxt filter remove) odd? [1 2 3 4 5])
[[1 3 5] [2 4]])
"You can also group by a primary key"
(= __
(group-by :id [{:id 1 :name "Bob"}
{:id 2 :name "Jennifer"}
{:id 1 :last-name "Smith"} ]))
"But be careful when you group by a non-required key"
(= {"Bob" [{:name "Bob" :id 1}]
"Jennifer" [{:name "Jennifer" :id 2}]
__ [{:last-name "Smith" :id 1}]}
(group-by :name [{:id 1 :name "Bob"}
{:id 2 :name "Jennifer"}
{:id 1 :last-name "Smith"}]))
"The true power of group-by comes with custom functions"
(= __
(group-by #(if (:bad %) :naughty-list :nice-list)
[{:name "Jimmy" :bad true}
{:name "Jane" :bad false}
{:name "Joe" :bad true}])))

51
src/koans/23_meta.clj Normal file
View file

@ -0,0 +1,51 @@
(ns koans.23-meta
(:require [koan-engine.core :refer :all]))
(def giants
(with-meta 'Giants
{:league "National League"}))
(meditations
"Some objects can be tagged using the with-meta function"
(= __ (meta giants))
"Or more succinctly with a reader macro"
(= __ (meta '^{:division "West"} Giants))
"While others can't"
(= __ (try
(with-meta
2
{:prime true})
(catch ClassCastException e
"This doesn't implement the IObj interface")))
"Notice when metadata carries over"
(= __ (meta (merge '^{:foo :bar} {:a 1 :b 2}
{:b 3 :c 4})))
"And when it doesn't"
(= __ (meta (merge {:a 1 :b 2}
'^{:foo :bar} {:b 3 :c 4})))
"Metadata can be used as a type hint to avoid reflection during runtime"
(= __ (#(.charAt ^String % 0) "Cast me"))
"You can directly update an object's metadata"
(= 8 (let [giants
(with-meta
'Giants
{:world-series-titles (atom 7)})]
(swap! (:world-series-titles (meta giants)) __)
@(:world-series-titles (meta giants))))
"You can also create a new object from another object with metadata"
(= {:league "National League" :park "Oracle Park"}
(meta (vary-meta giants
assoc __ __)))
"But it won't affect behavior like equality"
(= __ (vary-meta giants dissoc :league))
"Or the object's printed representation"
(= __ (pr-str (vary-meta giants dissoc :league))))

View file

@ -1,26 +1,29 @@
(ns koans.24-macros
(:require [koan-engine.core :refer :all]))
(defmacro hello [x] (defmacro hello [x]
(str "Hello, " x)) (str "Hello, " x))
(defmacro infix [form] (defmacro infix [form]
(list (second form) (first form) (nth form 2))) (list (second form) (first form) (nth form 2)))
(defmacro infix-better [form] (defmacro infix-concise [form]
`(~(second form) ; Note the syntax-quote (`) and unquote (~) characters! `(~(second form) ; Note the syntax-quote (`) and unquote (~) characters!
__ __
__)) __))
(defmacro r-infix [form] (defmacro recursive-infix [form]
(cond (not (seq? form)) (cond (not (seq? form))
__ __
(= 1 (count form)) (= 1 (count form))
`(r-infix ~(first form)) `(recursive-infix ~(first form))
:else :else
(let [operator (second form) (let [operator (second form)
first-arg (first form) first-arg (first form)
others __] others __]
`(~operator `(~operator
(r-infix ~first-arg) (recursive-infix ~first-arg)
(r-infix ~others))))) (recursive-infix ~others)))))
(meditations (meditations
"Macros are like functions created at compile time" "Macros are like functions created at compile time"
@ -33,10 +36,10 @@
(= __ (macroexpand '(infix (9 + 1)))) (= __ (macroexpand '(infix (9 + 1))))
"You can do better than that - hand crafting FTW!" "You can do better than that - hand crafting FTW!"
(= __ (macroexpand '(infix-better (10 * 2)))) (= '(* 10 2) (macroexpand '(infix-concise (10 * 2))))
"Things don't always work as you would like them to... " "Things don't always work as you would like them to"
(= __ (macroexpand '(infix-better ( 10 + (2 * 3))))) (= '(+ 10 (2 * 3)) (macroexpand '(infix-concise (10 + (2 * 3)))))
"Really, you don't understand recursion until you understand recursion" "Really, you don't understand recursion until you understand recursion"
(= 36 (r-infix (10 + (2 * 3) + (4 * 5))))) (= 36 (recursive-infix (10 + (2 * 3) + (4 * 5)))))

View file

@ -0,0 +1,66 @@
(ns koans.25-threading-macros
(:require [koan-engine.core :refer :all]))
(def a-list
'(1 2 3 4 5))
(def a-list-with-maps
'({:a 1} {:a 2} {:a 3}))
(defn function-that-takes-a-map [map a b]
(get map :a))
(defn function-that-takes-a-coll [a b coll]
(map :a coll))
(meditations
"We can use thread first for more readable sequential operations"
(= __
(-> {}
(assoc :a 1)))
"Consider also the case of strings"
(= __
(-> "Hello world"
(str ", and moon")
(str ", and stars")))
"When a function has no arguments to partially apply, just reference it"
(= __
(-> "String with a trailing space "
clojure.string/trim))
"Most operations that take a scalar value as an argument can be threaded-first"
(= __
(-> {}
(assoc :a 1)
(assoc :b 2)
(assoc :c {:d 4
:e 5})
(update-in [:c :e] inc)
(get-in [:c :e])))
"We can use functions we have written ourselves that follow this pattern"
(= __
(-> {}
(assoc :a 1)
(function-that-takes-a-map "hello" "there")))
"We can also thread last using ->>"
(= __
(->> [1 2 3]
(map inc)))
"Most operations that take a collection can be threaded-last"
(= __
(->> a-list
(map inc)
(filter even?)
(into [])
(reduce +)))
"We can use functions we have written ourselves that follow this pattern"
(= __
(->> a-list-with-maps
(function-that-takes-a-coll "hello" "there")
(into []))))

View file

@ -0,0 +1,30 @@
(ns koans.26-transducers
(:require [koan-engine.core :refer :all]))
(def example-transducer
(map inc))
(def transforms
(comp (map inc)
(filter even?)))
(meditations
"A sequence operation with only one argument often returns a transducer"
(= __
(sequence example-transducer [1 2 3]))
"Consider that sequence operations can be composed as transducers"
(= __
(transduce transforms conj [1 2 3]))
"We can do this eagerly"
(= __
(into [] transforms [1 2 3]))
"Or lazily"
(= __
(sequence transforms [1 2 3]))
"The transduce function can combine mapping and reduction"
(= __
(transduce transforms + [1 2 3])))

View file

@ -0,0 +1,44 @@
(ns koans.27-multimethods
(:require [koan-engine.core :refer :all]))
(defmulti multimethod-without-args
(fn [keyword-arg] keyword-arg))
(defmethod multimethod-without-args :first [_]
(str "Hello, World!"))
(defmethod multimethod-without-args :second [_]
(str "Hello there"))
(defmulti multimethod-with-args
(fn [opt-one opt-two] opt-one))
(defmethod multimethod-with-args :path-one [_ opts]
(:first-opt opts))
(defmethod multimethod-with-args :path-two [_ opts]
(let [numbers (:second-opt opts)]
(->> numbers
(map inc)
(reduce +))))
(defmethod multimethod-with-args :path-three [_])
(meditations
"A multimethod takes one or more arguments to dispatch on"
(= __
(multimethod-without-args :first))
"Though it can be ignored and represented by _ in defmethods"
(= __
(multimethod-without-args :second))
"Alternatively, we can use the arguments in defmethods"
(= __
(multimethod-with-args :path-one {:first-opt 1
:second-opt 2}))
"This allows us to do something different in each method implementation"
(= __
(multimethod-with-args :path-two {:first-opt 1
:second-opt [0 1 2]})))