pulled in lisp koans for solving

https://github.com/google/lisp-koans
This commit is contained in:
efim 2022-07-24 12:13:42 +00:00
parent fe0950582d
commit 49c00c24ee
104 changed files with 8515 additions and 1 deletions

View File

@ -7,7 +7,7 @@
let pkgs = nixpkgs.legacyPackages.${system}; in let pkgs = nixpkgs.legacyPackages.${system}; in
{ {
devShells.default = pkgs.mkShell { devShells.default = pkgs.mkShell {
buildInputs = [ pkgs.sbcl ]; buildInputs = [ pkgs.sbcl pkgs.inotify-tools ];
}; };
} }
); );

1
lisp-koans/.git_bak/HEAD Normal file
View File

@ -0,0 +1 @@
ref: refs/heads/master

View File

@ -0,0 +1,11 @@
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[remote "origin"]
url = https://github.com/google/lisp-koans.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master

View File

@ -0,0 +1 @@
Unnamed repository; edit this file 'description' to name the repository.

View File

@ -0,0 +1,15 @@
#!/nix/store/3j18grljsyy4nxc078g00sy4cx6cf16g-bash-5.1-p16/bin/bash
#
# An example hook script to check the commit log message taken by
# applypatch from an e-mail message.
#
# The hook should exit with non-zero status after issuing an
# appropriate message if it wants to stop the commit. The hook is
# allowed to edit the commit message file.
#
# To enable this hook, rename this file to "applypatch-msg".
. git-sh-setup
commitmsg="$(git rev-parse --git-path hooks/commit-msg)"
test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"}
:

View File

@ -0,0 +1,24 @@
#!/nix/store/3j18grljsyy4nxc078g00sy4cx6cf16g-bash-5.1-p16/bin/bash
#
# An example hook script to check the commit log message.
# Called by "git commit" with one argument, the name of the file
# that has the commit message. The hook should exit with non-zero
# status after issuing an appropriate message if it wants to stop the
# commit. The hook is allowed to edit the commit message file.
#
# To enable this hook, rename this file to "commit-msg".
# Uncomment the below to add a Signed-off-by line to the message.
# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
# hook is more suited to it.
#
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
# This example catches duplicate Signed-off-by lines.
test "" = "$(grep '^Signed-off-by: ' "$1" |
sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
echo >&2 Duplicate Signed-off-by lines.
exit 1
}

View File

@ -0,0 +1,173 @@
#!/nix/store/c9d6q19a057kdh7gwpnnwlz79d8q6vx7-perl-5.34.1/bin/perl
use strict;
use warnings;
use IPC::Open2;
# An example hook script to integrate Watchman
# (https://facebook.github.io/watchman/) with git to speed up detecting
# new and modified files.
#
# The hook is passed a version (currently 2) and last update token
# formatted as a string and outputs to stdout a new update token and
# all files that have been modified since the update token. Paths must
# be relative to the root of the working tree and separated by a single NUL.
#
# To enable this hook, rename this file to "query-watchman" and set
# 'git config core.fsmonitor .git/hooks/query-watchman'
#
my ($version, $last_update_token) = @ARGV;
# Uncomment for debugging
# print STDERR "$0 $version $last_update_token\n";
# Check the hook interface version
if ($version ne 2) {
die "Unsupported query-fsmonitor hook version '$version'.\n" .
"Falling back to scanning...\n";
}
my $git_work_tree = get_working_dir();
my $retry = 1;
my $json_pkg;
eval {
require JSON::XS;
$json_pkg = "JSON::XS";
1;
} or do {
require JSON::PP;
$json_pkg = "JSON::PP";
};
launch_watchman();
sub launch_watchman {
my $o = watchman_query();
if (is_work_tree_watched($o)) {
output_result($o->{clock}, @{$o->{files}});
}
}
sub output_result {
my ($clockid, @files) = @_;
# Uncomment for debugging watchman output
# open (my $fh, ">", ".git/watchman-output.out");
# binmode $fh, ":utf8";
# print $fh "$clockid\n@files\n";
# close $fh;
binmode STDOUT, ":utf8";
print $clockid;
print "\0";
local $, = "\0";
print @files;
}
sub watchman_clock {
my $response = qx/watchman clock "$git_work_tree"/;
die "Failed to get clock id on '$git_work_tree'.\n" .
"Falling back to scanning...\n" if $? != 0;
return $json_pkg->new->utf8->decode($response);
}
sub watchman_query {
my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j --no-pretty')
or die "open2() failed: $!\n" .
"Falling back to scanning...\n";
# In the query expression below we're asking for names of files that
# changed since $last_update_token but not from the .git folder.
#
# To accomplish this, we're using the "since" generator to use the
# recency index to select candidate nodes and "fields" to limit the
# output to file names only. Then we're using the "expression" term to
# further constrain the results.
if (substr($last_update_token, 0, 1) eq "c") {
$last_update_token = "\"$last_update_token\"";
}
my $query = <<" END";
["query", "$git_work_tree", {
"since": $last_update_token,
"fields": ["name"],
"expression": ["not", ["dirname", ".git"]]
}]
END
# Uncomment for debugging the watchman query
# open (my $fh, ">", ".git/watchman-query.json");
# print $fh $query;
# close $fh;
print CHLD_IN $query;
close CHLD_IN;
my $response = do {local $/; <CHLD_OUT>};
# Uncomment for debugging the watch response
# open ($fh, ">", ".git/watchman-response.json");
# print $fh $response;
# close $fh;
die "Watchman: command returned no output.\n" .
"Falling back to scanning...\n" if $response eq "";
die "Watchman: command returned invalid output: $response\n" .
"Falling back to scanning...\n" unless $response =~ /^\{/;
return $json_pkg->new->utf8->decode($response);
}
sub is_work_tree_watched {
my ($output) = @_;
my $error = $output->{error};
if ($retry > 0 and $error and $error =~ m/unable to resolve root .* directory (.*) is not watched/) {
$retry--;
my $response = qx/watchman watch "$git_work_tree"/;
die "Failed to make watchman watch '$git_work_tree'.\n" .
"Falling back to scanning...\n" if $? != 0;
$output = $json_pkg->new->utf8->decode($response);
$error = $output->{error};
die "Watchman: $error.\n" .
"Falling back to scanning...\n" if $error;
# Uncomment for debugging watchman output
# open (my $fh, ">", ".git/watchman-output.out");
# close $fh;
# Watchman will always return all files on the first query so
# return the fast "everything is dirty" flag to git and do the
# Watchman query just to get it over with now so we won't pay
# the cost in git to look up each individual file.
my $o = watchman_clock();
$error = $output->{error};
die "Watchman: $error.\n" .
"Falling back to scanning...\n" if $error;
output_result($o->{clock}, ("/"));
$last_update_token = $o->{clock};
eval { launch_watchman() };
return 0;
}
die "Watchman: $error.\n" .
"Falling back to scanning...\n" if $error;
return 1;
}
sub get_working_dir {
my $working_dir;
if ($^O =~ 'msys' || $^O =~ 'cygwin') {
$working_dir = Win32::GetCwd();
$working_dir =~ tr/\\/\//;
} else {
require Cwd;
$working_dir = Cwd::cwd();
}
return $working_dir;
}

View File

@ -0,0 +1,8 @@
#!/nix/store/3j18grljsyy4nxc078g00sy4cx6cf16g-bash-5.1-p16/bin/bash
#
# An example hook script to prepare a packed repository for use over
# dumb transports.
#
# To enable this hook, rename this file to "post-update".
exec git update-server-info

View File

@ -0,0 +1,14 @@
#!/nix/store/3j18grljsyy4nxc078g00sy4cx6cf16g-bash-5.1-p16/bin/bash
#
# An example hook script to verify what is about to be committed
# by applypatch from an e-mail message.
#
# The hook should exit with non-zero status after issuing an
# appropriate message if it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-applypatch".
. git-sh-setup
precommit="$(git rev-parse --git-path hooks/pre-commit)"
test -x "$precommit" && exec "$precommit" ${1+"$@"}
:

View File

@ -0,0 +1,49 @@
#!/nix/store/3j18grljsyy4nxc078g00sy4cx6cf16g-bash-5.1-p16/bin/bash
#
# An example hook script to verify what is about to be committed.
# Called by "git commit" with no arguments. The hook should
# exit with non-zero status after issuing an appropriate message if
# it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-commit".
if git rev-parse --verify HEAD >/dev/null 2>&1
then
against=HEAD
else
# Initial commit: diff against an empty tree object
against=$(git hash-object -t tree /dev/null)
fi
# If you want to allow non-ASCII filenames set this variable to true.
allownonascii=$(git config --type=bool hooks.allownonascii)
# Redirect output to stderr.
exec 1>&2
# Cross platform projects tend to avoid non-ASCII filenames; prevent
# them from being added to the repository. We exploit the fact that the
# printable range starts at the space character and ends with tilde.
if [ "$allownonascii" != "true" ] &&
# Note that the use of brackets around a tr range is ok here, (it's
# even required, for portability to Solaris 10's /usr/bin/tr), since
# the square bracket bytes happen to fall in the designated range.
test $(git diff --cached --name-only --diff-filter=A -z $against |
LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
then
cat <<\EOF
Error: Attempt to add a non-ASCII file name.
This can cause problems if you want to work with people on other platforms.
To be portable it is advisable to rename the file.
If you know what you are doing you can disable this check using:
git config hooks.allownonascii true
EOF
exit 1
fi
# If there are whitespace errors, print the offending file names and fail.
exec git diff-index --check --cached $against --

View File

@ -0,0 +1,13 @@
#!/nix/store/3j18grljsyy4nxc078g00sy4cx6cf16g-bash-5.1-p16/bin/bash
#
# An example hook script to verify what is about to be committed.
# Called by "git merge" with no arguments. The hook should
# exit with non-zero status after issuing an appropriate message to
# stderr if it wants to stop the merge commit.
#
# To enable this hook, rename this file to "pre-merge-commit".
. git-sh-setup
test -x "$GIT_DIR/hooks/pre-commit" &&
exec "$GIT_DIR/hooks/pre-commit"
:

View File

@ -0,0 +1,53 @@
#!/nix/store/3j18grljsyy4nxc078g00sy4cx6cf16g-bash-5.1-p16/bin/bash
# An example hook script to verify what is about to be pushed. Called by "git
# push" after it has checked the remote status, but before anything has been
# pushed. If this script exits with a non-zero status nothing will be pushed.
#
# This hook is called with the following parameters:
#
# $1 -- Name of the remote to which the push is being done
# $2 -- URL to which the push is being done
#
# If pushing without using a named remote those arguments will be equal.
#
# Information about the commits which are being pushed is supplied as lines to
# the standard input in the form:
#
# <local ref> <local oid> <remote ref> <remote oid>
#
# This sample shows how to prevent push of commits where the log message starts
# with "WIP" (work in progress).
remote="$1"
url="$2"
zero=$(git hash-object --stdin </dev/null | tr '[0-9a-f]' '0')
while read local_ref local_oid remote_ref remote_oid
do
if test "$local_oid" = "$zero"
then
# Handle delete
:
else
if test "$remote_oid" = "$zero"
then
# New branch, examine all commits
range="$local_oid"
else
# Update to existing branch, examine new commits
range="$remote_oid..$local_oid"
fi
# Check for WIP commit
commit=$(git rev-list -n 1 --grep '^WIP' "$range")
if test -n "$commit"
then
echo >&2 "Found WIP commit in $local_ref, not pushing"
exit 1
fi
fi
done
exit 0

View File

@ -0,0 +1,169 @@
#!/nix/store/3j18grljsyy4nxc078g00sy4cx6cf16g-bash-5.1-p16/bin/bash
#
# Copyright (c) 2006, 2008 Junio C Hamano
#
# The "pre-rebase" hook is run just before "git rebase" starts doing
# its job, and can prevent the command from running by exiting with
# non-zero status.
#
# The hook is called with the following parameters:
#
# $1 -- the upstream the series was forked from.
# $2 -- the branch being rebased (or empty when rebasing the current branch).
#
# This sample shows how to prevent topic branches that are already
# merged to 'next' branch from getting rebased, because allowing it
# would result in rebasing already published history.
publish=next
basebranch="$1"
if test "$#" = 2
then
topic="refs/heads/$2"
else
topic=`git symbolic-ref HEAD` ||
exit 0 ;# we do not interrupt rebasing detached HEAD
fi
case "$topic" in
refs/heads/??/*)
;;
*)
exit 0 ;# we do not interrupt others.
;;
esac
# Now we are dealing with a topic branch being rebased
# on top of master. Is it OK to rebase it?
# Does the topic really exist?
git show-ref -q "$topic" || {
echo >&2 "No such branch $topic"
exit 1
}
# Is topic fully merged to master?
not_in_master=`git rev-list --pretty=oneline ^master "$topic"`
if test -z "$not_in_master"
then
echo >&2 "$topic is fully merged to master; better remove it."
exit 1 ;# we could allow it, but there is no point.
fi
# Is topic ever merged to next? If so you should not be rebasing it.
only_next_1=`git rev-list ^master "^$topic" ${publish} | sort`
only_next_2=`git rev-list ^master ${publish} | sort`
if test "$only_next_1" = "$only_next_2"
then
not_in_topic=`git rev-list "^$topic" master`
if test -z "$not_in_topic"
then
echo >&2 "$topic is already up to date with master"
exit 1 ;# we could allow it, but there is no point.
else
exit 0
fi
else
not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`
/nix/store/c9d6q19a057kdh7gwpnnwlz79d8q6vx7-perl-5.34.1/bin/perl -e '
my $topic = $ARGV[0];
my $msg = "* $topic has commits already merged to public branch:\n";
my (%not_in_next) = map {
/^([0-9a-f]+) /;
($1 => 1);
} split(/\n/, $ARGV[1]);
for my $elem (map {
/^([0-9a-f]+) (.*)$/;
[$1 => $2];
} split(/\n/, $ARGV[2])) {
if (!exists $not_in_next{$elem->[0]}) {
if ($msg) {
print STDERR $msg;
undef $msg;
}
print STDERR " $elem->[1]\n";
}
}
' "$topic" "$not_in_next" "$not_in_master"
exit 1
fi
<<\DOC_END
This sample hook safeguards topic branches that have been
published from being rewound.
The workflow assumed here is:
* Once a topic branch forks from "master", "master" is never
merged into it again (either directly or indirectly).
* Once a topic branch is fully cooked and merged into "master",
it is deleted. If you need to build on top of it to correct
earlier mistakes, a new topic branch is created by forking at
the tip of the "master". This is not strictly necessary, but
it makes it easier to keep your history simple.
* Whenever you need to test or publish your changes to topic
branches, merge them into "next" branch.
The script, being an example, hardcodes the publish branch name
to be "next", but it is trivial to make it configurable via
$GIT_DIR/config mechanism.
With this workflow, you would want to know:
(1) ... if a topic branch has ever been merged to "next". Young
topic branches can have stupid mistakes you would rather
clean up before publishing, and things that have not been
merged into other branches can be easily rebased without
affecting other people. But once it is published, you would
not want to rewind it.
(2) ... if a topic branch has been fully merged to "master".
Then you can delete it. More importantly, you should not
build on top of it -- other people may already want to
change things related to the topic as patches against your
"master", so if you need further changes, it is better to
fork the topic (perhaps with the same name) afresh from the
tip of "master".
Let's look at this example:
o---o---o---o---o---o---o---o---o---o "next"
/ / / /
/ a---a---b A / /
/ / / /
/ / c---c---c---c B /
/ / / \ /
/ / / b---b C \ /
/ / / / \ /
---o---o---o---o---o---o---o---o---o---o---o "master"
A, B and C are topic branches.
* A has one fix since it was merged up to "next".
* B has finished. It has been fully merged up to "master" and "next",
and is ready to be deleted.
* C has not merged to "next" at all.
We would want to allow C to be rebased, refuse A, and encourage
B to be deleted.
To compute (1):
git rev-list ^master ^topic next
git rev-list ^master next
if these match, topic has not merged in next at all.
To compute (2):
git rev-list master..topic
if this is empty, it is fully merged to "master".
DOC_END

View File

@ -0,0 +1,24 @@
#!/nix/store/3j18grljsyy4nxc078g00sy4cx6cf16g-bash-5.1-p16/bin/bash
#
# An example hook script to make use of push options.
# The example simply echoes all push options that start with 'echoback='
# and rejects all pushes when the "reject" push option is used.
#
# To enable this hook, rename this file to "pre-receive".
if test -n "$GIT_PUSH_OPTION_COUNT"
then
i=0
while test "$i" -lt "$GIT_PUSH_OPTION_COUNT"
do
eval "value=\$GIT_PUSH_OPTION_$i"
case "$value" in
echoback=*)
echo "echo from the pre-receive-hook: ${value#*=}" >&2
;;
reject)
exit 1
esac
i=$((i + 1))
done
fi

View File

@ -0,0 +1,42 @@
#!/nix/store/3j18grljsyy4nxc078g00sy4cx6cf16g-bash-5.1-p16/bin/bash
#
# An example hook script to prepare the commit log message.
# Called by "git commit" with the name of the file that has the
# commit message, followed by the description of the commit
# message's source. The hook's purpose is to edit the commit
# message file. If the hook fails with a non-zero status,
# the commit is aborted.
#
# To enable this hook, rename this file to "prepare-commit-msg".
# This hook includes three examples. The first one removes the
# "# Please enter the commit message..." help message.
#
# The second includes the output of "git diff --name-status -r"
# into the message, just before the "git status" output. It is
# commented because it doesn't cope with --amend or with squashed
# commits.
#
# The third example adds a Signed-off-by line to the message, that can
# still be edited. This is rarely a good idea.
COMMIT_MSG_FILE=$1
COMMIT_SOURCE=$2
SHA1=$3
/nix/store/c9d6q19a057kdh7gwpnnwlz79d8q6vx7-perl-5.34.1/bin/perl -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' "$COMMIT_MSG_FILE"
# case "$COMMIT_SOURCE,$SHA1" in
# ,|template,)
# /nix/store/c9d6q19a057kdh7gwpnnwlz79d8q6vx7-perl-5.34.1/bin/perl -i.bak -pe '
# print "\n" . `git diff --cached --name-status -r`
# if /^#/ && $first++ == 0' "$COMMIT_MSG_FILE" ;;
# *) ;;
# esac
# SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
# git interpret-trailers --in-place --trailer "$SOB" "$COMMIT_MSG_FILE"
# if test -z "$COMMIT_SOURCE"
# then
# /nix/store/c9d6q19a057kdh7gwpnnwlz79d8q6vx7-perl-5.34.1/bin/perl -i.bak -pe 'print "\n" if !$first_line++' "$COMMIT_MSG_FILE"
# fi

View File

@ -0,0 +1,78 @@
#!/nix/store/3j18grljsyy4nxc078g00sy4cx6cf16g-bash-5.1-p16/bin/bash
# An example hook script to update a checked-out tree on a git push.
#
# This hook is invoked by git-receive-pack(1) when it reacts to git
# push and updates reference(s) in its repository, and when the push
# tries to update the branch that is currently checked out and the
# receive.denyCurrentBranch configuration variable is set to
# updateInstead.
#
# By default, such a push is refused if the working tree and the index
# of the remote repository has any difference from the currently
# checked out commit; when both the working tree and the index match
# the current commit, they are updated to match the newly pushed tip
# of the branch. This hook is to be used to override the default
# behaviour; however the code below reimplements the default behaviour
# as a starting point for convenient modification.
#
# The hook receives the commit with which the tip of the current
# branch is going to be updated:
commit=$1
# It can exit with a non-zero status to refuse the push (when it does
# so, it must not modify the index or the working tree).
die () {
echo >&2 "$*"
exit 1
}
# Or it can make any necessary changes to the working tree and to the
# index to bring them to the desired state when the tip of the current
# branch is updated to the new commit, and exit with a zero status.
#
# For example, the hook can simply run git read-tree -u -m HEAD "$1"
# in order to emulate git fetch that is run in the reverse direction
# with git push, as the two-tree form of git read-tree -u -m is
# essentially the same as git switch or git checkout that switches
# branches while keeping the local changes in the working tree that do
# not interfere with the difference between the branches.
# The below is a more-or-less exact translation to shell of the C code
# for the default behaviour for git's push-to-checkout hook defined in
# the push_to_deploy() function in builtin/receive-pack.c.
#
# Note that the hook will be executed from the repository directory,
# not from the working tree, so if you want to perform operations on
# the working tree, you will have to adapt your code accordingly, e.g.
# by adding "cd .." or using relative paths.
if ! git update-index -q --ignore-submodules --refresh
then
die "Up-to-date check failed"
fi
if ! git diff-files --quiet --ignore-submodules --
then
die "Working directory has unstaged changes"
fi
# This is a rough translation of:
#
# head_has_history() ? "HEAD" : EMPTY_TREE_SHA1_HEX
if git cat-file -e HEAD 2>/dev/null
then
head=HEAD
else
head=$(git hash-object -t tree --stdin </dev/null)
fi
if ! git diff-index --quiet --cached --ignore-submodules $head --
then
die "Working directory has staged changes"
fi
if ! git read-tree -u -m "$commit"
then
die "Could not update working tree to new HEAD"
fi

View File

@ -0,0 +1,128 @@
#!/nix/store/3j18grljsyy4nxc078g00sy4cx6cf16g-bash-5.1-p16/bin/bash
#
# An example hook script to block unannotated tags from entering.
# Called by "git receive-pack" with arguments: refname sha1-old sha1-new
#
# To enable this hook, rename this file to "update".
#
# Config
# ------
# hooks.allowunannotated
# This boolean sets whether unannotated tags will be allowed into the
# repository. By default they won't be.
# hooks.allowdeletetag
# This boolean sets whether deleting tags will be allowed in the
# repository. By default they won't be.
# hooks.allowmodifytag
# This boolean sets whether a tag may be modified after creation. By default
# it won't be.
# hooks.allowdeletebranch
# This boolean sets whether deleting branches will be allowed in the
# repository. By default they won't be.
# hooks.denycreatebranch
# This boolean sets whether remotely creating branches will be denied
# in the repository. By default this is allowed.
#
# --- Command line
refname="$1"
oldrev="$2"
newrev="$3"
# --- Safety check
if [ -z "$GIT_DIR" ]; then
echo "Don't run this script from the command line." >&2
echo " (if you want, you could supply GIT_DIR then run" >&2
echo " $0 <ref> <oldrev> <newrev>)" >&2
exit 1
fi
if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
echo "usage: $0 <ref> <oldrev> <newrev>" >&2
exit 1
fi
# --- Config
allowunannotated=$(git config --type=bool hooks.allowunannotated)
allowdeletebranch=$(git config --type=bool hooks.allowdeletebranch)
denycreatebranch=$(git config --type=bool hooks.denycreatebranch)
allowdeletetag=$(git config --type=bool hooks.allowdeletetag)
allowmodifytag=$(git config --type=bool hooks.allowmodifytag)
# check for no description
projectdesc=$(sed -e '1q' "$GIT_DIR/description")
case "$projectdesc" in
"Unnamed repository"* | "")
echo "*** Project description file hasn't been set" >&2
exit 1
;;
esac
# --- Check types
# if $newrev is 0000...0000, it's a commit to delete a ref.
zero=$(git hash-object --stdin </dev/null | tr '[0-9a-f]' '0')
if [ "$newrev" = "$zero" ]; then
newrev_type=delete
else
newrev_type=$(git cat-file -t $newrev)
fi
case "$refname","$newrev_type" in
refs/tags/*,commit)
# un-annotated tag
short_refname=${refname##refs/tags/}
if [ "$allowunannotated" != "true" ]; then
echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
exit 1
fi
;;
refs/tags/*,delete)
# delete tag
if [ "$allowdeletetag" != "true" ]; then
echo "*** Deleting a tag is not allowed in this repository" >&2
exit 1
fi
;;
refs/tags/*,tag)
# annotated tag
if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1
then
echo "*** Tag '$refname' already exists." >&2
echo "*** Modifying a tag is not allowed in this repository." >&2
exit 1
fi
;;
refs/heads/*,commit)
# branch
if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then
echo "*** Creating a branch is not allowed in this repository" >&2
exit 1
fi
;;
refs/heads/*,delete)
# delete branch
if [ "$allowdeletebranch" != "true" ]; then
echo "*** Deleting a branch is not allowed in this repository" >&2
exit 1
fi
;;
refs/remotes/*,commit)
# tracking branch
;;
refs/remotes/*,delete)
# delete tracking branch
if [ "$allowdeletebranch" != "true" ]; then
echo "*** Deleting a tracking branch is not allowed in this repository" >&2
exit 1
fi
;;
*)
# Anything else (is there anything else?)
echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
exit 1
;;
esac
# --- Finished
exit 0

BIN
lisp-koans/.git_bak/index Normal file

Binary file not shown.

View File

@ -0,0 +1,6 @@
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~

View File

@ -0,0 +1 @@
0000000000000000000000000000000000000000 fa286eb6a98b0ab463c9c6da97b3113220d34216 efim <efim.nefedov@nordigy.ru> 1658664650 +0000 clone: from https://github.com/google/lisp-koans.git

View File

@ -0,0 +1 @@
0000000000000000000000000000000000000000 fa286eb6a98b0ab463c9c6da97b3113220d34216 efim <efim.nefedov@nordigy.ru> 1658664650 +0000 clone: from https://github.com/google/lisp-koans.git

View File

@ -0,0 +1,2 @@
# pack-refs with: peeled fully-peeled sorted
fa286eb6a98b0ab463c9c6da97b3113220d34216 refs/remotes/origin/master

View File

@ -0,0 +1 @@
fa286eb6a98b0ab463c9c6da97b3113220d34216

34
lisp-koans/.koans Normal file
View File

@ -0,0 +1,34 @@
(
#:asserts
#:nil-false-empty
#:evaluation
#:atoms-vs-lists
#:let
#:scope-and-extent
#:basic-macros
#:lists
#:arrays
#:vectors
#:multiple-values
#:equality-distinctions
#:hash-tables
#:functions
#:strings
#:structures
#:iteration
#:mapcar-and-reduce
#:control-statements
#:loops
#:scoring-project
#:format
#:type-checking
#:clos
#:std-method-comb
#:condition-handlers
#:triangle-project
#:dice-project
#:backquote
#:macros
#+quicklisp #:threads
#:extra-credit
)

5
lisp-koans/BUILD Normal file
View File

@ -0,0 +1,5 @@
# Description: Common Lisp lisp-koans
licenses(["notice"]) # Apache License 2.0 at //third_party/lisp/lisp-koans/LICENSE
exports_files(["LICENSE"])

202
lisp-koans/LICENSE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
------------------------------------------------
For test-framework:
The MIT License
Copyright (c) 2004-2005 Christopher K. Riesbeck
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

108
lisp-koans/README.md Normal file
View File

@ -0,0 +1,108 @@
# Lisp Koans
## Getting Started
### One-time Method
From a terminal, execute your lisp interpreter on the file 'contemplate.lisp' e.g.
abcl --noinform --noinit --load contemplate.lisp --eval '(quit)'
ccl -n -l contemplate.lisp -e '(quit)'
clisp -q -norc -ansi contemplate.lisp
ecl -norc -load contemplate.lisp -eval '(quit)'
sbcl --script contemplate.lisp
### Watching the Koans
On Linux and MacOS systems, the shell scripts `meditate-linux.sh` and
`meditate-macos.sh` can be used to automatically evaluate 'contemplate.lisp'
whenever the koan files are modified, providing immediate feedback on changes
to the koans. To run the MacOS version you need to have
[`fswatch`](https://github.com/emcrisostomo/fswatch) installed. From a terminal:
$ cd lisp-koans
$ sh meditate-linux.sh # on Linux
$ sh meditate-macos.sh # on MacOS
## Results of Contemplation
Running on a fresh version should output the following:
```
Thinking about ASSERTS
FILL-IN-THE-BLANKS requires more meditation.
You have not yet reached enlightenment.
A koan is incomplete.
Please meditate on the following code:
File "koans/asserts.lisp"
Koan "FILL-IN-THE-BLANKS"
Current koan assert status is "(INCOMPLETE INCOMPLETE INCOMPLETE)"
You are now 0/198 koans and 0/31 lessons closer to reaching enlightenment.
```
This indicates that the script has completed, and that the learner should look
to asserts.lisp to locate and fix the problem. The problem will be within
a define-test expression such as
```lisp
;;; In order to progress, fill in the blanks, denoted via ____ in source code.
;;; Sometimes, you will be asked to provide values that are equal to something.
(define-test fill-in-the-blanks
(assert-equal ____ 2)
(assert-equal ____ 3.14)
(assert-equal ____ "Hello World"))
;;; Sometimes, you will be asked to say whether something is true or false,
;;; In Common Lisp, the canonical values for truth and falsehood are T and NIL.
(define-test assert-true
(assert-true ____))
(define-test assert-false
(assert-false ____))
```
In this case, the test is incomplete, and the student should fill
in the blank (\_\_\_\_) with appropriate lisp code to make the assert pass.
In order to test code, or evaluate tests interactively, students may copy
and paste code into the lisp command line REPL.
### Testing
To test the koans, execute your lisp interpreter on the file 'contemplate.lisp' e.g.
abcl --noinform --noinit --load test.lisp --eval '(quit)'
ccl -n -l test.lisp -e '(quit)'
clisp -q -norc -ansi test.lisp
ecl -norc -load test.lisp -eval '(quit)'
sbcl --script test.lisp
## Quoting the Ruby Koans instructions
"In test-driven development the mantra has always been, red, green,
refactor. Write a failing test and run it (red), make the test pass (green),
then refactor it (that is look at the code and see if you can make it any
better). In this case you will need to run the koan and see it fail (red), make
the test pass (green), then take a moment and reflect upon the test to see what
it is teaching you and improve the code to better communicate its
intent (refactor)."
## Content
The Common Lisp koans are based on the Python koans and Ruby koans projects.
Additionally, many of the tests are based on new material that is special
to Common Lisp.
Note that the unit on threads uses bordeaux-threads and bt-semaphore.
The user must have Quicklisp installed and loaded or a reader macro
will remove the instructions to run :threads.
For information and instructions on installing Quicklisp
please see:
https://www.quicklisp.org/beta/
The user can either remove #+quicklisp and uncomment
(load "~/.quicklisp/setup.lisp") in threads.lisp, or if they know
quicklisp will be loaded while running contemplate.lisp do nothing.

1
lisp-koans/TODO Normal file
View File

@ -0,0 +1 @@
* improve error reporting from "a koan signaled an error" to something more helpful

View File

@ -0,0 +1,29 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(in-package :cl-user)
;;; Though Clozure / CCL runs lisp-koans on the command line using
;;; "ccl -l contemplate.lisp", the following lines are needed to
;;; meditate on the koans within the CCL IDE.
;;; (The :hemlock is used to distiguish between ccl commandline and the IDE)
#+(and :ccl :hemlock)
(setf *default-pathname-defaults* (directory-namestring *load-pathname*))
(load "test-framework.lisp")
(load "lisp-koans.lisp")
#+quicklisp (ql:quickload :bordeaux-threads)
(lisp-koans.core:main)

View File

@ -0,0 +1,72 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(define-test basic-array-stuff
;; We make an 8x8 array and then fill it with a checkerboard pattern.
(let ((chess-board (make-array '(8 8))))
;; (DOTIMES (X 8) ...) will iterate with X taking values from 0 to 7.
(dotimes (x 8)
(dotimes (y 8)
;; AREF stands for "array reference".
(setf (aref chess-board x y) (if (evenp (+ x y)) :black :white))))
(assert-true (typep chess-board 'array))
(assert-equal :black (aref chess-board 0 0))
(assert-equal :white (aref chess-board 2 3))
;; The function ARRAY-RANK returns the number of dimensions of the array.
(assert-equal 2 (array-rank chess-board))
;; The function ARRAY-DIMENSIONS returns a list of the cardinality of the
;; array dimensions.
(assert-equal '(8 8) (array-dimensions chess-board))
;; ARRAY-TOTAL-SIZE returns the total number of elements in the array.
(assert-equal 64 (array-total-size chess-board))))
(define-test make-your-own-array
;; Make your own array that satisfies the test.
(let ((color-cube (make-array '(3 3 3))))
;; You may need to modify your array after you create it.
(setf (aref color-cube 0 1 2) :red
(aref color-cube 2 1 0) :white)
(if (typep color-cube '(simple-array T (3 3 3)))
(progn
(assert-equal 3 (array-rank color-cube))
(assert-equal '(3 3 3) (array-dimensions color-cube))
(assert-equal 27 (array-total-size color-cube))
(assert-equal (aref color-cube 0 1 2) :red)
(assert-equal (aref color-cube 2 1 0) :white))
(assert-true nil))))
(define-test adjustable-array
;; The size of an array does not need to be constant.
(let ((x (make-array '(2 2) :initial-element 5 :adjustable t)))
(assert-equal 5 (aref x 1 0))
(assert-equal '(2 2) (array-dimensions x))
(adjust-array x '(3 4))
(assert-equal '(3 4) (array-dimensions x))))
(define-test make-array-from-list
;; One can create arrays with initial contents.
(let ((x (make-array '(4) :initial-contents '(:one :two :three :four))))
(assert-equal '(4) (array-dimensions x))
(assert-equal :one (aref x 0))))
(define-test row-major-index
;; Row major indexing is a way to access elements with a single integer,
;; rather than a list of integers.
(let ((my-array (make-array '(2 2 2 2))))
(dotimes (i (* 2 2 2 2))
(setf (row-major-aref my-array i) i))
(assert-equal 0 (aref my-array 0 0 0 0))
(assert-equal 2 (aref my-array 0 0 1 0))
(assert-equal 4 (aref my-array 0 1 0 0))
(assert-equal 15 (aref my-array 1 1 1 1))))

View File

@ -0,0 +1,65 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; ╭╮ ╭╮ ///////
;;; ┃┃ ┃┃///////
;;; ┃┃╭┳━━┳━━╮ ┃┃╭┳━━┳━━┳━╮╭━━╮
;;; ┃┃┣┫━━┫╭╮┃ ┃╰╯┫╭╮┃╭╮┃╭╮┫━━┫
;;; ┃╰┫┣━━┃╰╯┃ ┃╭╮┫╰╯┃╭╮┃┃┃┣━━┃
;;; ╰━┻┻━━┫╭━╯/╰╯╰┻━━┻╯╰┻╯╰┻━━╯
;;; ┃┃ //////
;;; ╰╯//////
;;; Welcome to the Lisp Koans.
;;; May the code stored here influence your enlightenment as a programmer.
;;; In order to progress, fill in the blanks, denoted via ____ in source code.
;;; Sometimes, you will be asked to provide values that are equal to something.
(define-test fill-in-the-blanks
(assert-equal 2 2)
(assert-equal 3.14 3.14)
(assert-equal "Hello World" "Hello World"))
;;; Sometimes, you will be asked to say whether something is true or false,
;;; In Common Lisp, the canonical values for truth and falsehood are T and NIL.
(define-test assert-true
(assert-true t))
(define-test assert-false
(assert-false nil))
(define-test true-or-false
(true-or-false? t (= 34 34))
(true-or-false? nil (= 19 78)))
;;; Since T and NIL are symbols, you can type them in lowercase or uppercase;
;;; by default, Common Lisp will automatically upcase them upon reading.
(define-test upcase-downcase
;; Try inserting a lowercase t here.
(assert-equal t T)
;; Try inserting an uppercase NIL here.
(assert-equal NIL nil))
;;; Sometimes, you will be asked to provide a part of an expression that must be
;;; either true or false.
(define-test a-true-assertion
(assert-true (= 4 (+ 2 2))))
(define-test a-false-assertion
(assert-false (= 5 (+ 2 2))))

View File

@ -0,0 +1,43 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; Lists in lisp are forms beginning and ending with rounded parentheses.
;;; Atoms are symbols, numbers, or other forms usually separated by whitespace
;;; or parentheses.
(define-test list-or-atom
;; The function LISTP will return true if the input is a list.
;; The function ATOM will return true if the input is an atom.
(true-or-false? t (listp '(1 2 3)))
(true-or-false? nil (atom '(1 2 3)))
(true-or-false? t (listp '("heres" "some" "strings")))
(true-or-false? nil (atom '("heres" "some" "strings")))
(true-or-false? nil (listp "a string"))
(true-or-false? t (atom "a string"))
(true-or-false? nil (listp 2))
(true-or-false? t (atom 2))
(true-or-false? t (listp '(("first" "list") ("second" "list"))))
(true-or-false? nil (atom '(("first" "list") ("second" "list")))))
(define-test the-duality-of-nil
;; The empty list, NIL, is unique in that it is both a list and an atom.
(true-or-false? t (listp nil))
(true-or-false? t (atom nil)))
(define-test keywords
;; Symbols like :HELLO or :LIKE-THIS are keywords. They are treated
;; differently in Lisp: they are constants that always evaluate to themselves.
(true-or-false? t (equal :this-is-a-keyword :this-is-a-keyword))
(true-or-false? t (equal :this-is-a-keyword ':this-is-a-keyword))
(true-or-false? nil (equal :this-is-a-keyword :this-is-also-a-keyword)))

View File

@ -0,0 +1,71 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; Backquote notation is similar to quoting, except it allows for parts of the
;;; resulting expression to be "unquoted".
(define-test backquote-basics
(let ((x '(123))
(z '(7 8 9)))
;; ' quotes an expression normally.
(assert-equal '(x 45 6 z) '(x 45 6 z))
;; ` backquotes an expression; without any unquotes, it is equivalent to
;; using the normal quote.
(assert-equal '(x 45 6 z) `(x 45 6 z))
;; , unquotes a part of the expression.
(assert-equal '((123) 45 6 z) `(,x 45 6 z))
(assert-equal '((123) 45 6 (7 8 9)) `(,x 45 6 ,z))
;; ,@ splices an expression into the into the list surrounding it.
(assert-equal '((123) 45 6 7 8 9) `(,x 45 6 ,@z))
(assert-equal '(123 45 6 7 8 9) `(,@x 45 6 ,@z))))
(define-test backquote-forms
;; Because of its properties, backquote is useful for constructing Lisp forms
;; that are macroexpansions or parts of macroexpansions.
(let ((variable 'x))
;; Fill in the blank without without using backquote/unquote notation.
(assert-equal '(if (typep x 'string)
(format nil "The value of ~A is ~A" 'x x)
(error 'type-error :datum x :expected-type 'string))
`(if (typep ,variable 'string)
(format nil "The value of ~A is ~A" ',variable ,variable)
(error 'type-error :datum ,variable
:expected-type 'string))))
(let ((error-type 'type-error)
(error-arguments '(:datum x :expected-type 'string)))
;; Fill in the blank without without using backquote/unquote notation.
(assert-equal '(if (typep x 'string)
(format nil "The value of ~A is ~A" 'x x)
(error 'type-error :datum x :expected-type 'string))
`(if (typep x 'string)
(format nil "The value of ~A is ~A" 'x x)
(error ',error-type ,@error-arguments)))))
(define-test numbers-and-words
(let ((number 5)
(word 'dolphin))
(true-or-false? t (equal '(1 3 5) `(1 3 5)))
(true-or-false? nil (equal '(1 3 5) `(1 3 number)))
(assert-equal '(1 3 5) `(1 3 ,number))
(assert-equal '(word dolphin dolphin word) `(word ,word ,word word))))
(define-test splicing
(let ((axis '(x y z)))
(assert-equal '(the axis are (x y z)) `(the axis are ,axis))
(assert-equal '(the axis are x y z) `(the axis are ,@axis)))
(let ((coordinates '((43.15 77.6) (42.36 71.06))))
(assert-equal '(the coordinates are ((43.15 77.6) (42.36 71.06)))
`(the coordinates are ,coordinates))
(assert-equal '(the coordinates are (43.15 77.6) (42.36 71.06))
`(the coordinates are ,@coordinates))))

View File

@ -0,0 +1,112 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(define-test setf
;; SETF is a macro used to assign values to places. A place is a concept;
;; it is an abstract "somewhere" where a value is stored.
(let ((a 10)
(b (list 1 20 30 40 50))
;; We use COPY-SEQ to create a copy of a string, because using SETF to
;; modify literal data (strings, lists, etc.) is undefined behaviour.
(c (copy-seq "I am Tom.")))
;; A place may be a variable.
(setf a 1000)
(assert-equal 1000 a)
;; A place may be a part of some list.
(setf (first b) 10)
(assert-equal '(10 20 30 40 50) b)
;; A place may be a character in a string.
;; The #\x syntax denotes a single character, 'x'.
(setf (char c 5) #\B
(char c 7) #\b)
(assert-equal "I am Bob." c)
;; There are other kinds of places that we will explore in the future.
))
(define-test case
;; CASE is a simple pattern-matching macro, not unlike C's "switch".
;; It compares an input against a set of values and evaluates the code for
;; the branch where a match is found.
(let* ((a 4)
(b (case a
(3 :three)
(4 :four)
(5 :five))))
(assert-equal :four b))
;; CASE can accept a group of keys.
(let* ((c 4)
(d (case c
((0 2 4 6 8) :even-digit)
((1 3 5 7 9) :odd-digit))))
(assert-equal :even-digit d)))
(defun match-special-cases (thing)
;; T or OTHERWISE passed as the key matches any value.
;; NIL passed as the key matches no values.
;; These symbols need to passed in parentheses.
(case thing
((t) :found-a-t)
((nil) :found-a-nil)
(t :something-else)))
(define-test special-cases-of-case
;; You need to fill in the blanks in MATCH-SPECIAL-CASES.
(assert-equal :found-a-t (match-special-cases t))
(assert-equal :found-a-nil (match-special-cases nil))
(assert-equal :something-else (match-special-cases 42)))
(define-test your-own-case-statement
;; We use FLET to define a local function.
(flet ((cartoon-dads (input)
(case input
;; Fill in the blanks with proper cases.
(:bart :homer)
(:stewie :peter)
(:stan :randy)
(:this-one-doesnt-happen :fancy-cat)
(t :unknown))))
(assert-equal (cartoon-dads :bart) :homer)
(assert-equal (cartoon-dads :stewie) :peter)
(assert-equal (cartoon-dads :stan) :randy)
(assert-equal (cartoon-dads :space-ghost) :unknown)))
(define-test limits-of-case
;; So far, we have been comparing objects using EQUAL, one of the Lisp
;; comparison functions. CASE compares the keys using EQL, which is distinct
;; from EQUAL.
;; EQL is suitable for comparing numbers, characters, and objects for whom we
;; want to check verify they are the same object.
(let* ((string "A string")
(string-copy (copy-seq string)))
;; The above means that two distinct strings will not be the same under EQL,
;; even if they have the same contents.
(true-or-false? nil (eql string string-copy))
(true-or-false? t (equal string string-copy))
;; The above also means that CASE might give surprising results when used on
;; strings.
(let ((match (case string
("A string" :matched)
(t :not-matched))))
(assert-equal :not-matched match))
;; We will explore this topic further in the EQUALITY-DISTINCTIONS lesson.
))
(define-test cond
;; COND is similar to CASE, except it is more general. It accepts arbitrary
;; conditions and checks them in order until one of them is met.
(let* ((number 4)
(result (cond ((> number 0) :positive)
((< number 0) :negative)
(t :zero))))
(assert-equal :positive result)))

View File

@ -0,0 +1,181 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; CLOS is a shorthand for Common Lisp Object System.
(defclass racecar ()
;; A class definition lists all the slots of every instance.
(color speed))
(define-test defclass
;; Class instances are constructed via MAKE-INSTANCE.
(let ((car-1 (make-instance 'racecar))
(car-2 (make-instance 'racecar)))
;; Slot values can be set via SLOT-VALUE.
(setf (slot-value car-1 'color) :red)
(setf (slot-value car-1 'speed) 220)
(setf (slot-value car-2 'color) :blue)
(setf (slot-value car-2 'speed) 240)
(assert-equal :red (slot-value car-1 'color))
(assert-equal 240 (slot-value car-2 'speed))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Common Lisp predefines the symbol SPEED in the COMMON-LISP package, which
;;; means that we cannot define a function named after it. The function SHADOW
;;; creates a new symbol with the same name in the current package and shadows
;;; the predefined one within the current package.
(shadow 'speed)
(defclass spaceship ()
;; It is possible to define reader, writer, and accessor functions for slots.
((color :reader color :writer (setf color))
(speed :accessor speed)))
;;; Specifying a reader function named COLOR is equivalent to
;;; (DEFMETHOD COLOR ((OBJECT SPACECSHIP)) ...)
;;; Specifying a writer function named (SETF COLOR) is equivalent to
;;; (DEFMETHOD (SETF COLOR) (NEW-VALUE (OBJECT SPACECSHIP)) ...)
;;; Specifying an accessor function performs both of the above.
(define-test accessors
(let ((ship (make-instance 'spaceship)))
(setf (color ship) :orange
(speed ship) 1000)
(assert-equal :orange (color ship))
(assert-equal 1000 (speed ship))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defclass bike ()
;; It is also possible to define initial arguments for slots.
((color :reader color :initarg :color)
(speed :reader speed :initarg :speed)))
(define-test initargs
(let ((bike (make-instance 'bike :color :blue :speed 30)))
(assert-equal :blue (color bike))
(assert-equal 30 (speed bike))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Lisp classes can inherit from one another.
(defclass person ()
((name :initarg :name :accessor person-name)))
(defclass lisp-programmer (person)
((favorite-lisp-implementation :initarg :favorite-lisp-implementation
:accessor favorite-lisp-implementation)))
(defclass c-programmer (person)
((favorite-c-compiler :initarg :favorite-c-compiler
:accessor favorite-c-compiler)))
(define-test inheritance
(let ((jack (make-instance 'person :name :jack))
(bob (make-instance 'lisp-programmer
:name :bob
:favorite-lisp-implementation :sbcl))
(adam (make-instance 'c-programmer
:name :adam
:favorite-c-compiler :clang)))
(assert-equal :jack (person-name jack))
(assert-equal :bob (person-name bob))
(assert-equal :sbcl (favorite-lisp-implementation bob))
(assert-equal :adam (person-name adam))
(assert-equal :clang (favorite-c-compiler adam))
(true-or-false? t (typep bob 'person))
(true-or-false? t (typep bob 'lisp-programmer))
(true-or-false? nil (typep bob 'c-programmer))))
;;; This includes multiple inheritance.
(defclass clisp-programmer (lisp-programmer c-programmer) ())
(define-test multiple-inheritance
(let ((zenon (make-instance 'clisp-programmer
:name :zenon
:favorite-lisp-implementation :clisp
:favorite-c-compiler :gcc)))
(assert-equal :zenon (person-name zenon))
(assert-equal :clisp (favorite-lisp-implementation zenon))
(assert-equal :gcc (favorite-c-compiler zenon))
(true-or-false? t (typep zenon 'person))
(true-or-false? t (typep zenon 'lisp-programmer))
(true-or-false? t (typep zenon 'c-programmer))
(true-or-false? t (typep zenon 'clisp-programmer))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Multiple inheritance makes it possible to work with mixin classes.
(defclass greeting-mixin ()
((greeted-people :accessor greeted-people :initform '())))
(defgeneric greet (greeter greetee))
(defmethod greet ((object greeting-mixin) name)
;; PUSHNEW is similar to PUSH, but it does not modify the place if the object
;; we want to push is already found on the list in the place.
(pushnew name (greeted-people object) :test #'equal)
(format nil "Hello, ~A." name))
(defclass chatbot ()
((version :reader version :initarg :version)))
(defclass greeting-chatbot (greeting-mixin chatbot) ())
(define-test greeting-chatbot ()
(let ((chatbot (make-instance 'greeting-chatbot :version "1.0.0")))
(true-or-false? t (typep chatbot 'greeting-mixin))
(true-or-false? t (typep chatbot 'chatbot))
(true-or-false? t (typep chatbot 'greeting-chatbot))
(assert-equal "Hello, Tom." (greet chatbot "Tom"))
(assert-equal '("Tom") (greeted-people chatbot))
(assert-equal "Hello, Sue." (greet chatbot "Sue"))
(assert-equal "Hello, Mark." (greet chatbot "Mark"))
(assert-equal "Hello, Kate." (greet chatbot "Kate"))
(assert-equal "Hello, Mark." (greet chatbot "Mark"))
(assert-equal '("Kate" "Mark" "Sue" "Tom") (greeted-people chatbot))
(assert-equal "1.0.0" (version chatbot))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defclass american (person) ())
(defclass italian (person) ())
(defgeneric stereotypical-food (person)
;; The :METHOD option in DEFGENERIC is an alternative to DEFMETHOD.
(:method ((person italian)) :pasta)
(:method ((person american)) :burger))
;;; When methods or slot definitions of superclasses overlap with each other,
;;; the order of superclasses is used to resolve the conflict.
(defclass stereotypical-person (american italian) ())
(defclass another-stereotypical-person (italian american) ())
(define-test stereotypes
(let ((james (make-instance 'american))
(antonio (make-instance 'italian))
(roy (make-instance 'stereotypical-person))
(mary (make-instance 'another-stereotypical-person)))
(assert-equal :burger (stereotypical-food james))
(assert-equal :pasta (stereotypical-food antonio))
(assert-equal :burger (stereotypical-food roy))
(assert-equal :pasta (stereotypical-food mary))))

View File

@ -0,0 +1,279 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; Lisp condition types are very similar to classes. The standard specifies
;;; multiple standard condition types: among them, CONDITION, WARNING,
;;; SERIOUS-CONDITION, and ERROR.
;;; The type CONDITION is the base type of all condition objects.
(define-condition my-condition () ())
;;; The type WARNING is the base type of all conditions of which the programmer
;;; should be warned, unless the condition is somehow handled by the program.
(define-condition my-warning (warning) ())
;;; The type SERIOUS-CONDITION includes programming errors and other situations
;;; where computation cannot proceed (e.g. due to memory or storage issues).
(define-condition my-serious-condition (serious-condition) ())
;;; The type ERROR is the base type for all error situations in code.
(define-condition my-error (error) ())
(define-test type-hierarchy
;; Inheritance for condition types works the same way as for classes.
(let ((condition (make-condition 'my-condition)))
(true-or-false? t (typep condition 'my-condition))
(true-or-false? t (typep condition 'condition))
(true-or-false? nil (typep condition 'warning))
(true-or-false? nil (typep condition 'error)))
(let ((condition (make-condition 'my-warning)))
(true-or-false? t (typep condition 'my-warning))
(true-or-false? t (typep condition 'warning))
(true-or-false? nil (typep condition 'error)))
(let ((condition (make-condition 'my-serious-condition)))
(true-or-false? t (typep condition 'my-serious-condition))
(true-or-false? t (typep condition 'serious-condition))
(true-or-false? nil (typep condition 'warning))
(true-or-false? nil (typep condition 'error)))
(let ((condition (make-condition 'my-error)))
(true-or-false? t (typep condition 'my-error))
(true-or-false? nil (typep condition 'my-serious-condition))
(true-or-false? t (typep condition 'serious-condition))
(true-or-false? nil (typep condition 'warning))
(true-or-false? t (typep condition 'error))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; A condition handler is composed of a handler function that accepts a
;;; condition object and a condition type for which the function will be called.
(defvar *list*)
(define-condition silly-condition () ())
(define-condition very-silly-condition (silly-condition) ())
(define-condition most-silly-condition (very-silly-condition) ())
(defun handle-silly-condition (condition)
(declare (ignore condition))
(push :silly-condition *list*))
(defun handle-very-silly-condition (condition)
(declare (ignore condition))
(push :very-silly-condition *list*))
(defun handle-most-silly-condition (condition)
(declare (ignore condition))
(push :most-silly-condition *list*))
(define-test handler-bind
;; When a condition is signaled, all handlers whose type matches the
;; condition's type are allowed to execute.
(let ((*list* '()))
(handler-bind ((very-silly-condition #'handle-very-silly-condition)
(silly-condition #'handle-silly-condition)
(most-silly-condition #'handle-most-silly-condition))
(signal (make-condition 'most-silly-condition)))
(assert-equal '(:most-silly-condition
:silly-condition
:very-silly-condition)
*list*)))
(define-test handler-order
;; The order of binding handlers matters.
(let ((*list* '()))
(handler-bind ((silly-condition #'handle-silly-condition)
(very-silly-condition #'handle-very-silly-condition)
(most-silly-condition #'handle-most-silly-condition))
(signal (make-condition 'most-silly-condition)))
(assert-equal '(:most-silly-condition
:very-silly-condition
:silly-condition)
*list*)))
(define-test multiple-handler-binds
;; It is possible to bind handlers in steps.
(let ((*list* '()))
(handler-bind ((silly-condition #'handle-silly-condition)
(most-silly-condition #'handle-most-silly-condition))
(handler-bind ((very-silly-condition #'handle-very-silly-condition))
(signal (make-condition 'most-silly-condition))))
(assert-equal '(:most-silly-condition
:silly-condition
:very-silly-condition)
*list*)))
(define-test same-handler
;; The same handler may be bound multiple times.
(let ((*list* '()))
(handler-bind ((silly-condition #'handle-silly-condition)
(silly-condition #'handle-silly-condition))
(handler-bind ((very-silly-condition #'handle-very-silly-condition)
(silly-condition #'handle-silly-condition)
(very-silly-condition #'handle-very-silly-condition))
(signal (make-condition 'most-silly-condition))))
(assert-equal '(:silly-condition
:silly-condition
:very-silly-condition
:silly-condition
:very-silly-condition)
*list*)))
(define-test handler-types
;; A handler is not executed if it does not match the condition type.
(let ((*list* '()))
(handler-bind ((silly-condition #'handle-silly-condition)
(very-silly-condition #'handle-very-silly-condition)
(most-silly-condition #'handle-most-silly-condition))
(signal (make-condition 'very-silly-condition)))
(assert-equal '(:very-silly-condition :silly-condition) *list*)))
(define-test handler-transfer-of-control
;; A handler may decline to handle the condition if it returns normally,
;; or it may handle the condition by transferring control elsewhere.
(let ((*list* '()))
(block my-block
(handler-bind ((silly-condition #'handle-silly-condition)
(silly-condition (lambda (condition)
(declare (ignore condition))
(return-from my-block)))
(silly-condition #'handle-silly-condition))
(signal (make-condition 'silly-condition))))
(assert-equal '(:silly-condition) *list*)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun handle-error (condition)
(declare (ignore condition))
(push :error *list*))
(define-condition my-error (error) ())
(defun handle-my-error (condition)
(declare (ignore condition))
(push :my-error *list*))
(define-test handler-case
;; HANDLER-CASE always transfers control before executing the case forms.
(let ((*list* '()))
(handler-case (signal (make-condition 'my-error))
(error (condition) (handle-error condition))
(my-error (condition) (handle-my-error condition)))
(assert-equal '(:error) *list*)))
(define-test handler-case-order
;; The order of handler cases matters.
(let ((*list* '()))
(handler-case (signal (make-condition 'my-error))
(my-error (condition) (handle-my-error condition))
(error (condition) (handle-error condition)))
(assert-equal '(:my-error) *list*)))
(define-test handler-case-type
;; A handler cases is not executed if it does not match the condition type.
(let ((*list* '()))
(handler-case (signal (make-condition 'error))
(my-error (condition) (handle-my-error condition))
(error (condition) (handle-error condition)))
(assert-equal '(:error) *list*)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun divide (numerator denominator)
(/ numerator denominator))
(define-test error-signaling
;; ASSERT-ERROR is a Lisp Koans macro which verifies that the correct error
;; type is signaled.
(assert-equal 3 (divide 6 2))
(assert-error (divide 6 0) 'division-by-zero)
(assert-error (divide 6 :zero) 'type-error))
(define-test error-signaling-handler-case
(flet ((try-to-divide (numerator denominator)
;; In code outside Lisp Koans, HANDLER-CASE should be used.
(handler-case (divide numerator denominator)
(division-by-zero () :division-by-zero)
(type-error () :type-error))))
(assert-equal 3 (try-to-divide 6 2))
(assert-equal :division-by-zero (try-to-divide 6 0))
(assert-equal :type-error (try-to-divide 6 :zero))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Condition objects can contain metadata about the specific situation that
;;; occurred in the code.
(define-test accessors-division-by-zero
(let ((condition (handler-case (divide 6 0) (division-by-zero (c) c))))
;; Disabled on CLISP and ABCL due to conformance bugs.
;; See https://gitlab.com/gnu-clisp/clisp/-/issues/22
;; See https://github.com/armedbear/abcl/issues/177
#-(or clisp abcl)
(assert-equal '(6 0) (arithmetic-error-operands condition))
(let ((operation (arithmetic-error-operation condition)))
;; Disabled on ABCL due to a conformance bug.
;; See https://github.com/armedbear/abcl/issues/177
#-abcl
(assert-equal 3 (funcall operation 12 4)))))
(define-test accessors-type-error
(let ((condition (handler-case (divide 6 :zero) (type-error (c) c))))
(assert-equal :zero (type-error-datum condition))
(let ((expected-type (type-error-expected-type condition)))
(true-or-false? nil (typep :zero expected-type))
(true-or-false? t (typep 0 expected-type))
(true-or-false? nil (typep "zero" expected-type))
(true-or-false? t (typep 0.0 expected-type)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; We can define slots in our own condition types in a way that is similar to
;; DEFCLASS.
(define-condition parse-log-line-error (parse-error)
((line :initarg :line :reader line)
(reason :initarg :reason :reader reason)))
(defun log-line-type (line)
;; The macro CHECK-TYPE signals a TYPE-ERROR if the object is not of the
;; specified type.
(check-type line string)
(cond ((eql 0 (search "TIMESTAMP" line)) :timestamp)
((eql 0 (search "HTTP" line)) :http)
((eql 0 (search "LOGIN" line)) :login)
;; The function ERROR should be used for signaling serious conditions
;; and errors: if the condition is not handled, it halts program
;; execution and starts the Lisp debugger.
(t (error 'parse-log-line-error :line line
:reason :unknown-log-line-type))))
(define-test log-line-type-errors
(flet ((try-log-line-type (line)
(handler-case (log-line-type line)
(error (condition) condition))))
(assert-equal :timestamp (try-log-line-type "TIMESTAMP 2020-05-08 16:59:39"))
(assert-equal :http (try-log-line-type "HTTP GET / from 127.0.0.1"))
(assert-equal :login (try-log-line-type "LOGIN administrator:hunter2"))
(let ((condition (try-log-line-type "WARNING: 95% of disk space used")))
(assert-equal "WARNING: 95% of disk space used" (line condition))
(assert-equal :unknown-log-line-type (reason condition)))
(let ((condition (try-log-line-type 5555)))
(assert-equal 'string (type-error-expected-type condition))
(assert-equal 5555 (type-error-datum condition)))))

View File

@ -0,0 +1,68 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(define-test if
;; IF only evaluates and returns one branch of a conditional expression.
(assert-equal :true (if t :true :false))
(assert-equal :false (if nil :true :false))
;; This also applies to side effects that migh or might not be evaluated.
(let ((result))
(if t
(setf result :true)
(setf result :false))
(assert-equal :true result)
(if nil
(setf result :true)
(setf result :false))
(assert-equal :false result)))
(define-test when-unless
;; WHEN and UNLESS are like one-branched IF statements.
(let ((when-result nil)
(when-numbers '())
(unless-result nil)
(unless-numbers '()))
(dolist (x '(1 2 3 4 5 6 7 8 9 10))
(when (> x 5)
(setf when-result x)
(push x when-numbers))
(unless (> x 5)
(setf unless-result x)
(push x unless-numbers)))
(assert-equal 10 when-result)
(assert-equal '(10 9 8 7 6) when-numbers)
(assert-equal 5 unless-result)
(assert-equal '(5 4 3 2 1) unless-numbers)))
(define-test and-short-circuit
;; AND only evaluates forms until one evaluates to NIL.
(assert-equal 5
(let ((x 0))
(and
(setf x (+ 2 x))
(setf x (+ 3 x))
nil
(setf x (+ 4 x)))
x)))
(define-test or-short-circuit
;; OR only evaluates forms until one evaluates to non-NIL.
(assert-equal 2
(let ((x 0))
(or
(setf x (+ 2 x))
(setf x (+ 3 x))
nil
(setf x (+ 4 x)))
x)))

View File

@ -0,0 +1,95 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; In this project, we are going to define a CLOS class representing a simple
;;; set of dice. There are only two operations on the dice: reading the dice
;;; values and re-rolling their values.
(defclass dice-set ()
;; Fill in the blank with a proper slot definition.
((values :accessor dice-values :initform '())))
;;; This method might be unnecessary, depending on how you define the slots of
;;; DICE-SET.
;; (defmethod dice-values ((object dice-set))
;; ____)
(defmethod roll (count (object dice-set))
(check-type count (integer 1))
(setf (dice-values object)
(loop repeat count collect (1+ (random 6)))))
(define-test make-dice-set
(let ((dice (make-instance 'dice-set)))
(assert-true (typep dice 'dice-set))))
(define-test dice-are-six-sided
(let ((dice (make-instance 'dice-set)))
(roll 5 dice)
(assert-true (typep (dice-values dice) 'list))
(assert-equal 5 (length (dice-values dice)))
(dolist (die (dice-values dice))
(assert-true (typep die '(integer 1 6))))))
(define-test dice-values-do-not-change-without-rolling
(let ((dice (make-instance 'dice-set)))
(roll 100 dice)
(let ((dice-values-1 (dice-values dice))
(dice-values-2 (dice-values dice)))
(assert-equal dice-values-1 dice-values-2))))
(define-test roll-returns-new-dice-values
(let* ((dice (make-instance 'dice-set))
(dice-values (roll 100 dice)))
(assert-true (equal dice-values (dice-values dice)))))
(define-test dice-values-should-change-between-rolling
(let* ((dice (make-instance 'dice-set))
(first-time (roll 100 dice))
(second-time (roll 100 dice)))
(assert-false (equal first-time second-time))
(assert-true (equal second-time (dice-values dice)))))
(define-test different-dice-sets-have-different-values
(let* ((dice-1 (make-instance 'dice-set))
(dice-2 (make-instance 'dice-set)))
(roll 100 dice-1)
(roll 100 dice-2)
(assert-false (equal (dice-values dice-1) (dice-values dice-2)))))
(define-test different-numbers-of-dice
(let ((dice (make-instance 'dice-set)))
(assert-equal 5 (length (roll 5 dice)))
(assert-equal 100 (length (roll 100 dice)))
(assert-equal 1 (length (roll 1 dice)))))
(define-test junk-as-dice-count
(let ((dice (make-instance 'dice-set)))
(labels ((dice-failure (count)
(handler-case (progn (roll count dice)
(error "Test failure"))
(error (condition) condition)))
(test-dice-failure (value)
(let* ((condition (dice-failure value))
(expected-type (type-error-expected-type condition)))
(assert-true (typep condition 'type-error))
(assert-equal value (type-error-datum condition))
(assert-true (subtypep '(integer 1 6) expected-type)))))
(test-dice-failure 0)
(test-dice-failure "0")
(test-dice-failure :zero)
(test-dice-failure 18.0)
(test-dice-failure -7)
(test-dice-failure '(6 6 6)))))

View File

@ -0,0 +1,121 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; The most common equality predicates in Common Lisp are, in order of
;;; strictness, EQ, EQL, EQUAL, and EQUALP.
(define-test eq
;; EQ checks the identity of the two objects; it checks whether the two
;; objects are, in fact, one and the same object.
;; It is the fastest of the four; however, not guaranteed to work on numbers
;; and characters because of that.
(true-or-false? t (eq 'a 'a))
(true-or-false? nil (eq 3 3.0))
(true-or-false? nil (eq '(1 2) '(1 2)))
(true-or-false? nil (eq "Foo" "Foo"))
(true-or-false? nil (eq "Foo" (copy-seq "Foo")))
(true-or-false? nil (eq "FOO" "Foo")))
(define-test eql
;; EQL works like EQ, except it is specified to work for numbers and
;; characters.
;; Two numbers are EQL if they are of the same type and represent the same
;; number. Two characters are EQL if they represent the same character.
(true-or-false? t (eql 'a 'a))
(true-or-false? t (eql 3 3))
(true-or-false? nil (eql 3 3.0))
(true-or-false? nil (eql '(1 2) '(1 2)))
(true-or-false? nil (eql '(:a . :b) '(:a . :b)))
(true-or-false? t (eql #\S #\S))
(true-or-false? nil (eql "Foo" "Foo"))
(true-or-false? nil (eql "Foo" (copy-seq "Foo")))
(true-or-false? nil (eql "FOO" "Foo")))
(define-test equal
;; EQUAL works like EQL, except works differently for lists, strings, bit
;; vectors, and pathnames.
;; Two lists, strings, bit arrays, or pathnames are EQUAL if they have EQUAL
;; elements.
(true-or-false? t (equal 'a 'a))
(true-or-false? t (equal 3 3))
(true-or-false? nil (equal 3 3.0))
(true-or-false? t (equal '(1 2) '(1 2)))
(true-or-false? t (equal '(:a . :b) '(:a . :b)))
(true-or-false? nil (equal '(:a . :b) '(:a . :doesnt-match)))
(true-or-false? t (equal #\S #\S))
(true-or-false? t (equal "Foo" "Foo"))
(true-or-false? t (equal #*01010101 #*01010101))
(true-or-false? t (equal "Foo" (copy-seq "Foo")))
(true-or-false? nil (equal "FOO" "Foo"))
(true-or-false? t (equal #p"foo/bar/baz" #p"foo/bar/baz")))
(defstruct thing slot-1 slot-2)
(define-test equalp
;; EQUALP works like EQUAL, except it works differently for characters,
;; numbers, arrays, structures, and hash tables.
;; Two characters are EQUALP if they represent the same character, ignoring
;; the differences in character case.
;; Two numbers are EQUALP if they represent the same number, even if they are
;; of different types.
;; Two arrays are EQUALP if they have the same dimensions and their characters
;; are pairwise EQUALP.
;; Two structures are EQUALP if they are of the same class and their slots are
;; pairwise EQUALP.
;; We will contemplate hash tables in the HASH-TABLES lesson.
(true-or-false? t (equalp 'a 'a))
(true-or-false? t (equalp 3 3))
(true-or-false? t (equalp 3 3.0))
(true-or-false? t (equalp '(1 2) '(1 2)))
(true-or-false? t (equalp '(:a . :b) '(:a . :b)))
(true-or-false? nil (equalp '(:a . :b) '(:a . :doesnt-match)))
(true-or-false? t (equalp #\S #\S))
(true-or-false? t (equalp "Foo" "Foo"))
(true-or-false? t (equalp "Foo" (copy-seq "Foo")))
(true-or-false? t (equalp "FOO" "Foo"))
(true-or-false? t (equalp (make-array '(4 2) :initial-element 0)
(make-array '(4 2) :initial-element 0)))
(true-or-false? t (equalp (make-thing :slot-1 42 :slot-2 :forty-two)
(make-thing :slot-1 42 :slot-2 :forty-two))))
;;; In additional to the generic equality predicates, Lisp also provides
;;; type-specific predicates for numbers, strings, and characters.
(define-test =
;; The function = behaves just like EQUALP on numbers.
;; #C(... ...) is syntax sugar for creating a complex number.
(true-or-false? t (= 99.0 99 99.000 #C(99 0) #C(99.0 0.0)))
(true-or-false? nil (= 0 1 -1))
(true-or-false? t (= (/ 2 3) (/ 6 9) (/ 86 129))))
(define-test string=
;; The function STRING= behaves just like EQUAL on strings.
;; The function STRING-EQUAL behaves just like EQUALP on strings.
(true-or-false? t (string= "Foo" "Foo"))
(true-or-false? nil (string= "Foo" "FOO"))
(true-or-false? t (string-equal "Foo" "FOO"))
;; These functions accept additional keyword arguments that allow one to
;; only compare parts of the strings.
(true-or-false? t (string= "together" "frog" :start1 1 :end1 3
:start2 2))
(true-or-false? t (string-equal "together" "FROG" :start1 1 :end1 3
:start2 2)))
(define-test char=
;; The function CHAR= behaves just like EQL on characters.
;; The function CHAR-EQUAL behaves just like EQUALP on characters.
(true-or-false? t (char= #\A (char "ABCDEF" 0)))
(true-or-false? nil (char= #\A #\a))
(true-or-false? t (char-equal #\A (char "ABCDEF" 0)))
(true-or-false? t (char-equal #\A #\a)))

View File

@ -0,0 +1,66 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; In most imperative languages, the syntax of a function call has the function
;;; name succeeded by a list of arguments. In Lisp, the function name and
;;; arguments are all part of the same list, with the function name the first
;;; element of that list.
(define-test function-names
;; In these examples, +, -, *, and / are function names.
(assert-equal 5 (+ 2 3))
(assert-equal -2 (- 1 3))
(assert-equal 28 (* 7 4))
(assert-equal 25 (/ 100 4)))
(define-test numberp
;; NUMBERP is a predicate which returns true if its argument is a number.
(assert-equal t (numberp 5))
(assert-equal t (numberp 2.0))
(assert-equal nil (numberp "five")))
(define-test evaluation-order
;; Arguments to a function are evaluated before the function is called.
(assert-equal 9 (* (+ 1 2) (- 13 10))))
(define-test basic-comparisons
;; The below functions are boolean functions (predicates) that operate on
;; numbers.
(assert-equal t (> 25 4))
(assert-equal nil (< 8 2))
(assert-equal t (= 3 3))
(assert-equal t (<= 6 (/ 12 2)))
(assert-equal t (>= 20 (+ 1 2 3 4 5)))
(assert-equal t (/= 15 (+ 4 10))))
(define-test quote
;; Preceding a list with a quote (') will tell Lisp not to evaluate a list.
;; The quote special form suppresses normal evaluation, and instead returns
;; the literal list.
;; Evaluating the form (+ 1 2) returns the number 3, but evaluating the form
;; '(+ 1 2) returns the list (+ 1 2).
(assert-equal 3 (+ 1 2))
(assert-equal '(+ 1 2) '(+ 1 2))
(assert-equal '(+ 1 2) (list '+ 1 2))
;; The 'X syntax is syntactic sugar for (QUOTE X).
(true-or-false? t (equal '(/ 4 0) (quote (/ 4 0)))))
(define-test listp
;; LISTP is a predicate which returns true if the argument is a list.
(assert-equal t (listp '(1 2 3)))
(assert-equal nil (listp 100))
(assert-equal nil (listp "Hello world"))
(assert-equal t (listp nil))
(assert-equal nil (listp (+ 1 2)))
(assert-equal t (listp '(+ 1 2))))

View File

@ -0,0 +1,27 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; EXTRA CREDIT:
;;;
;;; Create a program that will play the Greed game.
;;; The full rules for the game are in the file extra-credit.txt.
;;;
;;; You already have a DICE-SET class and a score function you can use.
;;; Write a PLAYER class and a GAME class to complete the project.
;;;
;;; This is a free form assignment, so approach it however you desire.
(define-test play-greed
;; This page intentionally left blank.
(assert-true t))

View File

@ -0,0 +1,66 @@
= Playing Greed
Greed is a dice game played among 2 or more players, using 5
six-sided dice.
== Playing Greed
Each player takes a turn consisting of one or more rolls of the dice.
On the first roll of the game, a player rolls all five dice which are
scored according to the following:
Three 1's => 1000 points
Three 6's => 600 points
Three 5's => 500 points
Three 4's => 400 points
Three 3's => 300 points
Three 2's => 200 points
One 1 => 100 points
One 5 => 50 points
A single die can only be counted once in each roll. For example,
a "5" can only count as part of a triplet (contributing to the 500
points) or as a single 50 points, but not both in the same roll.
Example Scoring
Throw Score
--------- ------------------
5 1 3 4 1 50 + 2 * 100 = 250
1 1 1 3 1 1000 + 100 = 1100
2 4 4 5 4 400 + 50 = 450
The dice not contributing to the score are called the non-scoring
dice. "3" and "4" are non-scoring dice in the first example. "3" is
a non-scoring die in the second, and "2" is a non-score die in the
final example.
After a player rolls and the score is calculated, the scoring dice are
removed and the player has the option of rolling again using only the
non-scoring dice. If all of the thrown dice are scoring, then the
player may roll all 5 dice in the next roll.
The player may continue to roll as long as each roll scores points. If
a roll has zero points, then the player loses not only their turn, but
also accumulated score for that turn. If a player decides to stop
rolling before rolling a zero-point roll, then the accumulated points
for the turn is added to his total score.
== Getting "In The Game"
Before a player is allowed to accumulate points, they must get at
least 300 points in a single turn. Once they have achieved 300 points
in a single turn, the points earned in that turn and each following
turn will be counted toward their total score.
== End Game
Once a player reaches 3000 (or more) points, the game enters the final
round where each of the other players gets one more turn. The winner
is the player with the highest score after the final round.
== References
Greed is described on Wikipedia at
http://en.wikipedia.org/wiki/Greed_(dice_game), however the rules are
a bit different from the rules given here.

View File

@ -0,0 +1,109 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; The function FORMAT is used to create formatted output. It is similar to
;;; the C function printf().
;;; See http://www.gigamonkeys.com/book/a-few-format-recipes.html
;;; T as the first argument to FORMAT prints the string to standard output.
;;; NIL as the first argument to FORMAT causes it to return the string.
(define-test format-basic
;; If there are no format directives in the string, FORMAT will return
;; a string that is STRING= to its format control.
(assert-equal "Lorem ipsum dolor sit amet"
(format nil "Lorem ipsum dolor sit amet")))
(define-test format-aesthetic
;; The ~A format directive creates aesthetic output.
(assert-equal "This is the number 42"
(format nil "This is the number ~A" 42))
(assert-equal "This is the keyword FOO"
(format nil "This is the keyword ~A" :foo))
(assert-equal "(/ 24 (- 3 (/ 8 3))) evaluates to 72"
(format nil "~A evaluates to ~A"
'(/ 24 (- 3 (/ 8 3)))
(/ 24 (- 3 (/ 8 3)))))
(assert-equal "This is the character C"
(format nil "This is the character ~A" #\C))
(assert-equal "In a galaxy far far away"
(format nil "In a ~A" "galaxy far far away")))
(define-test format-standard
;; The ~S format directive prints objects with escape characters.
;; Not all Lisp objects require to be escaped.
(assert-equal "This is the number 42" (format nil "This is the number ~S" 42))
(assert-equal "(/ 24 (- 3 (/ 8 3))) evaluates to 72"
(format nil "~S evaluates to ~S"
'(/ 24 (- 3 (/ 8 3)))
(/ 24 (- 3 (/ 8 3)))))
;; Keywords are printed with their leading colon.
(assert-equal "This is the keyword :FOO"
(format nil "This is the keyword ~S" :foo))
;; Characters are printed in their #\X form. The backslash will need to be
;; escaped inside the printed string, just like in "#\\X".
(assert-equal "This is the character #\\C"
(format nil "This is the character ~S" #\C))
;; Strings include quote characters, which must be escaped:
;; such a string might look in code like "foo \"bar\"".
(assert-equal "In a \"galaxy far far away\""
(format nil "In a ~S" "galaxy far far away")))
(define-test format-radix
;; The ~B, ~O, ~D, and ~X radices print numbers in binary, octal, decimal, and
;; hexadecimal notation.
(assert-equal "This is the number 101010"
(format nil "This is the number ~B" 42))
(assert-equal "This is the number 52"
(format nil "This is the number ~O" 42))
(assert-equal "This is the number 42"
(format nil "This is the number ~D" 42))
(assert-equal "This is the number 2A"
(format nil "This is the number ~X" 42))
;; We can specify a custom radix by using the ~R directive.
(assert-equal "This is the number 1120"
(format nil "This is the number ~3R" 42))
;; It is possible to print whole forms this way.
(let ((form '(/ 24 (- 3 (/ 8 3))))
(result (/ 24 (- 3 (/ 8 3)))))
(assert-equal "(/ 11000 (- 11 (/ 1000 11))) evaluates to 1001000"
(format nil "~B evaluates to ~B" form result))
(assert-equal "(/ 30 (- 3 (/ 10 3))) evaluates to 110"
(format nil "~O evaluates to ~O" form result))
(assert-equal "(/ 24 (- 3 (/ 8 3))) evaluates to 72"
(format nil "~D evaluates to ~D" form result))
(assert-equal "(/ 18 (- 3 (/ 8 3))) evaluates to 48"
(format nil "~X evaluates to ~X" form result))
(assert-equal "(/ 220 (- 10 (/ 22 10))) evaluates to 2200"
(format nil "~3R evaluates to ~3R" form result))))
(define-test format-iteration
;; The ~{ and ~} directives iterate over a list.
(assert-equal "[1][2][3][4][5][6]" (format nil "~{[~A]~}" '(1 2 3 4 5 6)))
(assert-equal "[1 2][3 4][5 6]" (format nil "~{[~A ~A]~}" '(1 2 3 4 5 6)))
;; The directive ~^ aborts iteration when no more elements remain.
(assert-equal "[1], [2], [3], [4], [5], [6]"
(format nil "~{[~A]~^, ~}" '(1 2 3 4 5 6))))
(define-test format-case
;; The ~( and ~) directives adjust the string case.
(assert-equal "the quick brown fox"
(format nil "~(~A~)" "The QuIcK BROWN fox"))
;; Some FORMAT directives can be further adjusted with the : and @ modifiers.
(assert-equal "The Quick Brown Fox"
(format nil "~:(~A~)" "The QuIcK BROWN fox"))
(assert-equal "The quick brown fox"
(format nil "~@(~A~)" "The QuIcK BROWN fox"))
(assert-equal "THE QUICK BROWN FOX"
(format nil "~:@(~A~)" "The QuIcK BROWN fox")))

View File

@ -0,0 +1,184 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(defun some-named-function (a b)
(+ a b))
(define-test call-a-function
;; DEFUN can be used to define global functions.
(assert-equal 9 (some-named-function 4 5))
;; FLET can be used to define local functions.
(flet ((another-named-function (a b) (* a b)))
(assert-equal 20 (another-named-function 4 5)))
;; LABELS can be used to define local functions which can refer to themselves
;; or each other.
(labels ((recursive-function (a b)
(if (or (= 0 a) (= 0 b))
1
(+ (* a b) (recursive-function (1- a) (1- b))))))
(assert-equal 41 (recursive-function 4 5))))
(define-test shadow-a-function
(assert-eq 18 (some-named-function 7 11))
;; FLET and LABELS can shadow function definitions.
(flet ((some-named-function (a b) (* a b)))
(assert-equal 77 (some-named-function 7 11)))
(assert-equal 18 (some-named-function 7 11)))
(defun function-with-optional-parameters (&optional (a 2) (b 3) c)
;; If an optional argument to a function is not provided, it is given its
;; default value, or NIL, if no default value is specified.
(list a b c))
(define-test optional-parameters
(assert-equal '(42 24 4224) (function-with-optional-parameters 42 24 4224))
(assert-equal '(42 24 nil) (function-with-optional-parameters 42 24))
(assert-equal '(42 3 nil) (function-with-optional-parameters 42))
(assert-equal '(2 3 nil) (function-with-optional-parameters)))
(defun function-with-optional-indication
(&optional (a 2 a-provided-p) (b 3 b-provided-p))
;; It is possible to check whether an optional argument was provided.
(list a a-provided-p b b-provided-p))
(define-test optional-indication
(assert-equal '(42 t 24 t) (function-with-optional-indication 42 24))
(assert-equal '(42 t 3 nil) (function-with-optional-indication 42))
(assert-equal '(2 nil 3 nil) (function-with-optional-indication)))
(defun function-with-rest-parameter (&rest x)
;; A rest parameter gathers all remaining parameters in a list.
x)
(define-test rest-parameter
(assert-equal '() (function-with-rest-parameter))
(assert-equal '(1) (function-with-rest-parameter 1))
(assert-equal '(1 :two 333) (function-with-rest-parameter 1 :two 333)))
(defun function-with-keyword-parameters (&key (a :something) b c)
;; A keyword parameters is similar to an optional parameter, but is provided
;; by a keyword-value pair.
(list a b c))
(define-test keyword-parameters ()
(assert-equal '(:something nil nil) (function-with-keyword-parameters))
(assert-equal '(11 22 33) (function-with-keyword-parameters :a 11 :b 22 :c 33))
;; It is not necessary to specify all keyword parameters.
(assert-equal '(:something 22 nil) (function-with-keyword-parameters :b 22))
;; Keyword argument order is not important.
(assert-equal '(0 22 -5/2)
(function-with-keyword-parameters :b 22 :c -5/2 :a 0))
;; Lisp handles duplicate keyword parameters.
(assert-equal '(:something 22 nil)
(function-with-keyword-parameters :b 22 :b 40 :b 812)))
(defun function-with-keyword-indication
(&key (a 2 a-provided-p) (b 3 b-provided-p))
;; It is possible to check whether a keyword argument was provided.
(list a a-provided-p b b-provided-p))
(define-test keyword-indication
(assert-equal '(2 nil 3 nil) (function-with-keyword-indication))
(assert-equal '(3 t 4 t) (function-with-keyword-indication :a 3 :b 4))
(assert-equal '(11 t 22 t) (function-with-keyword-indication :a 11 :b 22))
(assert-equal '(2 nil 22 t) (function-with-keyword-indication :b 22))
(assert-equal '(0 t 22 t) (function-with-keyword-indication :b 22 :a 0)))
(defun function-with-funky-parameters (a &rest x &key b (c a c-provided-p))
;; Lisp functions can have surprisingly complex lambda lists.
;; A &rest parameter must come before &key parameters.
(list a b c c-provided-p x))
(define-test funky-parameters
(assert-equal '(1 nil 1 nil nil) (function-with-funky-parameters 1))
(assert-equal '(1 2 1 nil (:b 2)) (function-with-funky-parameters 1 :b 2))
(assert-equal '(1 2 3 t (:b 2 :c 3))
(function-with-funky-parameters 1 :b 2 :c 3))
(assert-equal '(1 2 3 t (:c 3 :b 2))
(function-with-funky-parameters 1 :c 3 :b 2)))
(define-test lambda
;; A list form starting with the symbol LAMBDA denotes an anonymous function.
;; It is possible to call that function immediately or to store it for later
;; use.
(let ((my-function (lambda (a b) (* a b))))
(assert-equal 99 (funcall my-function 11 9)))
;; A LAMBDA form is allowed to take the place of a function name.
(assert-equal 19 ((lambda (a b) (+ a b)) 10 9))
(let ((functions (list (lambda (a b) (+ a b))
(lambda (a b) (- a b))
(lambda (a b) (* a b))
(lambda (a b) (/ a b)))))
(assert-equal 35 (funcall (first functions) 2 33))
(assert-equal -31 (funcall (second functions) 2 33))
(assert-equal 66 (funcall (third functions) 2 33))
(assert-equal 2/33 (funcall (fourth functions) 2 33))))
(define-test lambda-with-optional-parameters
(assert-equal 19 ((lambda (a &optional (b 100)) (+ a b)) 10 9))
(assert-equal 110 ((lambda (a &optional (b 100)) (+ a b)) 10)))
(defun make-adder (x)
;; MAKE-ADDER will create a function that closes over the parameter X.
;; The parameter will be remembered as a part of the environment of the
;; returned function, which will continue refering to it.
(lambda (y) (+ x y)))
(define-test lexical-closures
(let ((adder-100 (make-adder 100))
(adder-500 (make-adder 500)))
;; ADD-100 and ADD-500 now close over different values.
(assert-equal 103 (funcall adder-100 3))
(assert-equal 503 (funcall adder-500 3))))
(defun make-reader-and-writer (x)
;; Both returned functions will refer to the same place.
(list (function (lambda () x))
(function (lambda (y) (setq x y)))))
(define-test lexical-closure-interactions
;; The macro DESTRUCTURING-BIND is like LET, except it binds the variables
;; listed in its first argument to the parts of the list returned by the form
;; that is its second argument.
(destructuring-bind (reader-1 writer-1) (make-reader-and-writer 1)
(destructuring-bind (reader-2 writer-2) (make-reader-and-writer :one)
(assert-equal 1 (funcall reader-1))
(funcall writer-1 0)
(assert-equal 0 (funcall reader-1))
;; The two different function pairs refer to different places.
(assert-equal :one (funcall reader-2))
(funcall writer-2 :zero)
(assert-equal :zero (funcall reader-2)))))
(define-test apply
;; The function APPLY applies a function to a list of arguments.
(let ((function (lambda (x y z) (+ x y z))))
(assert-equal 123 (apply function '(100 20 3))))
;; FUNCTION is a special operator that retrieves function objects, defined
;; both globally and locally. #'X is syntax sugar for (FUNCTION X).
(assert-equal 3 (apply (function +) '(1 2)))
(assert-equal -1 (apply #'- '(1 2)))
;; Only the last argument to APPLY must be a list.
(assert-equal 6 (apply #'+ 1 2 '(3)))
(assert-equal 4 (apply #'max 1 2 3 4 '())))
(define-test funcall
;; The function FUNCALL calls a function with arguments, not expecting a final
;; list of arguments.
(let ((function (lambda (x y z) (+ x y z))))
(assert-equal 321 (funcall function 300 20 1)))
(assert-equal 3 (funcall (function +) 1 2))
(assert-equal -1 (funcall #'- 1 2))
(assert-equal 6 (funcall #'+ 1 2 3))
(assert-equal 4 (funcall #'max 1 2 3 4)))

View File

@ -0,0 +1,111 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; A hash table data structure is sometimes known as a dictionary.
(define-test make-hash-table
(let ((my-hash-table (make-hash-table)))
(true-or-false? t (typep my-hash-table 'hash-table))
(true-or-false? t (hash-table-p my-hash-table))
(true-or-false? nil (hash-table-p (make-array '(3 3 3))))
;; The function HASH-TABLE-COUNT returns the number of entries currently
;; contained in a hash table.
(assert-equal 0 (hash-table-count my-hash-table))))
(define-test gethash
;; The function GETHASH can be used to access hash table values.
(let ((cube-roots (make-hash-table)))
;; We add the key-value pair 1 - "uno" to the hash table.
(setf (gethash 1 cube-roots) "uno")
(assert-equal "uno" (gethash 1 cube-roots))
(assert-equal 1 (hash-table-count cube-roots))
(setf (gethash 8 cube-roots) 2)
(setf (gethash -3 cube-roots) -27)
(assert-equal -27 (gethash -3 cube-roots))
(assert-equal 3 (hash-table-count cube-roots))
;; GETHASH returns a secondary value that is true if the key was found in
;; the hash-table and false otherwise.
(multiple-value-bind (value foundp) (gethash 8 cube-roots)
(assert-equal 2 value)
(assert-equal t foundp))
(multiple-value-bind (value foundp) (gethash 125 cube-roots)
(assert-equal nil value)
(assert-equal nil foundp))))
(define-test hash-table-test
;; A hash table can be constructed with different test predicates.
;; The programmer may choose between EQ, EQL, EQUAL, and EQUALP to get the
;; best performance and expected results from the hash table.
;; The default test predicate is EQL.
(let ((eq-table (make-hash-table :test #'eq))
(eql-table (make-hash-table))
(equal-table (make-hash-table :test #'equal))
(equalp-table (make-hash-table :test #'equalp)))
;; We will define four variables whose values are strings.
(let* ((string "one")
(same-string string)
(string-copy (copy-seq string))
(string-upcased "ONE"))
;; We will insert the value of each variable into each hash table.
(dolist (thing (list string same-string string-copy string-upcased))
(dolist (hash-table (list eq-table eql-table equal-table equalp-table))
(setf (gethash thing hash-table) t))))
;; How many entries does each hash table contain?
(assert-equal 3 (hash-table-count eq-table))
(assert-equal 3 (hash-table-count eql-table))
(assert-equal 2 (hash-table-count equal-table))
(assert-equal 1 (hash-table-count equalp-table))))
(define-test hash-table-equality
;; EQUALP considers two hash tables to be equal if they have the same test and
;; if its key-value pairs are the same under that test.
(let ((hash-table-1 (make-hash-table :test #'equal))
(hash-table-2 (make-hash-table :test #'equal)))
(setf (gethash "one" hash-table-1) "yat")
(setf (gethash "one" hash-table-2) "yat")
(setf (gethash "two" hash-table-1) "yi")
(setf (gethash "two" hash-table-2) "yi")
(true-or-false? nil (eq hash-table-1 hash-table-2))
(true-or-false? nil (equal hash-table-1 hash-table-2))
(true-or-false? t (equalp hash-table-1 hash-table-2))))
(define-test i-will-make-it-equalp
;; Disabled on ECL due to a conformance bug.
;; See https://gitlab.com/embeddable-common-lisp/ecl/-/issues/587
#-ecl
(let ((hash-table-1 (make-hash-table :test #'equal))
(hash-table-2 (make-hash-table :test #'equal)))
(setf (gethash "one" hash-table-1) "uno"
(gethash "two" hash-table-1) "dos")
(setf (gethash "one" hash-table-2) "eins"
(gethash "two" hash-table-2) "zwei")
(assert-false (equalp hash-table-1 hash-table-2))
;; Change the first hash table to be EQUALP to the second one.
(setf (gethash "one" hash-table-1) "eins"
(gethash "two" hash-table-1) "zwei")
(assert-true (equalp hash-table-1 hash-table-2))))
(define-test make-your-own-hash-table
;; Make your own hash table that satisfies the test.
(let ((colors (make-hash-table :test #'equal)))
;; You will need to modify your hash table after you create it.
(setf (gethash "blue" colors) '(0 0 1)
(gethash "green" colors) '(0 1 0)
(gethash "red" colors) '(1 0 0)
(gethash "black" colors) '(0 0 0))
(assert-equal (hash-table-count colors) 4)
(let ((values (list (gethash "blue" colors)
(gethash "green" colors)
(gethash "red" colors))))
(assert-equal values '((0 0 1) (0 1 0) (1 0 0))))))

View File

@ -0,0 +1,75 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; Lisp has multiple options for iteration.
;;; This set of koans will introduce some of the most common ones.
(define-test dolist
(let ((numbers '(4 8 15 16 23 42)))
;; The macro DOLIST binds a variable to subsequent elements of a list.
(let ((sum 0))
(dolist (number numbers)
;; (INCF PLACE N) is equivalent to (SETF PLACE (+ N PLACE)).
(incf sum number))
(assert-equal 108 sum))
;; DOLIST can optionally return a value.
(let ((sum 0))
(assert-equal 108 (dolist (number numbers sum)
(incf sum number))))))
(define-test dotimes
;; The macro DOTIMES binds a variable to subsequent integers from 0 to
;; (1- COUNT).
(let ((stack '()))
(dotimes (i 5)
(push i stack))
(assert-equal '(4 3 2 1 0) stack))
;; DOTIMES can optionally return a value.
(let ((stack '()))
(assert-equal '(4 3 2 1 0) (dotimes (i 5 stack)
(push i stack)))))
(define-test do
;; The macro DO accepts a list of variable bindings, a termination test with
;; epilogue forms, and Lisp code that should be executed on each iteration.
(let ((result '()))
(do ((i 0 (1+ i)))
((> i 5))
(push i result))
(assert-equal '(0 1 2 3 4 5) (nreverse result)))
;; The epilogue of DO can return a value.
(let ((result (do ((i 0 (1+ i))
;; A variable bound by DO noes not need to be updated on
;; each iteration.
(result '()))
((> i 5) (nreverse result))
(push i result))))
(assert-equal '(0 1 2 3 4 5) result)))
(define-test loop-basic-form
;; The macro LOOP in its simple form loops forever. It is possible to stop the
;; looping by calling the RETURN special form.
(let ((counter 0))
(loop (incf counter)
(when (>= counter 100)
(return counter)))
(assert-equal 100 counter))
;; The RETURN special form can return a value out of a LOOP.
(let ((counter 0))
(assert-equal 100 (loop (incf counter)
(when (>= counter 100)
(return counter)))))
;; The extended form of LOOP will be contemplated in a future koan.
)

View File

@ -0,0 +1,62 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(define-test let
;; The LET form establishes a lexical extent within which new variables are
;; created: a symbol that names a variable becomes bound to a value.
(let ((x 10)
(y 20))
(assert-equal 30 (+ x y))
;; It is possible to shadow previously visible bindings.
(let ((y 30))
(assert-equal 40 (+ x y)))
(assert-equal 30 (+ x y)))
;; Variables bound by LET have a default value of NIL.
(let (x)
(assert-equal nil x)))
(define-test let-versus-let*
;; LET* is similar to LET, except the bindings are established sequentially,
;; and a binding may use bindings that were established before it.
(let ((x 10)
(y 20))
(let ((x (+ y 100))
(y (+ x 100)))
(assert-equal 120 x)
(assert-equal 110 y))
(let* ((x (+ y 100))
(y (+ x 100)))
;; Which X is used to compute the value of Y?
(assert-equal 120 x)
(assert-equal 220 y))))
(define-test let-it-be-equal
;; Fill in the LET and LET* to get the tests to pass.
(let ((a 1)
(b :two)
(c "Three"))
(let ((a 100)
(b 200)
(c "Jellyfish"))
(assert-equal a 100)
(assert-equal b 200)
(assert-equal c "Jellyfish"))
(let* ((a 121)
(b 200)
;; In this third binding, you are allowed to use the variables bound
;; by the previous two LET* bindings.
(c (+ a (/ b a))))
(assert-equal a 121)
(assert-equal b 200)
(assert-equal c (+ a (/ b a))))))

View File

@ -0,0 +1,146 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; A singly linked list is the basic build block of Lisp. Each node of such a
;;; list is called a "cons cell" in Lisp. Each cons cell has two slots: a CAR,
;;; often used to hold an element of a list, and a CDR, often used to reference
;;; the next cons cell.
(define-test how-to-make-lists
(let (;; Literal lists can be passed by quoting them.
(fruits '(orange pomello clementine))
;; Freshly constructed lists can be passed using the LIST function.
(some-evens (list (* 2 1) (* 2 2) (* 2 3)))
;; Lists can also be passed using quotes and dot notation...
(long-numbers '(16487302 . (3826700034 . (10000000 . nil))))
;; ...or by using the function CONS.
(names (cons "Matthew" (cons "Mark" (cons "Margaret" '())))))
;; Try filling in the below blanks in different ways.
(assert-equal '(orange pomello clementine) fruits)
(assert-equal '(2 4 6) some-evens)
(assert-equal '(16487302 3826700034 10000000) long-numbers)
(assert-equal '("Matthew" "Mark" "Margaret") names)))
(define-test cons-tructing-lists
;; The function CONS can be used to add new elements at the beginning of
;; an existing list.
(let ((nums '()))
(setf nums (cons :one nums))
(assert-equal '(:one) nums)
(setf nums (cons :two nums))
(assert-equal '(:two :one) nums)
;; Lists can contain anything, even objects of different types.
(setf nums (cons 333 nums))
(assert-equal '(333 :two :one) nums)
;; Lists can contain other lists, too.
(setf nums (cons (list "some" "strings") nums))
(assert-equal '(("some" "strings") 333 :two :one) nums)))
(define-test car-and-cdr
;; We may use functions CAR and CDR (or, alternatively, FIRST and REST) to
;; access the two slots of a cons cell.
(let ((x (cons 1 2)))
(assert-equal 1 (car x))
(assert-equal 2 (cdr x)))
;; Calls to CAR and CDR are often intertwined to extract data from a nested
;; cons structure.
(let ((structure '((1 2) (("foo" . "bar")))))
(assert-equal '(1 2) (car structure))
(assert-equal '(("foo" . "bar")) (car (cdr structure)))
(assert-equal "bar" (cdr (car (car (cdr structure)))))
;; Lisp defines shorthand functions for up to four such nested calls.
(assert-equal '(1 2) (car structure))
(assert-equal '(("foo" . "bar")) (cadr structure))
(assert-equal "bar" (cdaadr structure))))
(define-test push-pop
;; PUSH and POP are macros similar to SETF, as both of them operate on places.
(let ((place '(10 20 30 40)))
;; PUSH sets the value of the place to a new cons cell containing some value
;; in its CAR.
(push 0 place)
(assert-equal '(0 10 20 30 40) place)
;; POP removes a single cons cell from a place, sets the place to its CDR,
;; and returns the value from its CAR.
(let ((value (pop place)))
(assert-equal 0 value)
(assert-equal '(10 20 30 40) place))
;; The return value of POP can be discarded to simply "remove" a single cons
;; cell from a place.
(pop place)
(let ((value (pop place)))
(assert-equal 20 value)
(assert-equal '(30 40) place))))
(define-test append-nconc
;; The functions APPEND and NCONC appends one list to the end of another.
;; While APPEND creates new lists, NCONC modifies existing ones; therefore
;; APPEND can be used on literals, but NCONC needs fresh lists.
(assert-equal '(:a :b :c) (append '(:a :b) '(:c)))
(assert-equal '(:a :b :c) (nconc (list :a :b) (list :c)))
(let ((list-1 (list 1 2 3))
(list-2 (list 4 5 6)))
;; Both APPEND and NCONC return the appended list, but the interesting part
;; is what happens when we try to use the original variables passed to them.
(assert-equal '(1 2 3 4 5 6) (append list-1 list-2))
(assert-equal '(1 2 3) list-1)
(assert-equal '(4 5 6) list-2)
(assert-equal '(1 2 3 4 5 6) (nconc list-1 list-2))
(assert-equal '(1 2 3 4 5 6) list-1)
(assert-equal '(4 5 6) list-2)))
(define-test accessing-list-elements
(let ((noms '("peanut" "butter" "and" "jelly")))
;; Common Lisp defines accessor functions for lists: FIRST, SECOND, ...,
;; up to TENTH.
(assert-equal "peanut" (first noms))
(assert-equal "butter" (second noms))
(assert-equal "jelly" (fourth noms))
;; The function LAST returns the last cons cell of a list.
(assert-equal '("jelly") (last noms))
;; The function NTH returns the n-th element of a list.
(assert-equal "butter" (nth 1 noms))
(assert-equal "peanut" (nth 0 noms))
(assert-equal "jelly" (nth 3 noms))))
(define-test cons-tructing-improper-lists
;; A proper list is a list whose final CDR ends with NIL.
;; An improper list either has a non-NIL value in its final CDR or does not
;; have a final CDR due to a cycle in its structure.
(let (;; We can construct non-cyclic improper lists using LIST*...
(x (list* 1 2 3 4 5))
;; ...or pass them as literals via dot notation.
(y '(6 7 8 9 . 0)))
(assert-equal '(4 . 5) (last x))
(assert-equal '(9 . 0) (last y)))
;; We can create a cyclic list by changing the last CDR of a list to refer to
;; another cons cell
(let ((list (list 1 2 3 4 5))
(cyclic-list (list 1 2 3 4 5)))
(setf (cdr (last cyclic-list)) cyclic-list)
;; Function LIST-LENGTH returns NIL if a list is cyclic.
(assert-equal 5 (list-length list))
(assert-equal nil (list-length cyclic-list))
;; Many Lisp functions operate only on proper lists.
;; The function NTH is not one of them; it can be used to retrieve elements
;; of cyclic lists.
(assert-equal 2 (nth 101 cyclic-list))))
(define-test slicing-lists
;; The function SUBSEQ returns a subsequence of a list.
(let ((noms (list "peanut" "butter" "and" "jelly")))
(assert-equal '("peanut") (subseq noms 0 1))
(assert-equal '("peanut" "butter") (subseq noms 0 2))
(assert-equal '() (subseq noms 2 2))
(assert-equal '("and" "jelly") (subseq noms 2))))

View File

@ -0,0 +1,140 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; The extended for of LOOP allows for advanced iteration.
;;; See http://www.gigamonkeys.com/book/loop-for-black-belts.html
(define-test loop-collect
;; LOOP can collect the results in various ways.
(let* ((result-1 (loop for letter in '(#\a #\b #\c #\d) collect letter))
(result-2 (loop for number in '(1 2 3 4 5) sum number))
(result-3 (loop for list in '((foo) (bar) (baz)) append list)))
(assert-equal '(#\a #\b #\c #\d) result-1)
(assert-equal 15 result-2)
(assert-equal '(foo bar baz) result-3)))
(define-test loop-multiple-variables
;; With multiple FOR clauses, the loop ends when any of the provided lists are
;; exhausted.
(let* ((letters '(:a :b :c :d))
(result (loop for letter in letters
for i from 1 to 1000
collect (list i letter))))
(assert-equal '((1 :a) (2 :b) (3 :c) (4 :d)) result)))
(define-test loop-in-versus-loop-on
;; Instead of iterating over each element of a list, we can iterate over each
;; cons cell of a list.
(let* ((letters '(:a :b :c))
(result-in (loop for thing in letters collect thing))
(result-on (loop for thing on letters collect thing)))
(assert-equal '(:a :b :c) result-in)
(assert-equal '((:a :b :c) (:b :c) (:c)) result-on)))
(define-test loop-for-by
;; Numeric iteration can go faster or slower if we use the BY keyword.
(let* ((result (loop for i from 0 to 30 by 5 collect i)))
(assert-equal '(0 5 10 15 20 25 30) result)))
(define-test loop-counting-backwards
;; We can count downwards instead of upwards by using DOWNTO instead of TO.
(let ((result (loop for i from 5 downto -5 collect i)))
(assert-equal '(5 4 3 2 1 0 -1 -2 -3 -4 -5) result)))
(define-test loop-list-by
;; List iteration can go faster or slower if we use the BY keyword.
(let* ((letters '(:a :b :c :d :e :f))
(result (loop for letter in letters collect letter))
(result-cdr (loop for letter in letters by #'cdr collect letter))
(result-cddr (loop for letter in letters by #'cddr collect letter))
(result-cdddr (loop for letter in letters by #'cdddr collect letter)))
(assert-equal '(:a :b :c :d :e :f) result)
(assert-equal '(:a :b :c :d :e :f) result-cdr)
(assert-equal '(:a :c :e) result-cddr)
(assert-equal '(:a :d) result-cdddr)))
(define-test loop-across
;; LOOP can iterate over a vector with the ACROSS keyword.
(let* ((vector (make-array '(5) :initial-contents '(0 1 2 3 4)))
(result (loop for number across vector collect number)))
(assert-equal '(0 1 2 3 4) result)))
(define-test loop-over-2d-array
(let ((array (make-array '(3 2) :initial-contents '((0 1) (2 3) (4 5)))))
;; LOOP can be combined with ROW-MAJOR-AREF to iterate over the contents of
;; a multidimensional array.
(let* ((result (loop for i from 0 below (array-total-size array)
collect (row-major-aref array i))))
(assert-equal '(0 1 2 3 4 5) result))
;; It is always possible to resort to nested loops.
(let* ((result (loop with max-i = (array-dimension array 0)
for i from 0 below max-i
collect (loop with max-j = (array-dimension array 1)
for j from 0 below max-j
collect (expt (aref array i j) 2)))))
(assert-equal '((0 1) (4 9) (16 25)) result))))
(define-test loop-hash-table
(let ((book-heroes (make-hash-table :test 'equal)))
(setf (gethash "The Hobbit" book-heroes) "Bilbo"
(gethash "Where The Wild Things Are" book-heroes) "Max"
(gethash "The Wizard Of Oz" book-heroes) "Dorothy"
(gethash "The Great Gatsby" book-heroes) "James Gatz")
;; LOOP can iterate over hash tables.
(let ((pairs-in-table (loop for key being the hash-keys of book-heroes
using (hash-value value)
collect (list key value))))
(assert-equal 4 (length pairs-in-table))
(true-or-false? t (find '("The Hobbit" "Bilbo") pairs-in-table
:test #'equal)))))
(define-test loop-statistics
;; LOOP can perform basics statistics on the collected elements.
(let ((result (loop for x in '(1 2 4 8 16 32)
collect x into collected
count x into counted
sum x into summed
maximize x into maximized
minimize x into minimized
finally (return (list collected counted summed
maximized minimized)))))
(destructuring-bind (collected counted summed maximized minimized) result
(assert-equal '(1 2 4 8 16 32) collected)
(assert-equal 6 counted)
(assert-equal 63 summed)
(assert-equal 32 maximized)
(assert-equal 1 minimized))))
(define-test loop-destructuring
;; LOOP can bind multiple variables on each iteration step.
(let* ((count 0)
(result (loop for (a b) in '((1 9) (2 8) (3 7) (4 6))
do (incf count)
collect (+ a b))))
(assert-equal 4 count)
(assert-equal '(10 10 10 10) result)))
(define-test loop-conditional-execution
(let ((numbers '(1 1 2 3 5 8 13 21)))
;; LOOP can execute some actions conditionally.
(let ((result (loop for x in numbers
when (evenp x) sum x)))
(assert-equal 10 result))
(let ((result (loop for x in numbers
unless (evenp x) sum x)))
(assert-equal 44 result))
(flet ((greater-than-10-p (x) (> x 10)))
(let ((result (loop for x in numbers
when (greater-than-10-p x) sum x)))
(assert-equal 34 result)))))

View File

@ -0,0 +1,123 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; A Lisp macro is a function that accepts Lisp data and produces a Lisp form.
;;; When the macro is called, its macro function receives unevaluated arguments
;;; and may use them to produce a new Lisp form. This form is then spliced in
;;; place of the original macro call and is then evaluated.
(defmacro my-and (&rest forms)
;; We use a LABELS local function to allow for recursive expansion.
(labels ((generate (forms)
(cond ((null forms) 'nil)
((null (rest forms)) (first forms))
(t `(when ,(first forms)
,(generate (rest forms)))))))
(generate forms)))
(define-test my-and
;; ASSERT-EXPANDS macroexpands the first form once and checks if it is equal
;; to the second form.
(assert-expands (my-and (= 0 (random 6)) (error "Bang!"))
'(when (= 0 (random 6)) (error "Bang!")))
(assert-expands (my-and (= 0 (random 6))
(= 0 (random 6))
(= 0 (random 6))
(error "Bang!"))
'(when (= 0 (random 6))
(when (= 0 (random 6))
(when (= 0 (random 6))
(error "Bang!"))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; A common macro pitfall is capturing a variable defined by the user.
(define-test variable-capture
(macrolet ((for ((var start stop) &body body)
`(do ((,var ,start (1+ ,var))
(limit ,stop))
((> ,var limit))
,@body)))
(let ((limit 10)
(result '()))
(for (i 0 3)
(push i result)
(assert-equal 3 limit))
(assert-equal '(0 1 2 3) (nreverse result)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Another pitfall is evaluating some forms multiple times where they are only
;;; meant to be evaluated once.
(define-test multiple-evaluation
;; We use MACROLET for defining a local macro.
(macrolet ((for ((var start stop) &body body)
`(do ((,var ,start (1+ ,var)))
((> ,var ,stop))
,@body)))
(let ((side-effects '())
(result '()))
;; Our functions RETURN-0 and RETURN-3 have side effects.
(flet ((return-0 () (push 0 side-effects) 0)
(return-3 () (push 3 side-effects) 3))
(for (i (return-0) (return-3))
(push i result)))
(assert-equal '(0 1 2 3) (nreverse result))
(assert-equal '(0 3 3 3 3 3) (nreverse side-effects)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Yet another pitfall is not respecting the evaluation order of the macro
;;; subforms.
(define-test wrong-evaluation-order
(macrolet ((for ((var start stop) &body body)
;; The function GENSYM creates GENerated SYMbols, guaranteed to
;; be unique in the whole Lisp system. Because of that, they
;; cannot capture other symbols, preventing variable capture.
(let ((limit (gensym "LIMIT")))
`(do ((,limit ,stop)
(,var ,start (1+ ,var)))
((> ,var ,limit))
,@body))))
(let ((side-effects '())
(result '()))
(flet ((return-0 () (push 0 side-effects) 0)
(return-3 () (push 3 side-effects) 3))
(for (i (return-0) (return-3))
(push i result)))
(assert-equal '(0 1 2 3) (nreverse result))
(assert-equal '(3 0) (nreverse side-effects)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-test for
(macrolet ((for ((var start stop) &body body)
;; Fill in the blank with a correct FOR macroexpansion that is
;; not affected by the three macro pitfalls mentioned above.
(let ((limit (gensym "LIMIT")))
`(do ((,var ,start (1+ ,var))
(,limit ,stop))
((> ,var ,limit))
,@body))))
(let ((side-effects '())
(result '()))
(flet ((return-0 () (push 0 side-effects) 0)
(return-3 () (push 3 side-effects) 3))
(for (i (return-0) (return-3))
(push i result)))
(assert-equal '(0 1 2 3) (nreverse result))
(assert-equal '(0 3) (nreverse side-effects)))))

View File

@ -0,0 +1,102 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; Lisp supports several functional alternatives to imperative iteration.
(define-test mapcar
(let ((numbers '(1 2 3 4 5 6)))
;; Inside MAPCAR, he function 1+ will be applied to each element of NUMBERS.
;; A new list will be collected from the results.
(assert-equal '(2 3 4 5 6 7) (mapcar #'1+ numbers))
(assert-equal '(-1 -2 -3 -4 -5 -6) (mapcar #'- numbers))
(assert-equal '((1) (2) (3) (4) (5) (6)) (mapcar #'list numbers))
(assert-equal '(nil t nil t nil t) (mapcar #'evenp numbers))
(assert-equal '(t t t t t t) (mapcar #'numberp numbers))
(assert-equal '(nil nil nil nil nil nil) (mapcar #'stringp numbers))
;; MAPCAR can work on multiple lists. The function will receive one argument
;; from each list.
(let ((other-numbers '(4 8 15 16 23 42)))
(assert-equal '(5 10 18 20 28 48) (mapcar #'+ numbers other-numbers))
(assert-equal '(4 16 45 64 115 252) (mapcar #'* numbers other-numbers))
;; The function MOD performs modulo division.
(assert-equal '(0 0 0 0 3 0) (mapcar #'mod other-numbers numbers)))))
(define-test mapcar-lambda
;; MAPCAR is often used with anonymous functions.
(let ((numbers '(8 21 152 37 403 14 7 -34)))
(assert-equal '(8 1 2 7 3 4 7 6) (mapcar (lambda (x) (mod x 10)) numbers)))
(let ((strings '("Mary had a little lamb"
"Old McDonald had a farm"
"Happy birthday to you")))
(assert-equal '(" had a l" "McDonald" "y birthd")
(mapcar (lambda (x) (subseq x 4 12)) strings))))
(define-test map
;; MAP is a variant of MAPCAR that works on any sequences.
;; It allows to specify the type of the resulting sequence.
(let ((string "lorem ipsum"))
(assert-equal "LOREM IPSUM" (map 'string #'char-upcase string))
(assert-equal '(#\L #\O #\R #\E #\M #\Space #\I #\P #\S #\U #\M)
(map 'list #'char-upcase string))
;; Not all vectors containing characters are strings.
(assert-equalp #(#\L #\O #\R #\E #\M #\Space #\I #\P #\S #\U #\M)
(map '(vector t) #'char-upcase string))))
(define-test transposition
;; MAPCAR gives the function as many arguments as there are lists.
(flet ((transpose (lists) (apply #'mapcar #'list lists)))
(let ((list '((1 2 3)
(4 5 6)
(7 8 9)))
(transposed-list '((1 4 7)
(2 5 8)
(3 6 9))))
(assert-equal transposed-list (transpose list))
(assert-equal list (transpose (transpose list))))
(assert-equal '(("these" "pretzels" "are")
("making" "me" "thirsty"))
(transpose '(("these" "making")
("pretzels" "me")
("are" "thirsty"))))))
(define-test reduce
;; The function REDUCE combines the elements of a list by applying a binary
;; function to the elements of a sequence from left to right.
(assert-equal 15 (reduce #'+ '(1 2 3 4 5)))
(assert-equal 10 (reduce #'+ '(1 2 3 4)))
(assert-equal 1 (reduce #'expt '(1 2 3 4 5))))
(define-test reduce-from-end
;; The :FROM-END keyword argument can be used to reduce from right to left.
(let ((numbers '(1 2 3 4 5)))
(assert-equal '((((1 . 2) . 3) . 4) . 5) (reduce #'cons numbers))
(assert-equal '(1 2 3 4 . 5) (reduce #'cons numbers :from-end t)))
(let ((numbers '(2 3 2)))
(assert-equal 64 (reduce #'expt numbers))
(assert-equal 512 (reduce #'expt numbers :from-end t))))
(define-test reduce-initial-value
;; :INITIAL-VALUE can supply the initial value for the reduction.
(let ((numbers '(1 2 3 4 5)))
(assert-equal 120 (reduce #'* numbers))
(assert-equal 0 (reduce #'* numbers :initial-value 0))
(assert-equal -120 (reduce #'* numbers :initial-value -1))))
(define-test inner-product
;; MAPCAR and REDUCE are powerful when used together.
;; Fill in the blanks to produce a local function that computes an inner
;; product of two vectors.
(flet ((inner-product (x y) (reduce #'+ (mapcar #'* x y))))
(assert-equal 32 (inner-product '(1 2 3) '(4 5 6)))
(assert-equal 310 (inner-product '(10 20 30) '(4 3 7)))))

View File

@ -0,0 +1,41 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; In Lisp, it is possible for a function to return more than one value.
;;; This is distinct from returning a list or structure of values.
(define-test multiple-values
(let ((x (floor 3/2))
;; The macro MULTIPLE-VALUE-LIST returns a list of all values returned
;; by a Lisp form.
(y (multiple-value-list (floor 3/2))))
(assert-equal x 1)
(assert-equal y '(1 1/2)))
(assert-equal '(24 3/4) (multiple-value-list (floor 99/4))))
(defun next-fib (a b)
;; The function VALUES allows returning multiple values.
(values b (+ a b)))
(define-test binding-and-setting-multiple-values
;; The macro MULTIPLE-VALUE-BIND is like LET, except it binds the variables
;; listed in its first argument to the values returned by the form that is its
;; second argument.
(multiple-value-bind (x y) (next-fib 3 5)
(let ((result (* x y)))
(assert-equal 40 result)))
;; SETF can also set multiple values if a VALUES form is provided as a place.
(let (x y)
(setf (values x y) (next-fib 5 8))
(assert-equal '(8 13) (list x y))))

View File

@ -0,0 +1,52 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(define-test t-and-nil-are-opposites
;; NOT is a function which returns the boolean opposite of its argument.
(true-or-false? t (not nil))
(true-or-false? nil (not t)))
(define-test nil-and-empty-list-are-the-same-thing
;; In Common Lisp, NIL is also the empty list.
(true-or-false? nil '())
(true-or-false? t (not '())))
(define-test in-lisp-many-things-are-true
;; In Common Lisp, the canonical values for truth is T.
;; However, everything that is non-NIL is true, too.
(true-or-false? t 5)
(true-or-false? nil (not 5))
(true-or-false? t "a string")
;; Even an empty string...
(true-or-false? t "")
;; ...or a list containing a NIL...
(true-or-false? t (list nil))
;; ...or an array with no elements...
(true-or-false? t (make-array 0))
;; ...or the number zero.
(true-or-false? t 0))
(define-test and
;; The logical operator AND can take multiple arguments.
(true-or-false? t (and t t t t t))
(true-or-false? nil (and t t nil t t))
;; If all values passed to AND are true, it returns the last value.
(assert-equal 5 (and t t t t t 5)))
(define-test or
;; The logical operator OR can also take multiple arguments.
(true-or-false? t (or nil nil nil t nil))
;; OR returns the first non-NIL value it encounters, or NIL if there are none.
(assert-equal nil (or nil nil nil))
(assert-equal 1 (or 1 2 3 4 5)))

View File

@ -0,0 +1,48 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(define-test shadowing
(assert-equal '(4 2) (let ((z 4)) (list z (let ((z 2)) z)))))
(defun block-1 ()
(block here
(return-from here 4)
5))
(defun block-2 ()
(block outer
(block inner
(return-from outer 'space)
(return-from inner 'tube))
(return-from outer 'valve)))
(define-test block-return-from
(assert-equal 4 (block-1))
(assert-equal 'space (block-2)))
;;; See http://www.gigamonkeys.com/book/variables.html
(define-test lexical-variables-can-be-enclosed
(assert-equal 10 (let ((f (let ((x 10))
(lambda () x))))
(let ((x 20))
(funcall f)))))
(define-test dynamic-variables-are-affected-by-execution-path
(assert-equal 20 (let ((f (let ((x 10))
(declare (special x))
(lambda () x))))
(let ((x 20))
(declare (special x))
(funcall f)))))

View File

@ -0,0 +1,97 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; Greed is a dice game played among 2 or more players, using 5
;;; six-sided dice.
;;;
;;; Each player takes a turn consisting of one or more rolls of the dice.
;;; On the first roll of the game, a player rolls all five dice which are
;;; scored according to the following:
;;;
;;; Three 1's => 1000 points
;;; Three 6's => 600 points
;;; Three 5's => 500 points
;;; Three 4's => 400 points
;;; Three 3's => 300 points
;;; Three 2's => 200 points
;;; One 1 => 100 points
;;; One 5 => 50 points
;;;
;;; A single die can only be counted once in each roll. For example,
;;; a "5" can only count as part of a triplet (contributing to the 500
;;; points) or as a single 50 points, but not both in the same roll.
;;;
;;; Example Scoring
;;;
;;; Throw Score
;;; --------- ------------------
;;; 5 1 3 4 1 50 + 2 * 100 = 250
;;; 1 1 1 3 1 1000 + 100 = 1100
;;; 2 4 4 5 4 400 + 50 = 450
;;;
;;; The dice not contributing to the score are called the non-scoring
;;; dice. "3" and "4" are non-scoring dice in the first example. "3" is
;;; a non-scoring die in the second, and "2" is a non-score die in the
;;; final example.
;;;
;;; More scoring examples are given in the tests below.
;;;
;;; Your goal is to write the scoring function for Greed.
(defun score-once (&rest dice)
(let ((sorted (sort (copy-list dice) #'<)))
(cond ((search '(1 1 1) sorted) (list 1000 (remove 1 sorted :count 3)))
((search '(2 2 2) sorted) (list 200 (remove 2 sorted :count 3)))
((search '(3 3 3) sorted) (list 300 (remove 3 sorted :count 3)))
((search '(4 4 4) sorted) (list 400 (remove 4 sorted :count 3)))
((search '(5 5 5) sorted) (list 500 (remove 5 sorted :count 3)))
((search '(6 6 6) sorted) (list 600 (remove 6 sorted :count 3)))
((find 5 sorted) (list 50 (remove 5 sorted :count 1)))
((find 1 sorted) (list 100 (remove 1 sorted :count 1)))
(t (list 0 '())))))
(defun score (&rest dice)
(loop for current-dice = dice then remaining-dice
for (score remaining-dice) = (apply #'score-once current-dice)
sum score
while remaining-dice))
(define-test score-of-an-empty-list-is-zero
(assert-equal 0 (score)))
(define-test score-of-a-single-roll-of-5-is-50
(assert-equal 50 (score 5)))
(define-test score-of-a-single-roll-of-1-is-100
(assert-equal 100 (score 1)))
(define-test score-of-multiple-1s-and-5s-is-the-sum-of-individual-scores
(assert-equal 300 (score 1 5 5 1)))
(define-test score-of-single-2s-3s-4s-and-6s-are-zero
(assert-equal 0 (score 2 3 4 6)))
(define-test score-of-a-triple-1-is-1000
(assert-equal 1000 (score 1 1 1)))
(define-test score-of-other-triples-is-100x
(assert-equal 200 (score 2 2 2))
(assert-equal 300 (score 3 3 3))
(assert-equal 400 (score 4 4 4))
(assert-equal 500 (score 5 5 5))
(assert-equal 600 (score 6 6 6)))
(define-test score-of-mixed-is-sum
(assert-equal 250 (score 2 5 2 2 3))
(assert-equal 550 (score 5 5 5 5)))

View File

@ -0,0 +1,220 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(defclass access-counter ()
((value :accessor value :initarg :value)
(access-count :reader access-count :initform 0)))
;;; The generated reader, writer, and accessor functions are generic functions.
;;; The methods of a generic function are combined using a method combination;
;;; by default, the standard method combination is used.
;;; This allows us to define :BEFORE and :AFTER methods whose code is executed
;;; before or after the primary method, and whose return values are discarded.
;;; The :BEFORE and :AFTER keywords used in this context are called qualifiers.
(defmethod value :after ((object access-counter))
(incf (slot-value object 'access-count)))
(defmethod (setf value) :after (new-value (object access-counter))
(incf (slot-value object 'access-count)))
(define-test defmethod-after
(let ((counter (make-instance 'access-counter :value 42)))
(assert-equal 0 (access-count counter))
(assert-equal 42 (value counter))
(assert-equal 1 (access-count counter))
(setf (value counter) 24)
(assert-equal 2 (access-count counter))
(assert-equal 24 (value counter))
(assert-equal 3 (access-count counter))
;; We read the value three more times and discard the result.
(value counter)
(value counter)
(value counter)
(assert-equal 6 (access-count counter))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; In addition to :BEFORE and :AFTER methods is also possible to write :AROUND
;;; methods, which execute instead of the primary methods. In such context, it
;;; is possible to call the primary method via CALL-NEXT-METHOD.
;;; In the standard method combination, the :AROUND method, if one exists, is
;;; executed first, and it may choose whether and how to call next methods.
(defgeneric grab-lollipop ()
(:method () :lollipop))
(defgeneric grab-lollipop-while-mom-is-nearby (was-nice-p)
(:method :around (was-nice-p) (if was-nice-p (call-next-method) :no-lollipop))
(:method (was-nice-p) (declare (ignore was-nice-p)) :lollipop))
(define-test lollipop
(assert-equal :lollipop (grab-lollipop))
(assert-equal :lollipop (grab-lollipop-while-mom-is-nearby t))
(assert-equal :no-lollipop (grab-lollipop-while-mom-is-nearby nil)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defclass countdown ()
;; The countdown object represents an ongoing countdown. Each time the
;; REMAINING-TIME function is called, it should return a number one less than
;; the previous time that it returned. If the countdown hits zero, :BANG
;; should be returned instead.
((remaining-time :reader remaining-time :initarg :time)))
(defmethod remaining-time :around ((object countdown))
(let ((time (call-next-method)))
(if (< 0 time)
;; DECF is similar to INCF. It decreases the value stored in the place
;; and returns the decreased value.
(decf (slot-value object 'remaining-time))
:bang)))
(define-test countdown
(let ((countdown (make-instance 'countdown :time 4)))
(assert-equal 3 (remaining-time countdown))
(assert-equal 2 (remaining-time countdown))
(assert-equal 1 (remaining-time countdown))
(assert-equal 0 (remaining-time countdown))
(assert-equal :bang (remaining-time countdown))
(assert-equal :bang (remaining-time countdown))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; It is possible for multiple :BEFORE, :AFTER, :AROUND, or primary methods to
;;; be executed in a single method call.
(defclass object ()
((counter :accessor counter :initform 0)))
(defclass bigger-object (object) ())
(defgeneric frobnicate (x)
(:method :around ((x bigger-object))
(incf (counter x) 8)
(call-next-method))
(:method :around ((x object))
(incf (counter x) 70)
(call-next-method))
(:method :before ((x bigger-object))
(incf (counter x) 600))
(:method :before ((x object))
(incf (counter x) 5000))
(:method ((x bigger-object))
(incf (counter x) 40000)
(call-next-method))
(:method ((x object))
(incf (counter x) 300000))
(:method :after ((x object))
(incf (counter x) 2000000))
(:method :after ((x bigger-object))
(incf (counter x) 10000000)))
(define-test multiple-methods
(let ((object (make-instance 'object)))
(frobnicate object)
(assert-equal 2305070 (counter object)))
(let ((object (make-instance 'bigger-object)))
(frobnicate object)
(assert-equal 12345678 (counter object))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; The method order of the standard combination is as follows:
;;; First, the most specific :AROUND method is executed.
;;; Second, all :BEFORE methods are executed, most specific first.
;;; Third, the most specific primary method is executed.
;;; Fourth, all :AFTER methods are executed, most specific last.
(defgeneric calculate (x)
(:method :around ((x bigger-object))
(setf (counter x) 40)
(call-next-method))
(:method :around ((x object))
(incf (counter x) 24)
(call-next-method))
(:method :before ((x bigger-object))
(setf (counter x) (mod (counter x) 6)))
(:method :before ((x object))
(setf (counter x) (/ (counter x) 4)))
(:method ((x bigger-object))
(setf (counter x) (* (counter x) (counter x)))
(call-next-method))
(:method ((x object))
(decf (counter x) 100))
(:method :after ((x object))
(setf (counter x) (/ 1 (counter x))))
(:method :after ((x bigger-object))
(incf (counter x) 2)))
(define-test standard-method-combination-order
(let ((object (make-instance 'object)))
(calculate object)
(assert-equal -1/94 (counter object)))
(let ((object (make-instance 'bigger-object)))
(calculate object)
(assert-equal 197/99 (counter object))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defclass programmer () ())
(defclass senior-programmer (programmer) ())
(defclass full-stack-programmer (programmer) ())
(defclass senior-full-stack-programmer (senior-programmer
full-stack-programmer)
())
;;; The :BEFORE, :AFTER, and :AROUND methods are only available in the standard
;;; method combination. It is possible to use other method combinations, such as
;;; +.
(defgeneric salary-at-company-a (programmer)
(:method-combination +)
(:method + ((programmer programmer)) 120000)
(:method + ((programmer senior-programmer)) 200000)
(:method + ((programmer full-stack-programmer)) 48000))
(define-test salary-at-company-a
(let ((programmer (make-instance 'programmer)))
(assert-equal 120000 (salary-at-company-a programmer)))
(let ((programmer (make-instance 'senior-programmer)))
(assert-equal 320000 (salary-at-company-a programmer)))
(let ((programmer (make-instance 'full-stack-programmer)))
(assert-equal 168000 (salary-at-company-a programmer)))
(let ((programmer (make-instance 'senior-full-stack-programmer)))
(assert-equal 368000 (salary-at-company-a programmer))))
;;; It is also possible to define custom method combinations.
(define-method-combination multiply :operator *)
(defgeneric salary-at-company-b (programmer)
(:method-combination multiply)
(:method multiply ((programmer programmer)) 120000)
(:method multiply ((programmer senior-programmer)) 2)
(:method multiply ((programmer full-stack-programmer)) 7/5))
(define-test salary-at-company-b
(let ((programmer (make-instance 'programmer)))
(assert-equal 120000 (salary-at-company-b programmer)))
(let ((programmer (make-instance 'senior-programmer)))
(assert-equal 240000 (salary-at-company-b programmer)))
(let ((programmer (make-instance 'full-stack-programmer)))
(assert-equal 168000 (salary-at-company-b programmer)))
(let ((programmer (make-instance 'senior-full-stack-programmer)))
(assert-equal 336000 (salary-at-company-b programmer))))

View File

@ -0,0 +1,73 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(define-test what-is-a-string
(let ((string "Do, or do not. There is no try."))
(true-or-false? t (typep string 'string))
;; Strings are vectors of characters.
(true-or-false? t (typep string 'array))
(true-or-false? t (typep string 'vector))
(true-or-false? t (typep string '(vector character)))
(true-or-false? nil (typep string 'integer))))
(define-test multiline-string
;; A Lisp string can span multiple lines.
(let ((string "this is
a multi
line string"))
(true-or-false? t (typep string 'string))))
(define-test escapes-in-strings
;; Quotes and backslashes in Lisp strings must be escaped.
(let ((my-string "this string has one of these \" and a \\ in it"))
(true-or-false? t (typep my-string 'string))))
(define-test substrings
;; Since strings are sequences, it is possible to use SUBSEQ on them.
(let ((string "Lorem ipsum dolor sit amet"))
(assert-equal "dolor sit amet" (subseq string 12))
(assert-equal "ipsum" (subseq string 6 11))
(assert-equal "orem" (subseq string 1 5))))
(define-test strings-versus-characters
;; Strings and characters have distinct types.
(true-or-false? t (typep #\a 'character))
(true-or-false? nil (typep "A" 'character))
(true-or-false? nil (typep #\a 'string))
;; One can use both AREF and CHAR to refer to characters in a string.
(let ((my-string "Cookie Monster"))
(assert-equal #\C (char my-string 0))
(assert-equal #\k (char my-string 3))
(assert-equal #\M (aref my-string 7))))
(define-test concatenating-strings
;; Concatenating strings in Common Lisp is possible, if a little cumbersome.
(let ((a "Lorem")
(b "ipsum")
(c "dolor"))
(assert-equal "Lorem ipsum dolor" (concatenate 'string a " " b " " c))))
(define-test searching-for-characters
;; The function POSITION can be used to find the first position of an element
;; in a sequence. If the element is not found, NIL is returned.
(assert-equal 1 (position #\b "abc"))
(assert-equal 2 (position #\c "abc"))
(assert-equal nil (position #\d "abc")))
(define-test finding-substrings
;; The function SEARCH can be used to search a sequence for subsequences.
(let ((title "A supposedly fun thing I'll never do again"))
(assert-equal 2 (search "supposedly" title))
(assert-equal 12 (search " fun" title))))

View File

@ -0,0 +1,111 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; Lisp structures encapsulate data which belongs together. They are a template
;;; of sorts, providing a way to generate multiple instances of uniformly
;;; organized information
;;; Defining a structure also interns accessor functions to get and set the
;;; slots of that structure.
;;; The following form creates a new structure class named BASKETBALL-PLAYER
;;; with slots named NAME, TEAM, and NUMBER.
;;; This additionally creates functions MAKE-BASKETBALL-PLAYER,
;;; COPY-BASKETBALL-PLAYER, BASKETBALL-PLAYER-P, BASKETBALL-PLAYER-NAME,
;;; BASKETBALL-PLAYER-TEAM, and BASKETBALL-PLAYER-NUMBER.
(defstruct basketball-player
name team number)
(define-test make-struct
(let ((player (make-basketball-player :name "Larry" :team :celtics
:number 33)))
(true-or-false? t (basketball-player-p player))
(assert-equal "Larry" (basketball-player-name player))
(assert-equal :celtics (basketball-player-team player))
(assert-equal 33 (basketball-player-number player))
(setf (basketball-player-team player) :retired)
(assert-equal :retired (basketball-player-team player))))
;;; Structure fields can have default values.
(defstruct baseball-player
name (team :red-sox) (position :outfield))
(define-test struct-defaults
(let ((player (make-baseball-player)))
;; We have not specified a default value for NAME, therefore we cannot
;; read it here - it would invoke undefined behaviour.
(assert-equal :red-sox (baseball-player-team player))
(assert-equal :outfield (baseball-player-position player))))
;;; The accessor names can get pretty long. It's possible to specify a different
;;; prefix with the :CONC-NAME option.
(defstruct (american-football-player (:conc-name nfl-guy-))
name position team)
(define-test struct-access
(let ((player (make-american-football-player
:name "Drew Brees" :position :qb :team "Saints")))
(assert-equal "Drew Brees" (nfl-guy-name player))
(assert-equal "Saints" (nfl-guy-team player))
(assert-equal :qb (nfl-guy-position player))))
;;; Structs can be defined to include other structure definitions.
;;; This form of inheritance allows composition of objects.
(defstruct (nba-contract (:include basketball-player))
salary start-year end-year)
(define-test structure-inheritance
(let ((contract (make-nba-contract :salary 136000000
:start-year 2004 :end-year 2011
:name "Kobe Bryant"
:team :lakers :number 24)))
(assert-equal 2004 (nba-contract-start-year contract))
(assert-equal 'nba-contract (type-of contract))
;; Inherited structures follow the rules of type hierarchy.
(true-or-false? t (typep contract 'basketball-player))
;; One can access structure fields both with the structure's own accessors
;; and with the inherited accessors.
(assert-equal :lakers (nba-contract-team contract))
(assert-equal :lakers (basketball-player-team contract))))
;;; Copying a structure named FOO is handled with the COPY-FOO function.
;;; All such copies are shallow.
(define-test structure-equality-and-copying
(let ((manning-1 (make-american-football-player
:name "Manning" :team (list "Colts" "Broncos")))
(manning-2 (make-american-football-player
:name "Manning" :team (list "Colts" "Broncos"))))
;; MANNING-1 and MANNING-2 are different objects...
(true-or-false? nil (eq manning-1 manning-2))
;;...but they contain the same information.
(true-or-false? t (equalp manning-1 manning-2))
(let ((manning-3 (copy-american-football-player manning-1)))
(true-or-false? nil (eq manning-1 manning-3))
(true-or-false? t (equalp manning-1 manning-3))
;; Setting the slot of one instance does not modify the others...
(setf (nfl-guy-name manning-1) "Rogers")
(true-or-false? nil (string= (nfl-guy-name manning-1)
(nfl-guy-name manning-3)))
(assert-equal "Rogers" (nfl-guy-name manning-1))
(assert-equal "Manning" (nfl-guy-name manning-3))
;; ...but modifying shared structure may affect other instances.
(setf (car (nfl-guy-team manning-1)) "Giants")
(true-or-false? t (string= (car (nfl-guy-team manning-1))
(car (nfl-guy-team manning-3))))
(assert-equal "Giants" (car (nfl-guy-team manning-1)))
(assert-equal "Giants" (car (nfl-guy-team manning-3))))))

View File

@ -0,0 +1,161 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; This lesson group uses Quicklisp to load Bordeaux Threads, a portability
;;; library for working with threads. This is because threads are not a part of
;;; the Common Lisp standard and implementations do them differently.
;;; If you are using Quicklisp, please feel free to enable this lesson by
;;; following the instructions in the README.
;;; TODO: wait for Bordeaux Threads to implement a portable SEMAPHORE-COUNT
;;; and use it in the semaphore koans.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-test thread-return-value
;; When a thread object is constructed, it accepts a function to execute.
(let* ((thread (bt:make-thread (lambda () (+ 2 2))))
;; When the thread's function finishes, its return value becomes the
;; return value of BT:JOIN-THREAD.
(value (bt:join-thread thread)))
(assert-equal ____ value)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defvar *variable*)
(define-test thread-global-bindings
;; The global value of a variable is shared between all threads.
(setf *variable* 42)
(let ((thread (bt:make-thread (lambda ()
(when (= *variable* 42)
(setf *variable* 24)
t)))))
(assert-true (bt:join-thread thread))
(assert-equal ____ *variable*)))
(define-test thread-local-bindings
;; Newly established local bindings of a variable are visible only in the
;; thread that established these bindings.
(setf *variable* 42)
(let ((thread (bt:make-thread (lambda ()
(let ((*variable* 42))
(setf *variable* 24))))))
(bt:join-thread thread)
(assert-equal ____ *variable*)))
(define-test thread-initial-bindings
;; Initial dynamic bindings may be passed to the new thread.
(setf *variable* 42)
(let ((thread (bt:make-thread (lambda () (setf *variable* 24))
:initial-bindings '((*variable* . 42)))))
(bt:join-thread thread)
(assert-equal ____ *variable*)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-test thread-name
;; Threads can have names.
(let ((thread (bt:make-thread #'+ :name "Summing thread")))
(assert-equal ____ (bt:thread-name thread))
(assert-equal ____ (bt:join-thread thread))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-test thread-function-arguments
;; Passing arguments to thread functions requires closing over them.
(let* ((x 240)
(y 18)
(thread (bt:make-thread (lambda () (* x y)))))
(assert-equal ____ (bt:join-thread thread))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-test destroy-thread
;; Looping and renegade threads can usually be killed via BT:DESTROY-THREAD.
;; It is the last measure, since doing so might leave the Lisp system in an
;; unpredictable state if the thread was doing something complex.
(let ((thread (bt:make-thread (lambda () (loop (sleep 1))))))
(true-or-false? ____ (bt:thread-alive-p thread))
(bt:destroy-thread thread)
(true-or-false? ____ (bt:thread-alive-p thread))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defvar *another-variable*)
;; Preventing concurrent access to some data can be achieved via a lock in
;; order to avoid race conditions.
(defvar *lock* (bt:make-lock))
(define-test lock
(setf *another-variable* 0)
(flet ((increaser () (bt:with-lock-held (*lock*) (incf *another-variable*))))
(loop repeat 100
collect (bt:make-thread #'increaser) into threads
finally (loop until (notany #'bt:thread-alive-p threads))
(assert-equal ____ *another-variable*))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; We can further orchestrate threads by using semaphores.
(defvar *semaphore* (bt:make-semaphore))
(defun signal-our-semaphore ()
(bt:signal-semaphore semaphore))
(defun wait-on-our-semaphore ()
(bt:wait-on-semaphore semaphore :timeout 100))
(define-test semaphore
(assert-equal 1 (bt:join-thread (bt:make-thread #'signal-our-semaphore)))
(assert-equal ____ (bt:join-thread (bt:make-thread #'signal-our-semaphore)))
(assert-equal ____ (bt:join-thread (bt:make-thread #'signal-our-semaphore)))
(assert-equal 2 (bt:join-thread (bt:make-thread #'wait-on-our-semaphore)))
(assert-equal ____ (bt:join-thread (bt:make-thread #'wait-on-our-semaphore)))
(assert-equal ____ (bt:join-thread (bt:make-thread #'wait-on-our-semaphore)))
(assert-equal ____ (bt:join-thread (bt:make-thread #'wait-on-our-semaphore))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Semaphores can be used to manage resource allocation and to trigger some
;; threads to run when the semaphore value is above zero.
(defvar *foobar-semaphore* (bt:make-semaphore))
(defvar *foobar-list*)
(defun bar-pusher ()
(dotimes (i 10)
(sleep 0.01)
(push i (nth i *foobar-list*))
(push :bar (nth i *foobar-list*))
;; We push :BAR before :FOO, so the final list looks like (:FOO :BAR).
(bt:signal-semaphore *foobar-semaphore*)))
(defun foo-pusher ()
(dotimes (i 10)
(bt:wait-on-semaphore *foobar-semaphore*)
(push :foo (nth i *foobar-list*))))
(define-test list-of-foobars
(setf *foobar-list* (make-list 10))
(let ((bar-pusher (bt:make-thread #'bar-pusher))
(foo-pusher (bt:make-thread #'foo-pusher)))
(bt:join-thread foo-pusher))
(assert-equal ____ (nth 0 *foobar-list*))
(assert-equal ____ (nth 1 *foobar-list*))
(assert-equal ____ (nth 5 *foobar-list*)))

View File

@ -0,0 +1,75 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(define-condition triangle-error (error)
;; Fill in the blank with a suitable slot definition.
((triangle-error-sides :reader triangle-error-sides :initarg :sides)))
(defun triangle (a b c)
(check-type a (real (0)))
(check-type b (real (0)))
(check-type c (real (0)))
;; Fill in the blank with a function that satisfies the below tests.
(let* ((min (min a b c))
(max (max a b c))
(mid (car (remove min (remove max (list a b c) :count 1) :count 1))))
(cond ((<= (+ min mid) max) (error 'triangle-error :sides (list a b c)))
((= max mid min) :equilateral)
((= max mid) :isosceles)
((= mid min) :isosceles)
(t :scalene))))
(define-test equilateral-triangles
;; Equilateral triangles have three sides of equal length,
(assert-equal :equilateral (triangle 2 2 2))
(assert-equal :equilateral (triangle 10 10 10)))
(define-test isosceles-triangles
;; Isosceles triangles have two sides of equal length,
(assert-equal :isosceles (triangle 3 4 4))
(assert-equal :isosceles (triangle 4 3 4))
(assert-equal :isosceles (triangle 4 4 3))
(assert-equal :isosceles (triangle 2 2 3))
(assert-equal :isosceles (triangle 10 10 2)))
(define-test scalene-triangles
;; Scalene triangles have three sides of different lengths.
(assert-equal :scalene (triangle 3 4 5))
(assert-equal :scalene (triangle 10 11 12))
(assert-equal :scalene (triangle 5 4 2)))
(define-test illegal-triangles
;; Not all triplets make valid triangles.
(flet ((triangle-failure (a b c)
(handler-case (progn (triangle a b c) (error "Test failure"))
(error (condition) condition))))
(let ((condition (triangle-failure 0 0 0)))
(assert-true (typep condition 'type-error))
(assert-equal 0 (type-error-datum condition))
;; The type (REAL (0)) represents all positive numbers.
(assert-true (subtypep (type-error-expected-type condition) '(real (0))))
;; If two type specifiers are SUBTYPEP of one another, then they represent
;; the same Lisp type.
(assert-true (subtypep '(real (0)) (type-error-expected-type condition))))
(let ((condition (triangle-failure 3 4 -5)))
(assert-true (typep condition 'type-error))
(assert-equal -5 (type-error-datum condition))
(assert-true (subtypep (type-error-expected-type condition) '(real (0))))
(assert-true (subtypep '(real (0)) (type-error-expected-type condition))))
(let ((condition (triangle-failure 1 1 3)))
(assert-true (typep condition 'triangle-error))
(assert-equal '(1 1 3) (triangle-error-sides condition)))
(let ((condition (triangle-failure 2 4 2)))
(assert-true (typep condition 'triangle-error))
(assert-equal '(2 4 2) (triangle-error-sides condition)))))

View File

@ -0,0 +1,153 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; There is a type hierarchy in Lisp, based on the set theory.
;;; An object may belong to multiple types at the same time.
;;; Every object is of type T. No object is of type NIL.
(define-test typep
;; TYPEP returns true if the provided object is of the provided type.
(true-or-false? t (typep "hello" 'string))
(true-or-false? t (typep "hello" 'array))
(true-or-false? nil (typep "hello" 'list))
(true-or-false? t (typep "hello" '(simple-array character (5))))
(true-or-false? t (typep '(1 2 3) 'list))
(true-or-false? t (typep 99 'integer))
(true-or-false? t (typep nil 'NULL))
(true-or-false? t (typep 22/7 'ratio))
(true-or-false? t (typep 4.0 'float))
(true-or-false? t (typep #\a 'character))
(true-or-false? t (typep #'length 'function)))
(define-test type-of
;; TYPE-OF returns a type specifier for the object.
(assert-equal 'null (type-of '()))
(assert-equal 'ratio (type-of 4/6)))
(define-test overlapping-types
;; Because Lisp types are mathematical sets, they are allowed to overlap.
(let ((thing '()))
(true-or-false? t (typep thing 'list))
(true-or-false? t (typep thing 'atom))
(true-or-false? t (typep thing 'null))
(true-or-false? t (typep thing 't))))
(define-test fixnum-versus-bignum
;; In Lisp, integers are either fixnums or bignums. Fixnums are handled more
;; efficiently by the implementation, but some large integers can only be
;; represented as bignums.
;; Lisp converts between these two types on the fly. The constants
;; MOST-NEGATIVE-FIXNUM and MOST-POSITIVE-FIXNUM describe the limits for
;; fixnums.
(let ((integer-1 0)
(integer-2 most-positive-fixnum)
(integer-3 (1+ most-positive-fixnum))
(integer-4 (1- most-negative-fixnum)))
(true-or-false? t (typep integer-1 'fixnum))
(true-or-false? nil (typep integer-1 'bignum))
(true-or-false? t (typep integer-2 'fixnum))
(true-or-false? nil (typep integer-2 'bignum))
(true-or-false? nil (typep integer-3 'fixnum))
(true-or-false? t (typep integer-3 'bignum))
(true-or-false? nil (typep integer-4 'fixnum))
(true-or-false? t (typep integer-4 'bignum))
;; Regardless of whether an integer is a fixnum or a bignum, it is still
;; an integer.
(true-or-false? t (typep integer-1 'integer))
(true-or-false? t (typep integer-2 'integer))
(true-or-false? t (typep integer-3 'integer))
(true-or-false? t (typep integer-4 'integer))))
(define-test subtypep
(assert-true (typep 1 'bit))
(assert-true (typep 1 'fixnum))
(assert-true (typep 1 'integer))
(assert-true (typep 2 'integer))
;; The function SUBTYPEP attempts to answer whether one type specifier
;; represents a subtype of the other type specifier.
(true-or-false? t (subtypep 'bit 'integer))
(true-or-false? t (subtypep 'vector 'array))
(true-or-false? t (subtypep 'string 'vector))
(true-or-false? t (subtypep 'null 'list)))
(define-test list-type-specifiers
;; Some type specifiers are lists; this way, they carry more information than
;; type specifiers which are symbols.
(assert-true (typep (make-array 0) '(vector * 0)))
(assert-true (typep (make-array 42) '(vector * 42)))
(assert-true (typep (make-array 42 :element-type 'bit) '(vector bit 42)))
(assert-true (typep (make-array '(4 2)) '(array * (4 2))))
(true-or-false? t (typep (make-array '(3 3)) '(simple-array t (3 3))))
(true-or-false? nil (typep (make-array '(3 2 1)) '(simple-array t (1 2 3)))))
(define-test list-type-specifiers-hierarchy
;; Type specifiers that are lists also follow hierarchy.
(true-or-false? t (subtypep '(simple-array t (3 3)) '(simple-array t *)))
(true-or-false? t (subtypep '(vector double-float 100) '(vector * 100)))
(true-or-false? t (subtypep '(vector double-float 100) '(vector double-float *)))
(true-or-false? t (subtypep '(vector double-float 100) '(vector * *)))
(true-or-false? t (subtypep '(vector double-float 100) '(array * *)))
(true-or-false? t (subtypep '(vector double-float 100) t)))
(define-test type-coercion
(assert-true (typep 0 'integer))
(true-or-false? nil (typep 0 'short-float))
(true-or-false? nil (subtypep 'integer 'short-float))
(true-or-false? nil (subtypep 'short-float 'integer))
;; The function COERCE makes it possible to convert values between some
;; standard types.
(true-or-false? t (typep (coerce 0 'short-float) 'short-float)))
(define-test atoms-are-anything-thats-not-a-cons
;; In Lisp, an atom is anything that is not a cons cell. The function ATOM
;; returns true if its object is an atom.
(true-or-false? t (atom 4))
(true-or-false? nil (atom '(1 2 3 4)))
(true-or-false? nil (atom '(:foo . :bar)))
(true-or-false? t (atom 'symbol))
(true-or-false? t (atom :keyword))
(true-or-false? t (atom #(1 2 3 4 5)))
(true-or-false? t (atom #\A))
(true-or-false? t (atom "string"))
(true-or-false? t (atom (make-array '(4 4)))))
(define-test functionp
;; The function FUNCTIONP returns true if its arguments is a function.
(assert-true (functionp (lambda (a b c) (+ a b c))))
(true-or-false? t (functionp #'make-array))
(true-or-false? nil (functionp 'make-array))
(true-or-false? t (functionp (lambda (x) (* x x))))
(true-or-false? nil (functionp '(lambda (x) (* x x))))
(true-or-false? nil (functionp '(1 2 3)))
(true-or-false? nil (functionp t)))
(define-test other-type-predicates
;; Lisp defines multiple type predicates for standard types..
(true-or-false? t (numberp 999))
(true-or-false? t (listp '(9 9 9)))
(true-or-false? t (integerp 999))
(true-or-false? t (rationalp 9/99))
(true-or-false? t (floatp 9.99))
(true-or-false? t (stringp "nine nine nine"))
(true-or-false? t (characterp #\9))
(true-or-false? t (bit-vector-p #*01001)))
(define-test guess-that-type
;; Fill in the blank with a type specifier that satisfies the following tests.
(let ((type '(simple-array array (5 3 *))))
(assert-true (subtypep type '(simple-array * (* 3 *))))
(assert-true (subtypep type '(simple-array * (5 * *))))
(assert-true (subtypep type '(simple-array array *)))
(assert-true (typep (make-array '(5 3 9) :element-type 'string) type))
(assert-true (typep (make-array '(5 3 33) :element-type 'vector) type))))

View File

@ -0,0 +1,88 @@
;; Copyright 2013 Google Inc.
;;
;; Licensed under the Apache License, Version 2.0 (the "License");
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
(defun test-variable-assignment-with-setf ()
;; the let pattern allows us to create local variables with
;; lexical scope.
(let (var_name_1 (var_name_2 "Michael"))
;; variables may be defined with or without initial values.
(and
(equalp var_name_2 "Michael")
; new values may be assigned to variables with setf
(setf var_name_2 "Janet")
(equalp var_name_2 "Janet")
; setf may assign multiple variables in one form.
(setf var_name_1 "Tito"
var_name_2 "Jermaine")
(equalp var_name_1 "Tito")
(equalp var_name_2 "Jermaine"))))
(defun test-setf-for-lists ()
;; setf also works on list elements
(let (l)
(setf l '(1 2 3))
(equalp l '(1 2 3))
; First second and third are convenient accessor functions
; referring to the elements of a list
; For those interested, they are convenient to car, cadr, and caddr
(setf (first l) 10)
(setf (second l) 20)
(setf (third l) 30)
(equalp l '(10 20 30))))
(defparameter param_name_1 "Janet")
; defparameter requires an initial form. It is a compiler error to exclude it
;(defparameter param_no_init) ;; this will fail
(defconstant additive_identity 0)
; defconstant also requires an initial form
; (defconstant constant_no_init)
; reassigning parameters to new values is also ok, but parameters carry the
; connotation of immutability. If it's going to change frequently, it should
; be a var.
(setf param_name_1 "The other one")
; reassigning a constant is an error.
; this should result in a compile time error
; (setf additive_identity -1)
;; -------------------------------
;; below is necessary to run tests.
;; -------------------------------
(defvar failed-test-names nil)
(defun run-test (testfun)
(let ((fun-name (function-name testfun)))
(if (apply testfun '())
(format t ".")
(progn
(setf failed-test-names (cons fun-name failed-test-names))
(format t "F")))))
(defun function-name (function) (nth-value 2 (function-lambda-expression function)))
(run-test #'test-variable-assignment-with-setf)
(run-test #'test-setf-for-lists)
(format t "~%")
(defun report-failure (test-name)
(format t "~S failed.~%" test-name))
(if (endp failed-test-names) ; no failed tests
(format t "all tests pass.~%")
(mapcar #'report-failure failed-test-names))

View File

@ -0,0 +1,54 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; Vectors are one-dimensional arrays. This means that general array operations
;;; will work on vectors normally. However, Lisp also defines some functions for
;;; operating on sequences - which means, either vectors or lists.
(define-test vector-basics
;; #(...) is syntax sugar for defining literal vectors.
(let ((vector #(1 11 111)))
(true-or-false? t (typep vector 'vector))
(assert-equal 11 (aref vector 1))))
(define-test length
;; The function LENGTH works both for vectors and for lists.
(assert-equal 3 (length '(1 2 3)))
(assert-equal 3 (length #(1 2 3))))
(define-test bit-vector
;; #*0011 defines a bit vector literal with four elements: 0, 0, 1 and 1.
(assert-equal #*0011 (make-array 4 :element-type 'bit
:initial-contents '(0 0 1 1)))
(true-or-false? t (typep #*1001 'bit-vector))
(assert-equal 0 (aref #*1001 1)))
(define-test bitwise-operations
;; Lisp defines a few bitwise operations that work on bit vectors.
(assert-equal #*1000 (bit-and #*1100 #*1010))
(assert-equal #*1110 (bit-ior #*1100 #*1010))
(assert-equal #*0110 (bit-xor #*1100 #*1010)))
(defun list-to-bit-vector (list)
;; Implement a function that turns a list into a bit vector.
(coerce list 'bit-vector))
(define-test list-to-bit-vector
;; You need to fill in the blank in LIST-TO-BIT-VECTOR.
(assert-true (typep (list-to-bit-vector '(0 0 1 1 0)) 'bit-vector))
(assert-equal (aref (list-to-bit-vector '(0)) 0) 0)
(assert-equal (aref (list-to-bit-vector '(0 1)) 1) 1)
(assert-equal (length (list-to-bit-vector '(0 0 1 1 0 0 1 1))) 8))

View File

@ -0,0 +1,72 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(define-test basic-array-stuff
;; We make an 8x8 array and then fill it with a checkerboard pattern.
(let ((chess-board (make-array '(8 8))))
;; (DOTIMES (X 8) ...) will iterate with X taking values from 0 to 7.
(dotimes (x 8)
(dotimes (y 8)
;; AREF stands for "array reference".
(setf (aref chess-board x y) (if (evenp (+ x y)) :black :white))))
(assert-true (typep chess-board 'array))
(assert-equal ____ (aref chess-board 0 0))
(assert-equal ____ (aref chess-board 2 3))
;; The function ARRAY-RANK returns the number of dimensions of the array.
(assert-equal ____ (array-rank chess-board))
;; The function ARRAY-DIMENSIONS returns a list of the cardinality of the
;; array dimensions.
(assert-equal ____ (array-dimensions chess-board))
;; ARRAY-TOTAL-SIZE returns the total number of elements in the array.
(assert-equal ____ (array-total-size chess-board))))
(define-test make-your-own-array
;; Make your own array that satisfies the test.
(let ((color-cube ____))
;; You may need to modify your array after you create it.
(setf (____ color-cube ____ ____ ____) ____
(____ color-cube ____ ____ ____) ____)
(if (typep color-cube '(simple-array t (3 3 3)))
(progn
(assert-equal 3 (array-rank color-cube))
(assert-equal '(3 3 3) (array-dimensions color-cube))
(assert-equal 27 (array-total-size color-cube))
(assert-equal (aref color-cube 0 1 2) :red)
(assert-equal (aref color-cube 2 1 0) :white))
(assert-true nil))))
(define-test adjustable-array
;; The size of an array does not need to be constant.
(let ((x (make-array '(2 2) :initial-element 5 :adjustable t)))
(assert-equal ____ (aref x 1 0))
(assert-equal ____ (array-dimensions x))
(adjust-array x '(3 4))
(assert-equal ____ (array-dimensions x))))
(define-test make-array-from-list
;; One can create arrays with initial contents.
(let ((x (make-array '(4) :initial-contents '(:one :two :three :four))))
(assert-equal ____ (array-dimensions x))
(assert-equal ____ (aref x 0))))
(define-test row-major-index
;; Row major indexing is a way to access elements with a single integer,
;; rather than a list of integers.
(let ((my-array (make-array '(2 2 2 2))))
(dotimes (i (* 2 2 2 2))
(setf (row-major-aref my-array i) i))
(assert-equal ____ (aref my-array 0 0 0 0))
(assert-equal ____ (aref my-array 0 0 1 0))
(assert-equal ____ (aref my-array 0 1 0 0))
(assert-equal ____ (aref my-array 1 1 1 1))))

View File

@ -0,0 +1,65 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; ╭╮ ╭╮ ///////
;;; ┃┃ ┃┃///////
;;; ┃┃╭┳━━┳━━╮ ┃┃╭┳━━┳━━┳━╮╭━━╮
;;; ┃┃┣┫━━┫╭╮┃ ┃╰╯┫╭╮┃╭╮┃╭╮┫━━┫
;;; ┃╰┫┣━━┃╰╯┃ ┃╭╮┫╰╯┃╭╮┃┃┃┣━━┃
;;; ╰━┻┻━━┫╭━╯/╰╯╰┻━━┻╯╰┻╯╰┻━━╯
;;; ┃┃ //////
;;; ╰╯//////
;;; Welcome to the Lisp Koans.
;;; May the code stored here influence your enlightenment as a programmer.
;;; In order to progress, fill in the blanks, denoted via ____ in source code.
;;; Sometimes, you will be asked to provide values that are equal to something.
(define-test fill-in-the-blanks
(assert-equal ____ 2)
(assert-equal ____ 3.14)
(assert-equal ____ "Hello World"))
;;; Sometimes, you will be asked to say whether something is true or false,
;;; In Common Lisp, the canonical values for truth and falsehood are T and NIL.
(define-test assert-true
(assert-true ____))
(define-test assert-false
(assert-false ____))
(define-test true-or-false
(true-or-false? ____ (= 34 34))
(true-or-false? ____ (= 19 78)))
;;; Since T and NIL are symbols, you can type them in lowercase or uppercase;
;;; by default, Common Lisp will automatically upcase them upon reading.
(define-test upcase-downcase
;; Try inserting a lowercase t here.
(assert-equal ____ T)
;; Try inserting an uppercase NIL here.
(assert-equal ____ nil))
;;; Sometimes, you will be asked to provide a part of an expression that must be
;;; either true or false.
(define-test a-true-assertion
(assert-true (= ____ (+ 2 2))))
(define-test a-false-assertion
(assert-false (= ____ (+ 2 2))))

View File

@ -0,0 +1,43 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; Lists in lisp are forms beginning and ending with rounded parentheses.
;;; Atoms are symbols, numbers, or other forms usually separated by whitespace
;;; or parentheses.
(define-test list-or-atom
;; The function LISTP will return true if the input is a list.
;; The function ATOM will return true if the input is an atom.
(true-or-false? ____ (listp '(1 2 3)))
(true-or-false? ____ (atom '(1 2 3)))
(true-or-false? ____ (listp '("heres" "some" "strings")))
(true-or-false? ____ (atom '("heres" "some" "strings")))
(true-or-false? ____ (listp "a string"))
(true-or-false? ____ (atom "a string"))
(true-or-false? ____ (listp 2))
(true-or-false? ____ (atom 2))
(true-or-false? ____ (listp '(("first" "list") ("second" "list"))))
(true-or-false? ____ (atom '(("first" "list") ("second" "list")))))
(define-test the-duality-of-nil
;; The empty list, NIL, is unique in that it is both a list and an atom.
(true-or-false? ____ (listp nil))
(true-or-false? ____ (atom nil)))
(define-test keywords
;; Symbols like :HELLO or :LIKE-THIS are keywords. They are treated
;; differently in Lisp: they are constants that always evaluate to themselves.
(true-or-false? ____ (equal :this-is-a-keyword :this-is-a-keyword))
(true-or-false? ____ (equal :this-is-a-keyword ':this-is-a-keyword))
(true-or-false? ____ (equal :this-is-a-keyword :this-is-also-a-keyword)))

View File

@ -0,0 +1,65 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; Backquote notation is similar to quoting, except it allows for parts of the
;;; resulting expression to be "unquoted".
(define-test backquote-basics
(let ((x '(123))
(z '(7 8 9)))
;; ' quotes an expression normally.
(assert-equal ____ '(x 45 6 z))
;; ` backquotes an expression; without any unquotes, it is equivalent to
;; using the normal quote.
(assert-equal ____ `(x 45 6 z))
;; , unquotes a part of the expression.
(assert-equal ____ `(,x 45 6 z))
(assert-equal ____ `(,x 45 6 ,z))
;; ,@ splices an expression into the into the list surrounding it.
(assert-equal ____ `(,x 45 6 ,@z))
(assert-equal ____ `(,@x 45 6 ,@z))))
(define-test backquote-forms
;; Because of its properties, backquote is useful for constructing Lisp forms
;; that are macroexpansions or parts of macroexpansions.
(let ((variable 'x))
;; Fill in the blank without without using backquote/unquote notation.
(assert-equal ____
`(if (typep ,variable 'string)
(format nil "The value of ~A is ~A" ',variable ,variable)
(error 'type-error :datum ,variable
:expected-type 'string))))
(let ((error-type 'type-error)
(error-arguments '(:datum x :expected-type 'string)))
;; Fill in the blank without without using backquote/unquote notation.
(assert-equal ____
`(if (typep x 'string)
(format nil "The value of ~A is ~A" 'x x)
(error ',error-type ,@error-arguments)))))
(define-test numbers-and-words
(let ((number 5)
(word 'dolphin))
(true-or-false? ____ (equal '(1 3 5) `(1 3 5)))
(true-or-false? ____ (equal '(1 3 5) `(1 3 number)))
(assert-equal ____ `(1 3 ,number))
(assert-equal _____ `(word ,word ,word word))))
(define-test splicing
(let ((axis '(x y z)))
(assert-equal '(the axis are ____) `(the axis are ,axis))
(assert-equal '(the axis are ____) `(the axis are ,@axis)))
(let ((coordinates '((43.15 77.6) (42.36 71.06))))
(assert-equal ____ `(the coordinates are ,coordinates))
(assert-equal ____ `(the coordinates are ,@coordinates))))

View File

@ -0,0 +1,112 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(define-test setf
;; SETF is a macro used to assign values to places. A place is a concept;
;; it is an abstract "somewhere" where a value is stored.
(let ((a 10)
(b (list 1 20 30 40 50))
;; We use COPY-SEQ to create a copy of a string, because using SETF to
;; modify literal data (strings, lists, etc.) is undefined behaviour.
(c (copy-seq "I am Tom.")))
;; A place may be a variable.
(setf a 1000)
(assert-equal ____ a)
;; A place may be a part of some list.
(setf (first b) 10)
(assert-equal ____ b)
;; A place may be a character in a string.
;; The #\x syntax denotes a single character, 'x'.
(setf (char c 5) #\B
(char c 7) #\b)
(assert-equal ____ c)
;; There are other kinds of places that we will explore in the future.
))
(define-test case
;; CASE is a simple pattern-matching macro, not unlike C's "switch".
;; It compares an input against a set of values and evaluates the code for
;; the branch where a match is found.
(let* ((a 4)
(b (case a
(3 :three)
(4 :four)
(5 :five))))
(assert-equal ____ b))
;; CASE can accept a group of keys.
(let* ((c 4)
(d (case c
((0 2 4 6 8) :even-digit)
((1 3 5 7 9) :odd-digit))))
(assert-equal ____ d)))
(defun match-special-cases (thing)
;; T or OTHERWISE passed as the key matches any value.
;; NIL passed as the key matches no values.
;; These symbols need to passed in parentheses.
(case thing
(____ :found-a-t)
(____ :found-a-nil)
(____ :something-else)))
(define-test special-cases-of-case
;; You need to fill in the blanks in MATCH-SPECIAL-CASES.
(assert-equal :found-a-t (match-special-cases t))
(assert-equal :found-a-nil (match-special-cases nil))
(assert-equal :something-else (match-special-cases 42)))
(define-test your-own-case-statement
;; We use FLET to define a local function.
(flet ((cartoon-dads (input)
(case input
;; Fill in the blanks with proper cases.
____
____
____
(:this-one-doesnt-happen :fancy-cat)
(t :unknown))))
(assert-equal (cartoon-dads :bart) :homer)
(assert-equal (cartoon-dads :stewie) :peter)
(assert-equal (cartoon-dads :stan) :randy)
(assert-equal (cartoon-dads :space-ghost) :unknown)))
(define-test limits-of-case
;; So far, we have been comparing objects using EQUAL, one of the Lisp
;; comparison functions. CASE compares the keys using EQL, which is distinct
;; from EQUAL.
;; EQL is suitable for comparing numbers, characters, and objects for whom we
;; want to check verify they are the same object.
(let* ((string "A string")
(string-copy (copy-seq string)))
;; The above means that two distinct strings will not be the same under EQL,
;; even if they have the same contents.
(true-or-false? ____ (eql string string-copy))
(true-or-false? ____ (equal string string-copy))
;; The above also means that CASE might give surprising results when used on
;; strings.
(let ((match (case string
("A string" :matched)
(t :not-matched))))
(assert-equal ____ match))
;; We will explore this topic further in the EQUALITY-DISTINCTIONS lesson.
))
(define-test cond
;; COND is similar to CASE, except it is more general. It accepts arbitrary
;; conditions and checks them in order until one of them is met.
(let* ((number 4)
(result (cond ((> number 0) :positive)
((< number 0) :negative)
(t :zero))))
(assert-equal ____ result)))

181
lisp-koans/koans/clos.lisp Normal file
View File

@ -0,0 +1,181 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; CLOS is a shorthand for Common Lisp Object System.
(defclass racecar ()
;; A class definition lists all the slots of every instance.
(color speed))
(define-test defclass
;; Class instances are constructed via MAKE-INSTANCE.
(let ((car-1 (make-instance 'racecar))
(car-2 (make-instance 'racecar)))
;; Slot values can be set via SLOT-VALUE.
(setf (slot-value car-1 'color) :red)
(setf (slot-value car-1 'speed) 220)
(setf (slot-value car-2 'color) :blue)
(setf (slot-value car-2 'speed) 240)
(assert-equal ____ (slot-value car-1 'color))
(assert-equal ____ (slot-value car-2 'speed))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Common Lisp predefines the symbol SPEED in the COMMON-LISP package, which
;;; means that we cannot define a function named after it. The function SHADOW
;;; creates a new symbol with the same name in the current package and shadows
;;; the predefined one within the current package.
(shadow 'speed)
(defclass spaceship ()
;; It is possible to define reader, writer, and accessor functions for slots.
((color :reader color :writer (setf color))
(speed :accessor speed)))
;;; Specifying a reader function named COLOR is equivalent to
;;; (DEFMETHOD COLOR ((OBJECT SPACECSHIP)) ...)
;;; Specifying a writer function named (SETF COLOR) is equivalent to
;;; (DEFMETHOD (SETF COLOR) (NEW-VALUE (OBJECT SPACECSHIP)) ...)
;;; Specifying an accessor function performs both of the above.
(define-test accessors
(let ((ship (make-instance 'spaceship)))
(setf (color ship) :orange
(speed ship) 1000)
(assert-equal ____ (color ship))
(assert-equal ____ (speed ship))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defclass bike ()
;; It is also possible to define initial arguments for slots.
((color :reader color :initarg :color)
(speed :reader speed :initarg :speed)))
(define-test initargs
(let ((bike (make-instance 'bike :color :blue :speed 30)))
(assert-equal ____ (color bike))
(assert-equal ____ (speed bike))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Lisp classes can inherit from one another.
(defclass person ()
((name :initarg :name :accessor person-name)))
(defclass lisp-programmer (person)
((favorite-lisp-implementation :initarg :favorite-lisp-implementation
:accessor favorite-lisp-implementation)))
(defclass c-programmer (person)
((favorite-c-compiler :initarg :favorite-c-compiler
:accessor favorite-c-compiler)))
(define-test inheritance
(let ((jack (make-instance 'person :name :jack))
(bob (make-instance 'lisp-programmer
:name :bob
:favorite-lisp-implementation :sbcl))
(adam (make-instance 'c-programmer
:name :adam
:favorite-c-compiler :clang)))
(assert-equal ____ (person-name jack))
(assert-equal ____ (person-name bob))
(assert-equal ____ (favorite-lisp-implementation bob))
(assert-equal ____ (person-name adam))
(assert-equal ____ (favorite-c-compiler adam))
(true-or-false? ____ (typep bob 'person))
(true-or-false? ____ (typep bob 'lisp-programmer))
(true-or-false? ____ (typep bob 'c-programmer))))
;;; This includes multiple inheritance.
(defclass clisp-programmer (lisp-programmer c-programmer) ())
(define-test multiple-inheritance
(let ((zenon (make-instance 'clisp-programmer
:name :zenon
:favorite-lisp-implementation :clisp
:favorite-c-compiler :gcc)))
(assert-equal ____ (person-name zenon))
(assert-equal ____ (favorite-lisp-implementation zenon))
(assert-equal ____ (favorite-c-compiler zenon))
(true-or-false? ____ (typep zenon 'person))
(true-or-false? ____ (typep zenon 'lisp-programmer))
(true-or-false? ____ (typep zenon 'c-programmer))
(true-or-false? ____ (typep zenon 'clisp-programmer))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Multiple inheritance makes it possible to work with mixin classes.
(defclass greeting-mixin ()
((greeted-people :accessor greeted-people :initform '())))
(defgeneric greet (greeter greetee))
(defmethod greet ((object greeting-mixin) name)
;; PUSHNEW is similar to PUSH, but it does not modify the place if the object
;; we want to push is already found on the list in the place.
(pushnew name (greeted-people object) :test #'equal)
(format nil "Hello, ~A." name))
(defclass chatbot ()
((version :reader version :initarg :version)))
(defclass greeting-chatbot (greeting-mixin chatbot) ())
(define-test greeting-chatbot ()
(let ((chatbot (make-instance 'greeting-chatbot :version "1.0.0")))
(true-or-false? ____ (typep chatbot 'greeting-mixin))
(true-or-false? ____ (typep chatbot 'chatbot))
(true-or-false? ____ (typep chatbot 'greeting-chatbot))
(assert-equal ____ (greet chatbot "Tom"))
(assert-equal ____ (greeted-people chatbot))
(assert-equal ____ (greet chatbot "Sue"))
(assert-equal ____ (greet chatbot "Mark"))
(assert-equal ____ (greet chatbot "Kate"))
(assert-equal ____ (greet chatbot "Mark"))
(assert-equal ____ (greeted-people chatbot))
(assert-equal ____ (version chatbot))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defclass american (person) ())
(defclass italian (person) ())
(defgeneric stereotypical-food (person)
;; The :METHOD option in DEFGENERIC is an alternative to DEFMETHOD.
(:method ((person italian)) :pasta)
(:method ((person american)) :burger))
;;; When methods or slot definitions of superclasses overlap with each other,
;;; the order of superclasses is used to resolve the conflict.
(defclass stereotypical-person (american italian) ())
(defclass another-stereotypical-person (italian american) ())
(define-test stereotypes
(let ((james (make-instance 'american))
(antonio (make-instance 'italian))
(roy (make-instance 'stereotypical-person))
(mary (make-instance 'another-stereotypical-person)))
(assert-equal ____ (stereotypical-food james))
(assert-equal ____ (stereotypical-food antonio))
(assert-equal ____ (stereotypical-food roy))
(assert-equal ____ (stereotypical-food mary))))

View File

@ -0,0 +1,265 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; Lisp condition types are very similar to classes. The standard specifies
;;; multiple standard condition types: among them, CONDITION, WARNING,
;;; SERIOUS-CONDITION, and ERROR.
;;; The type CONDITION is the base type of all condition objects.
(define-condition my-condition () ())
;;; The type WARNING is the base type of all conditions of which the programmer
;;; should be warned, unless the condition is somehow handled by the program.
(define-condition my-warning (warning) ())
;;; The type SERIOUS-CONDITION includes programming errors and other situations
;;; where computation cannot proceed (e.g. due to memory or storage issues).
(define-condition my-serious-condition (serious-condition) ())
;;; The type ERROR is the base type for all error situations in code.
(define-condition my-error (error) ())
(define-test type-hierarchy
;; Inheritance for condition types works the same way as for classes.
(let ((condition (make-condition 'my-condition)))
(true-or-false? ____ (typep condition 'my-condition))
(true-or-false? ____ (typep condition 'condition))
(true-or-false? ____ (typep condition 'warning))
(true-or-false? ____ (typep condition 'error)))
(let ((condition (make-condition 'my-warning)))
(true-or-false? ____ (typep condition 'my-warning))
(true-or-false? ____ (typep condition 'warning))
(true-or-false? ____ (typep condition 'error)))
(let ((condition (make-condition 'my-serious-condition)))
(true-or-false? ____ (typep condition 'my-serious-condition))
(true-or-false? ____ (typep condition 'serious-condition))
(true-or-false? ____ (typep condition 'warning))
(true-or-false? ____ (typep condition 'error)))
(let ((condition (make-condition 'my-error)))
(true-or-false? ____ (typep condition 'my-error))
(true-or-false? ____ (typep condition 'my-serious-condition))
(true-or-false? ____ (typep condition 'serious-condition))
(true-or-false? ____ (typep condition 'warning))
(true-or-false? ____ (typep condition 'error))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; A condition handler is composed of a handler function that accepts a
;;; condition object and a condition type for which the function will be called.
(defvar *list*)
(define-condition silly-condition () ())
(define-condition very-silly-condition (silly-condition) ())
(define-condition most-silly-condition (very-silly-condition) ())
(defun handle-silly-condition (condition)
(declare (ignore condition))
(push :silly-condition *list*))
(defun handle-very-silly-condition (condition)
(declare (ignore condition))
(push :very-silly-condition *list*))
(defun handle-most-silly-condition (condition)
(declare (ignore condition))
(push :most-silly-condition *list*))
(define-test handler-bind
;; When a condition is signaled, all handlers whose type matches the
;; condition's type are allowed to execute.
(let ((*list* '()))
(handler-bind ((very-silly-condition #'handle-very-silly-condition)
(silly-condition #'handle-silly-condition)
(most-silly-condition #'handle-most-silly-condition))
(signal (make-condition 'most-silly-condition)))
(assert-equal ____ *list*)))
(define-test handler-order
;; The order of binding handlers matters.
(let ((*list* '()))
(handler-bind ((silly-condition #'handle-silly-condition)
(very-silly-condition #'handle-very-silly-condition)
(most-silly-condition #'handle-most-silly-condition))
(signal (make-condition 'most-silly-condition)))
(assert-equal ____ *list*)))
(define-test multiple-handler-binds
;; It is possible to bind handlers in steps.
(let ((*list* '()))
(handler-bind ((silly-condition #'handle-silly-condition)
(most-silly-condition #'handle-most-silly-condition))
(handler-bind ((very-silly-condition #'handle-very-silly-condition))
(signal (make-condition 'most-silly-condition))))
(assert-equal ____ *list*)))
(define-test same-handler
;; The same handler may be bound multiple times.
(let ((*list* '()))
(handler-bind ((silly-condition #'handle-silly-condition)
(silly-condition #'handle-silly-condition))
(handler-bind ((very-silly-condition #'handle-very-silly-condition)
(silly-condition #'handle-silly-condition)
(very-silly-condition #'handle-very-silly-condition))
(signal (make-condition 'most-silly-condition))))
(assert-equal ____ *list*)))
(define-test handler-types
;; A handler is not executed if it does not match the condition type.
(let ((*list* '()))
(handler-bind ((silly-condition #'handle-silly-condition)
(very-silly-condition #'handle-very-silly-condition)
(most-silly-condition #'handle-most-silly-condition))
(signal (make-condition 'very-silly-condition)))
(assert-equal ____ *list*)))
(define-test handler-transfer-of-control
;; A handler may decline to handle the condition if it returns normally,
;; or it may handle the condition by transferring control elsewhere.
(let ((*list* '()))
(block my-block
(handler-bind ((silly-condition #'handle-silly-condition)
(silly-condition (lambda (condition)
(declare (ignore condition))
(return-from my-block)))
(silly-condition #'handle-silly-condition))
(signal (make-condition 'silly-condition))))
(assert-equal ____ *list*)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun handle-error (condition)
(declare (ignore condition))
(push :error *list*))
(define-condition my-error (error) ())
(defun handle-my-error (condition)
(declare (ignore condition))
(push :my-error *list*))
(define-test handler-case
;; HANDLER-CASE always transfers control before executing the case forms.
(let ((*list* '()))
(handler-case (signal (make-condition 'my-error))
(error (condition) (handle-error condition))
(my-error (condition) (handle-my-error condition)))
(assert-equal ____ *list*)))
(define-test handler-case-order
;; The order of handler cases matters.
(let ((*list* '()))
(handler-case (signal (make-condition 'my-error))
(my-error (condition) (handle-my-error condition))
(error (condition) (handle-error condition)))
(assert-equal ____ *list*)))
(define-test handler-case-type
;; A handler cases is not executed if it does not match the condition type.
(let ((*list* '()))
(handler-case (signal (make-condition 'error))
(my-error (condition) (handle-my-error condition))
(error (condition) (handle-error condition)))
(assert-equal ____ *list*)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun divide (numerator denominator)
(/ numerator denominator))
(define-test error-signaling
;; ASSERT-ERROR is a Lisp Koans macro which verifies that the correct error
;; type is signaled.
(assert-equal 3 (divide 6 2))
(assert-error (divide 6 0) 'division-by-zero)
(assert-error (divide 6 :zero) 'type-error))
(define-test error-signaling-handler-case
(flet ((try-to-divide (numerator denominator)
;; In code outside Lisp Koans, HANDLER-CASE should be used.
(handler-case (divide numerator denominator)
(division-by-zero () :division-by-zero)
(type-error () :type-error))))
(assert-equal ____ (try-to-divide 6 2))
(assert-equal ____ (try-to-divide 6 0))
(assert-equal ____ (try-to-divide 6 :zero))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Condition objects can contain metadata about the specific situation that
;;; occurred in the code.
(define-test accessors-division-by-zero
(let ((condition (handler-case (divide 6 0) (division-by-zero (c) c))))
;; Disabled on CLISP and ABCL due to conformance bugs.
;; See https://gitlab.com/gnu-clisp/clisp/-/issues/22
;; See https://github.com/armedbear/abcl/issues/177
#-(or clisp abcl)
(assert-equal ____ (arithmetic-error-operands condition))
(let ((operation (arithmetic-error-operation condition)))
;; Disabled on ABCL due to a conformance bug.
;; See https://github.com/armedbear/abcl/issues/177
#-abcl
(assert-equal ____ (funcall operation 12 4)))))
(define-test accessors-type-error
(let ((condition (handler-case (divide 6 :zero) (type-error (c) c))))
(assert-equal ____ (type-error-datum condition))
(let ((expected-type (type-error-expected-type condition)))
(true-or-false? ____ (typep :zero expected-type))
(true-or-false? ____ (typep 0 expected-type))
(true-or-false? ____ (typep "zero" expected-type))
(true-or-false? ____ (typep 0.0 expected-type)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; We can define slots in our own condition types in a way that is similar to
;; DEFCLASS.
(define-condition parse-log-line-error (parse-error)
((line :initarg :line :reader line)
(reason :initarg :reason :reader reason)))
(defun log-line-type (line)
;; The macro CHECK-TYPE signals a TYPE-ERROR if the object is not of the
;; specified type.
(check-type line string)
(cond ((eql 0 (search "TIMESTAMP" line)) :timestamp)
((eql 0 (search "HTTP" line)) :http)
((eql 0 (search "LOGIN" line)) :login)
;; The function ERROR should be used for signaling serious conditions
;; and errors: if the condition is not handled, it halts program
;; execution and starts the Lisp debugger.
(t (error 'parse-log-line-error :line line
:reason :unknown-log-line-type))))
(define-test log-line-type-errors
(flet ((try-log-line-type (line)
(handler-case (log-line-type line)
(error (condition) condition))))
(assert-equal ____ (try-log-line-type "TIMESTAMP 2020-05-08 16:59:39"))
(assert-equal ____ (try-log-line-type "HTTP GET / from 127.0.0.1"))
(assert-equal ____ (try-log-line-type "LOGIN administrator:hunter2"))
(let ((condition (try-log-line-type "WARNING: 95% of disk space used")))
(assert-equal ____ (line condition))
(assert-equal ____ (reason condition)))
(let ((condition (try-log-line-type 5555)))
(assert-equal 'string (____ condition))
(assert-equal 5555 (____ condition)))))

View File

@ -0,0 +1,68 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(define-test if
;; IF only evaluates and returns one branch of a conditional expression.
(assert-equal ____ (if t :true :false))
(assert-equal ____ (if nil :true :false))
;; This also applies to side effects that migh or might not be evaluated.
(let ((result))
(if t
(setf result :true)
(setf result :false))
(assert-equal ____ result)
(if nil
(setf result :true)
(setf result :false))
(assert-equal ____ result)))
(define-test when-unless
;; WHEN and UNLESS are like one-branched IF statements.
(let ((when-result nil)
(when-numbers '())
(unless-result nil)
(unless-numbers '()))
(dolist (x '(1 2 3 4 5 6 7 8 9 10))
(when (> x 5)
(setf when-result x)
(push x when-numbers))
(unless (> x 5)
(setf unless-result x)
(push x unless-numbers)))
(assert-equal ____ when-result)
(assert-equal ____ when-numbers)
(assert-equal ____ unless-result)
(assert-equal ____ unless-numbers)))
(define-test and-short-circuit
;; AND only evaluates forms until one evaluates to NIL.
(assert-equal ____
(let ((x 0))
(and
(setf x (+ 2 x))
(setf x (+ 3 x))
nil
(setf x (+ 4 x)))
x)))
(define-test or-short-circuit
;; OR only evaluates forms until one evaluates to non-NIL.
(assert-equal ____
(let ((x 0))
(or
(setf x (+ 2 x))
(setf x (+ 3 x))
nil
(setf x (+ 4 x)))
x)))

View File

@ -0,0 +1,93 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; In this project, we are going to define a CLOS class representing a simple
;;; set of dice. There are only two operations on the dice: reading the dice
;;; values and re-rolling their values.
(defclass dice-set ()
;; Fill in the blank with a proper slot definition.
(____))
;;; This method might be unnecessary, depending on how you define the slots of
;;; DICE-SET.
(defmethod dice-values ((object dice-set))
____)
(defmethod roll (count (object dice-set))
____)
(define-test make-dice-set
(let ((dice (make-instance 'dice-set)))
(assert-true (typep dice 'dice-set))))
(define-test dice-are-six-sided
(let ((dice (make-instance 'dice-set)))
(roll 5 dice)
(assert-true (typep (dice-values dice) 'list))
(assert-equal 5 (length (dice-values dice)))
(dolist (die (dice-values dice))
(assert-true (typep die '(integer 1 6))))))
(define-test dice-values-do-not-change-without-rolling
(let ((dice (make-instance 'dice-set)))
(roll 100 dice)
(let ((dice-values-1 (dice-values dice))
(dice-values-2 (dice-values dice)))
(assert-equal dice-values-1 dice-values-2))))
(define-test roll-returns-new-dice-values
(let* ((dice (make-instance 'dice-set))
(dice-values (roll 100 dice)))
(assert-true (equal dice-values (dice-values dice)))))
(define-test dice-values-should-change-between-rolling
(let* ((dice (make-instance 'dice-set))
(first-time (roll 100 dice))
(second-time (roll 100 dice)))
(assert-false (equal first-time second-time))
(assert-true (equal second-time (dice-values dice)))))
(define-test different-dice-sets-have-different-values
(let* ((dice-1 (make-instance 'dice-set))
(dice-2 (make-instance 'dice-set)))
(roll 100 dice-1)
(roll 100 dice-2)
(assert-false (equal (dice-values dice-1) (dice-values dice-2)))))
(define-test different-numbers-of-dice
(let ((dice (make-instance 'dice-set)))
(assert-equal 5 (length (roll 5 dice)))
(assert-equal 100 (length (roll 100 dice)))
(assert-equal 1 (length (roll 1 dice)))))
(define-test junk-as-dice-count
(let ((dice (make-instance 'dice-set)))
(labels ((dice-failure (count)
(handler-case (progn (roll count dice)
(error "Test failure"))
(error (condition) condition)))
(test-dice-failure (value)
(let* ((condition (dice-failure value))
(expected-type (type-error-expected-type condition)))
(assert-true (typep condition 'type-error))
(assert-equal value (type-error-datum condition))
(assert-true (subtypep '(integer 1 6) expected-type)))))
(test-dice-failure 0)
(test-dice-failure "0")
(test-dice-failure :zero)
(test-dice-failure 18.0)
(test-dice-failure -7)
(test-dice-failure '(6 6 6)))))

View File

@ -0,0 +1,121 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; The most common equality predicates in Common Lisp are, in order of
;;; strictness, EQ, EQL, EQUAL, and EQUALP.
(define-test eq
;; EQ checks the identity of the two objects; it checks whether the two
;; objects are, in fact, one and the same object.
;; It is the fastest of the four; however, not guaranteed to work on numbers
;; and characters because of that.
(true-or-false? ____ (eq 'a 'a))
(true-or-false? ____ (eq 3 3.0))
(true-or-false? ____ (eq '(1 2) '(1 2)))
(true-or-false? ____ (eq "Foo" "Foo"))
(true-or-false? ____ (eq "Foo" (copy-seq "Foo")))
(true-or-false? ____ (eq "FOO" "Foo")))
(define-test eql
;; EQL works like EQ, except it is specified to work for numbers and
;; characters.
;; Two numbers are EQL if they are of the same type and represent the same
;; number. Two characters are EQL if they represent the same character.
(true-or-false? ____ (eql 'a 'a))
(true-or-false? ____ (eql 3 3))
(true-or-false? ____ (eql 3 3.0))
(true-or-false? ____ (eql '(1 2) '(1 2)))
(true-or-false? ____ (eql '(:a . :b) '(:a . :b)))
(true-or-false? ____ (eql #\S #\S))
(true-or-false? ____ (eql "Foo" "Foo"))
(true-or-false? ____ (eql "Foo" (copy-seq "Foo")))
(true-or-false? ____ (eql "FOO" "Foo")))
(define-test equal
;; EQUAL works like EQL, except works differently for lists, strings, bit
;; vectors, and pathnames.
;; Two lists, strings, bit arrays, or pathnames are EQUAL if they have EQUAL
;; elements.
(true-or-false? ____ (equal 'a 'a))
(true-or-false? ____ (equal 3 3))
(true-or-false? ____ (equal 3 3.0))
(true-or-false? ____ (equal '(1 2) '(1 2)))
(true-or-false? ____ (equal '(:a . :b) '(:a . :b)))
(true-or-false? ____ (equal '(:a . :b) '(:a . :doesnt-match)))
(true-or-false? ____ (equal #\S #\S))
(true-or-false? ____ (equal "Foo" "Foo"))
(true-or-false? ____ (equal #*01010101 #*01010101))
(true-or-false? ____ (equal "Foo" (copy-seq "Foo")))
(true-or-false? ____ (equal "FOO" "Foo"))
(true-or-false? ____ (equal #p"foo/bar/baz" #p"foo/bar/baz")))
(defstruct thing slot-1 slot-2)
(define-test equalp
;; EQUALP works like EQUAL, except it works differently for characters,
;; numbers, arrays, structures, and hash tables.
;; Two characters are EQUALP if they represent the same character, ignoring
;; the differences in character case.
;; Two numbers are EQUALP if they represent the same number, even if they are
;; of different types.
;; Two arrays are EQUALP if they have the same dimensions and their characters
;; are pairwise EQUALP.
;; Two structures are EQUALP if they are of the same class and their slots are
;; pairwise EQUALP.
;; We will contemplate hash tables in the HASH-TABLES lesson.
(true-or-false? ____ (equalp 'a 'a))
(true-or-false? ____ (equalp 3 3))
(true-or-false? ____ (equalp 3 3.0))
(true-or-false? ____ (equalp '(1 2) '(1 2)))
(true-or-false? ____ (equalp '(:a . :b) '(:a . :b)))
(true-or-false? ____ (equalp '(:a . :b) '(:a . :doesnt-match)))
(true-or-false? ____ (equalp #\S #\S))
(true-or-false? ____ (equalp "Foo" "Foo"))
(true-or-false? ____ (equalp "Foo" (copy-seq "Foo")))
(true-or-false? ____ (equalp "FOO" "Foo"))
(true-or-false? ____ (equalp (make-array '(4 2) :initial-element 0)
(make-array '(4 2) :initial-element 0)))
(true-or-false? ____ (equalp (make-thing :slot-1 42 :slot-2 :forty-two)
(make-thing :slot-1 42 :slot-2 :forty-two))))
;;; In additional to the generic equality predicates, Lisp also provides
;;; type-specific predicates for numbers, strings, and characters.
(define-test =
;; The function = behaves just like EQUALP on numbers.
;; #C(... ...) is syntax sugar for creating a complex number.
(true-or-false? ____ (= 99.0 99 99.000 #C(99 0) #C(99.0 0.0)))
(true-or-false? ____ (= 0 1 -1))
(true-or-false? ____ (= (/ 2 3) (/ 6 9) (/ 86 129))))
(define-test string=
;; The function STRING= behaves just like EQUAL on strings.
;; The function STRING-EQUAL behaves just like EQUALP on strings.
(true-or-false? ____ (string= "Foo" "Foo"))
(true-or-false? ____ (string= "Foo" "FOO"))
(true-or-false? ____ (string-equal "Foo" "FOO"))
;; These functions accept additional keyword arguments that allow one to
;; only compare parts of the strings.
(true-or-false? ____ (string= "together" "frog" :start1 1 :end1 3
:start2 2))
(true-or-false? ____ (string-equal "together" "FROG" :start1 1 :end1 3
:start2 2)))
(define-test char=
;; The function CHAR= behaves just like EQL on characters.
;; The function CHAR-EQUAL behaves just like EQUALP on characters.
(true-or-false? ____ (char= #\A (char "ABCDEF" 0)))
(true-or-false? ____ (char= #\A #\a))
(true-or-false? ____ (char-equal #\A (char "ABCDEF" 0)))
(true-or-false? ____ (char-equal #\A #\a)))

View File

@ -0,0 +1,66 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; In most imperative languages, the syntax of a function call has the function
;;; name succeeded by a list of arguments. In Lisp, the function name and
;;; arguments are all part of the same list, with the function name the first
;;; element of that list.
(define-test function-names
;; In these examples, +, -, *, and / are function names.
(assert-equal ____ (+ 2 3))
(assert-equal ____ (- 1 3))
(assert-equal ____ (* 7 4))
(assert-equal ____ (/ 100 4)))
(define-test numberp
;; NUMBERP is a predicate which returns true if its argument is a number.
(assert-equal ____ (numberp 5))
(assert-equal ____ (numberp 2.0))
(assert-equal ____ (numberp "five")))
(define-test evaluation-order
;; Arguments to a function are evaluated before the function is called.
(assert-equal ____ (* (+ 1 2) (- 13 10))))
(define-test basic-comparisons
;; The below functions are boolean functions (predicates) that operate on
;; numbers.
(assert-equal ____ (> 25 4))
(assert-equal ____ (< 8 2))
(assert-equal ____ (= 3 3))
(assert-equal ____ (<= 6 (/ 12 2)))
(assert-equal ____ (>= 20 (+ 1 2 3 4 5)))
(assert-equal ____ (/= 15 (+ 4 10))))
(define-test quote
;; Preceding a list with a quote (') will tell Lisp not to evaluate a list.
;; The quote special form suppresses normal evaluation, and instead returns
;; the literal list.
;; Evaluating the form (+ 1 2) returns the number 3, but evaluating the form
;; '(+ 1 2) returns the list (+ 1 2).
(assert-equal ____ (+ 1 2))
(assert-equal ____ '(+ 1 2))
(assert-equal ____ (list '+ 1 2))
;; The 'X syntax is syntactic sugar for (QUOTE X).
(true-or-false? ____ (equal '(/ 4 0) (quote (/ 4 0)))))
(define-test listp
;; LISTP is a predicate which returns true if the argument is a list.
(assert-equal ____ (listp '(1 2 3)))
(assert-equal ____ (listp 100))
(assert-equal ____ (listp "Hello world"))
(assert-equal ____ (listp nil))
(assert-equal ____ (listp (+ 1 2)))
(assert-equal ____ (listp '(+ 1 2))))

View File

@ -0,0 +1,26 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; EXTRA CREDIT:
;;;
;;; Create a program that will play the Greed game.
;;; The full rules for the game are in the file extra-credit.txt.
;;;
;;; You already have a DICE-SET class and a score function you can use.
;;; Write a PLAYER class and a GAME class to complete the project.
;;;
;;; This is a free form assignment, so approach it however you desire.
(define-test play-greed
(assert-true ____))

View File

@ -0,0 +1,66 @@
= Playing Greed
Greed is a dice game played among 2 or more players, using 5
six-sided dice.
== Playing Greed
Each player takes a turn consisting of one or more rolls of the dice.
On the first roll of the game, a player rolls all five dice which are
scored according to the following:
Three 1's => 1000 points
Three 6's => 600 points
Three 5's => 500 points
Three 4's => 400 points
Three 3's => 300 points
Three 2's => 200 points
One 1 => 100 points
One 5 => 50 points
A single die can only be counted once in each roll. For example,
a "5" can only count as part of a triplet (contributing to the 500
points) or as a single 50 points, but not both in the same roll.
Example Scoring
Throw Score
--------- ------------------
5 1 3 4 1 50 + 2 * 100 = 250
1 1 1 3 1 1000 + 100 = 1100
2 4 4 5 4 400 + 50 = 450
The dice not contributing to the score are called the non-scoring
dice. "3" and "4" are non-scoring dice in the first example. "3" is
a non-scoring die in the second, and "2" is a non-score die in the
final example.
After a player rolls and the score is calculated, the scoring dice are
removed and the player has the option of rolling again using only the
non-scoring dice. If all of the thrown dice are scoring, then the
player may roll all 5 dice in the next roll.
The player may continue to roll as long as each roll scores points. If
a roll has zero points, then the player loses not only their turn, but
also accumulated score for that turn. If a player decides to stop
rolling before rolling a zero-point roll, then the accumulated points
for the turn is added to his total score.
== Getting "In The Game"
Before a player is allowed to accumulate points, they must get at
least 300 points in a single turn. Once they have achieved 300 points
in a single turn, the points earned in that turn and each following
turn will be counted toward their total score.
== End Game
Once a player reaches 3000 (or more) points, the game enters the final
round where each of the other players gets one more turn. The winner
is the player with the highest score after the final round.
== References
Greed is described on Wikipedia at
http://en.wikipedia.org/wiki/Greed_(dice_game), however the rules are
a bit different from the rules given here.

View File

@ -0,0 +1,84 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; The function FORMAT is used to create formatted output. It is similar to
;;; the C function printf().
;;; See http://www.gigamonkeys.com/book/a-few-format-recipes.html
;;; T as the first argument to FORMAT prints the string to standard output.
;;; NIL as the first argument to FORMAT causes it to return the string.
(define-test format-basic
;; If there are no format directives in the string, FORMAT will return
;; a string that is STRING= to its format control.
(assert-equal ____ (format nil "Lorem ipsum dolor sit amet")))
(define-test format-aesthetic
;; The ~A format directive creates aesthetic output.
(assert-equal ____ (format nil "This is the number ~A" 42))
(assert-equal ____ (format nil "This is the keyword ~A" :foo))
(assert-equal ____ (format nil "~A evaluates to ~A"
'(/ 24 (- 3 (/ 8 3)))
(/ 24 (- 3 (/ 8 3)))))
(assert-equal ____ (format nil "This is the character ~A" #\C))
(assert-equal ____ (format nil "In a ~A" "galaxy far far away")))
(define-test format-standard
;; The ~S format directive prints objects with escape characters.
;; Not all Lisp objects require to be escaped.
(assert-equal ____ (format nil "This is the number ~S" 42))
(assert-equal ____ (format nil "~S evaluates to ~S"
'(/ 24 (- 3 (/ 8 3)))
(/ 24 (- 3 (/ 8 3)))))
;; Keywords are printed with their leading colon.
(assert-equal ____ (format nil "This is the keyword ~S" :foo))
;; Characters are printed in their #\X form. The backslash will need to be
;; escaped inside the printed string, just like in "#\\X".
(assert-equal ____ (format nil "This is the character ~S" #\C))
;; Strings include quote characters, which must be escaped:
;; such a string might look in code like "foo \"bar\"".
(assert-equal ____ (format nil "In a ~S" "galaxy far far away")))
(define-test format-radix
;; The ~B, ~O, ~D, and ~X radices print numbers in binary, octal, decimal, and
;; hexadecimal notation.
(assert-equal ____ (format nil "This is the number ~B" 42))
(assert-equal ____ (format nil "This is the number ~O" 42))
(assert-equal ____ (format nil "This is the number ~D" 42))
(assert-equal ____ (format nil "This is the number ~X" 42))
;; We can specify a custom radix by using the ~R directive.
(assert-equal ____ (format nil "This is the number ~3R" 42))
;; It is possible to print whole forms this way.
(let ((form '(/ 24 (- 3 (/ 8 3))))
(result (/ 24 (- 3 (/ 8 3)))))
(assert-equal ____ (format nil "~B evaluates to ~B" form result))
(assert-equal ____ (format nil "~O evaluates to ~O" form result))
(assert-equal ____ (format nil "~D evaluates to ~D" form result))
(assert-equal ____ (format nil "~X evaluates to ~X" form result))
(assert-equal ____ (format nil "~3R evaluates to ~3R" form result))))
(define-test format-iteration
;; The ~{ and ~} directives iterate over a list.
(assert-equal ____ (format nil "~{[~A]~}" '(1 2 3 4 5 6)))
(assert-equal ____ (format nil "~{[~A ~A]~}" '(1 2 3 4 5 6)))
;; The directive ~^ aborts iteration when no more elements remain.
(assert-equal ____ (format nil "~{[~A]~^, ~}" '(1 2 3 4 5 6))))
(define-test format-case
;; The ~( and ~) directives adjust the string case.
(assert-equal ____ (format nil "~(~A~)" "The QuIcK BROWN fox"))
;; Some FORMAT directives can be further adjusted with the : and @ modifiers.
(assert-equal ____ (format nil "~:(~A~)" "The QuIcK BROWN fox"))
(assert-equal ____ (format nil "~@(~A~)" "The QuIcK BROWN fox"))
(assert-equal ____ (format nil "~:@(~A~)" "The QuIcK BROWN fox")))

View File

@ -0,0 +1,180 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(defun some-named-function (a b)
(+ a b))
(define-test call-a-function
;; DEFUN can be used to define global functions.
(assert-equal ____ (some-named-function 4 5))
;; FLET can be used to define local functions.
(flet ((another-named-function (a b) (* a b)))
(assert-equal ____ (another-named-function 4 5)))
;; LABELS can be used to define local functions which can refer to themselves
;; or each other.
(labels ((recursive-function (a b)
(if (or (= 0 a) (= 0 b))
1
(+ (* a b) (recursive-function (1- a) (1- b))))))
(assert-equal ____ (recursive-function 4 5))))
(define-test shadow-a-function
(assert-eq 18 (some-named-function 7 11))
;; FLET and LABELS can shadow function definitions.
(flet ((some-named-function (a b) (* a b)))
(assert-equal ____ (some-named-function 7 11)))
(assert-equal ____ (some-named-function 7 11)))
(defun function-with-optional-parameters (&optional (a 2) (b 3) c)
;; If an optional argument to a function is not provided, it is given its
;; default value, or NIL, if no default value is specified.
(list a b c))
(define-test optional-parameters
(assert-equal ____ (function-with-optional-parameters 42 24 4224))
(assert-equal ____ (function-with-optional-parameters 42 24))
(assert-equal ____ (function-with-optional-parameters 42))
(assert-equal ____ (function-with-optional-parameters)))
(defun function-with-optional-indication
(&optional (a 2 a-provided-p) (b 3 b-provided-p))
;; It is possible to check whether an optional argument was provided.
(list a a-provided-p b b-provided-p))
(define-test optional-indication
(assert-equal ____ (function-with-optional-indication 42 24))
(assert-equal ____ (function-with-optional-indication 42))
(assert-equal ____ (function-with-optional-indication)))
(defun function-with-rest-parameter (&rest x)
;; A rest parameter gathers all remaining parameters in a list.
x)
(define-test rest-parameter
(assert-equal ____ (function-with-rest-parameter))
(assert-equal ____ (function-with-rest-parameter 1))
(assert-equal ____ (function-with-rest-parameter 1 :two 333)))
(defun function-with-keyword-parameters (&key (a :something) b c)
;; A keyword parameters is similar to an optional parameter, but is provided
;; by a keyword-value pair.
(list a b c))
(define-test keyword-parameters ()
(assert-equal ____ (function-with-keyword-parameters))
(assert-equal ____ (function-with-keyword-parameters :a 11 :b 22 :c 33))
;; It is not necessary to specify all keyword parameters.
(assert-equal ____ (function-with-keyword-parameters :b 22))
;; Keyword argument order is not important.
(assert-equal ____ (function-with-keyword-parameters :b 22 :c -5/2 :a 0))
;; Lisp handles duplicate keyword parameters.
(assert-equal ____ (function-with-keyword-parameters :b 22 :b 40 :b 812)))
(defun function-with-keyword-indication
(&key (a 2 a-provided-p) (b 3 b-provided-p))
;; It is possible to check whether a keyword argument was provided.
(list a a-provided-p b b-provided-p))
(define-test keyword-indication
(assert-equal ____ (function-with-keyword-indication))
(assert-equal ____ (function-with-keyword-indication :a 3 :b 4))
(assert-equal ____ (function-with-keyword-indication :a 11 :b 22))
(assert-equal ____ (function-with-keyword-indication :b 22))
(assert-equal ____ (function-with-keyword-indication :b 22 :a 0)))
(defun function-with-funky-parameters (a &rest x &key b (c a c-provided-p))
;; Lisp functions can have surprisingly complex lambda lists.
;; A &rest parameter must come before &key parameters.
(list a b c c-provided-p x))
(define-test funky-parameters
(assert-equal ____ (function-with-funky-parameters 1))
(assert-equal ____ (function-with-funky-parameters 1 :b 2))
(assert-equal ____ (function-with-funky-parameters 1 :b 2 :c 3))
(assert-equal ____ (function-with-funky-parameters 1 :c 3 :b 2)))
(define-test lambda
;; A list form starting with the symbol LAMBDA denotes an anonymous function.
;; It is possible to call that function immediately or to store it for later
;; use.
(let ((my-function (lambda (a b) (* a b))))
(assert-equal ____ (funcall my-function 11 9)))
;; A LAMBDA form is allowed to take the place of a function name.
(assert-equal ____ ((lambda (a b) (+ a b)) 10 9))
(let ((functions (list (lambda (a b) (+ a b))
(lambda (a b) (- a b))
(lambda (a b) (* a b))
(lambda (a b) (/ a b)))))
(assert-equal ____ (funcall (first functions) 2 33))
(assert-equal ____ (funcall (second functions) 2 33))
(assert-equal ____ (funcall (third functions) 2 33))
(assert-equal ____ (funcall (fourth functions) 2 33))))
(define-test lambda-with-optional-parameters
(assert-equal ____ ((lambda (a &optional (b 100)) (+ a b)) 10 9))
(assert-equal ____ ((lambda (a &optional (b 100)) (+ a b)) 10)))
(defun make-adder (x)
;; MAKE-ADDER will create a function that closes over the parameter X.
;; The parameter will be remembered as a part of the environment of the
;; returned function, which will continue refering to it.
(lambda (y) (+ x y)))
(define-test lexical-closures
(let ((adder-100 (make-adder 100))
(adder-500 (make-adder 500)))
;; ADD-100 and ADD-500 now close over different values.
(assert-equal ____ (funcall adder-100 3))
(assert-equal ____ (funcall adder-500 3))))
(defun make-reader-and-writer (x)
;; Both returned functions will refer to the same place.
(list (function (lambda () x))
(function (lambda (y) (setq x y)))))
(define-test lexical-closure-interactions
;; The macro DESTRUCTURING-BIND is like LET, except it binds the variables
;; listed in its first argument to the parts of the list returned by the form
;; that is its second argument.
(destructuring-bind (reader-1 writer-1) (make-reader-and-writer 1)
(destructuring-bind (reader-2 writer-2) (make-reader-and-writer :one)
(assert-equal ____ (funcall reader-1))
(funcall writer-1 0)
(assert-equal ____ (funcall reader-1))
;; The two different function pairs refer to different places.
(assert-equal ____ (funcall reader-2))
(funcall writer-2 :zero)
(assert-equal ____ (funcall reader-2)))))
(define-test apply
;; The function APPLY applies a function to a list of arguments.
(let ((function (lambda (x y z) (+ x y z))))
(assert-equal ____ (apply function '(100 20 3))))
;; FUNCTION is a special operator that retrieves function objects, defined
;; both globally and locally. #'X is syntax sugar for (FUNCTION X).
(assert-equal ____ (apply (function +) '(1 2)))
(assert-equal ____ (apply #'- '(1 2)))
;; Only the last argument to APPLY must be a list.
(assert-equal ____ (apply #'+ 1 2 '(3)))
(assert-equal ____ (apply #'max 1 2 3 4 '())))
(define-test funcall
;; The function FUNCALL calls a function with arguments, not expecting a final
;; list of arguments.
(let ((function (lambda (x y z) (+ x y z))))
(assert-equal ____ (funcall function 300 20 1)))
(assert-equal ____ (funcall (function +) 1 2))
(assert-equal ____ (funcall #'- 1 2))
(assert-equal ____ (funcall #'+ 1 2 3))
(assert-equal ____ (funcall #'max 1 2 3 4)))

View File

@ -0,0 +1,108 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; A hash table data structure is sometimes known as a dictionary.
(define-test make-hash-table
(let ((my-hash-table (make-hash-table)))
(true-or-false? ____ (typep my-hash-table 'hash-table))
(true-or-false? ____ (hash-table-p my-hash-table))
(true-or-false? ____ (hash-table-p (make-array '(3 3 3))))
;; The function HASH-TABLE-COUNT returns the number of entries currently
;; contained in a hash table.
(assert-equal ____ (hash-table-count my-hash-table))))
(define-test gethash
;; The function GETHASH can be used to access hash table values.
(let ((cube-roots (make-hash-table)))
;; We add the key-value pair 1 - "uno" to the hash table.
(setf (gethash 1 cube-roots) "uno")
(assert-equal ____ (gethash 1 cube-roots))
(assert-equal ____ (hash-table-count cube-roots))
(setf (gethash 8 cube-roots) 2)
(setf (gethash -3 cube-roots) -27)
(assert-equal ____ (gethash -3 cube-roots))
(assert-equal ____ (hash-table-count cube-roots))
;; GETHASH returns a secondary value that is true if the key was found in
;; the hash-table and false otherwise.
(multiple-value-bind (value foundp) (gethash 8 cube-roots)
(assert-equal ____ value)
(assert-equal ____ foundp))
(multiple-value-bind (value foundp) (gethash 125 cube-roots)
(assert-equal ____ value)
(assert-equal ____ foundp))))
(define-test hash-table-test
;; A hash table can be constructed with different test predicates.
;; The programmer may choose between EQ, EQL, EQUAL, and EQUALP to get the
;; best performance and expected results from the hash table.
;; The default test predicate is EQL.
(let ((eq-table (make-hash-table :test #'eq))
(eql-table (make-hash-table))
(equal-table (make-hash-table :test #'equal))
(equalp-table (make-hash-table :test #'equalp)))
;; We will define four variables whose values are strings.
(let* ((string "one")
(same-string string)
(string-copy (copy-seq string))
(string-upcased "ONE"))
;; We will insert the value of each variable into each hash table.
(dolist (thing (list string same-string string-copy string-upcased))
(dolist (hash-table (list eq-table eql-table equal-table equalp-table))
(setf (gethash thing hash-table) t))))
;; How many entries does each hash table contain?
(assert-equal ____ (hash-table-count eq-table))
(assert-equal ____ (hash-table-count eql-table))
(assert-equal ____ (hash-table-count equal-table))
(assert-equal ____ (hash-table-count equalp-table))))
(define-test hash-table-equality
;; EQUALP considers two hash tables to be equal if they have the same test and
;; if its key-value pairs are the same under that test.
(let ((hash-table-1 (make-hash-table :test #'equal))
(hash-table-2 (make-hash-table :test #'equal)))
(setf (gethash "one" hash-table-1) "yat")
(setf (gethash "one" hash-table-2) "yat")
(setf (gethash "two" hash-table-1) "yi")
(setf (gethash "two" hash-table-2) "yi")
(true-or-false? ____ (eq hash-table-1 hash-table-2))
(true-or-false? ____ (equal hash-table-1 hash-table-2))
(true-or-false? ____ (equalp hash-table-1 hash-table-2))))
(define-test i-will-make-it-equalp
;; Disabled on ECL due to a conformance bug.
;; See https://gitlab.com/embeddable-common-lisp/ecl/-/issues/587
#-ecl
(let ((hash-table-1 (make-hash-table :test #'equal))
(hash-table-2 (make-hash-table :test #'equal)))
(setf (gethash "one" hash-table-1) "uno"
(gethash "two" hash-table-1) "dos")
(setf (gethash "one" hash-table-2) "eins"
(gethash "two" hash-table-2) "zwei")
(assert-false (equalp hash-table-1 hash-table-2))
;; Change the first hash table to be EQUALP to the second one.
(setf (gethash ____ hash-table-1) ____
(gethash ____ hash-table-1) ____)
(assert-true (equalp hash-table-1 hash-table-2))))
(define-test make-your-own-hash-table
;; Make your own hash table that satisfies the test.
(let ((colors ____))
;; You will need to modify your hash table after you create it.
____
(assert-equal (hash-table-count colors) 4)
(let ((values (list (gethash "blue" colors)
(gethash "green" colors)
(gethash "red" colors))))
(assert-equal values '((0 0 1) (0 1 0) (1 0 0))))))

View File

@ -0,0 +1,75 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; Lisp has multiple options for iteration.
;;; This set of koans will introduce some of the most common ones.
(define-test dolist
(let ((numbers '(4 8 15 16 23 42)))
;; The macro DOLIST binds a variable to subsequent elements of a list.
(let ((sum 0))
(dolist (number numbers)
;; (INCF PLACE N) is equivalent to (SETF PLACE (+ N PLACE)).
(incf sum number))
(assert-equal ____ sum))
;; DOLIST can optionally return a value.
(let ((sum 0))
(assert-equal ____ (dolist (number numbers sum)
(incf sum number))))))
(define-test dotimes
;; The macro DOTIMES binds a variable to subsequent integers from 0 to
;; (1- COUNT).
(let ((stack '()))
(dotimes (i 5)
(push i stack))
(assert-equal ____ stack))
;; DOTIMES can optionally return a value.
(let ((stack '()))
(assert-equal ____ (dotimes (i 5 stack)
(push i stack)))))
(define-test do
;; The macro DO accepts a list of variable bindings, a termination test with
;; epilogue forms, and Lisp code that should be executed on each iteration.
(let ((result '()))
(do ((i 0 (1+ i)))
((> i 5))
(push i result))
(assert-equal ____ result))
;; The epilogue of DO can return a value.
(let ((result (do ((i 0 (1+ i))
;; A variable bound by DO noes not need to be updated on
;; each iteration.
(result '()))
((> i 5) (nreverse result))
(push i result))))
(assert-equal ____ result)))
(define-test loop-basic-form
;; The macro LOOP in its simple form loops forever. It is possible to stop the
;; looping by calling the RETURN special form.
(let ((counter 0))
(loop (incf counter)
(when (>= counter 100)
(return counter)))
(assert-equal ____ counter))
;; The RETURN special form can return a value out of a LOOP.
(let ((counter 0))
(assert-equal ____ (loop (incf counter)
(when (>= counter 100)
(return counter)))))
;; The extended form of LOOP will be contemplated in a future koan.
)

62
lisp-koans/koans/let.lisp Normal file
View File

@ -0,0 +1,62 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(define-test let
;; The LET form establishes a lexical extent within which new variables are
;; created: a symbol that names a variable becomes bound to a value.
(let ((x 10)
(y 20))
(assert-equal ____ (+ x y))
;; It is possible to shadow previously visible bindings.
(let ((y 30))
(assert-equal ____ (+ x y)))
(assert-equal ____ (+ x y)))
;; Variables bound by LET have a default value of NIL.
(let (x)
(assert-equal ____ x)))
(define-test let-versus-let*
;; LET* is similar to LET, except the bindings are established sequentially,
;; and a binding may use bindings that were established before it.
(let ((x 10)
(y 20))
(let ((x (+ y 100))
(y (+ x 100)))
(assert-equal ____ x)
(assert-equal ____ y))
(let* ((x (+ y 100))
(y (+ x 100)))
;; Which X is used to compute the value of Y?
(assert-equal ____ x)
(assert-equal ____ y))))
(define-test let-it-be-equal
;; Fill in the LET and LET* to get the tests to pass.
(let ((a 1)
(b :two)
(c "Three"))
(let ((____ ____)
(____ ____)
(____ ____))
(assert-equal a 100)
(assert-equal b 200)
(assert-equal c "Jellyfish"))
(let* ((____ ____)
(____ ____)
;; In this third binding, you are allowed to use the variables bound
;; by the previous two LET* bindings.
(____ ____))
(assert-equal a 121)
(assert-equal b 200)
(assert-equal c (+ a (/ b a))))))

146
lisp-koans/koans/lists.lisp Normal file
View File

@ -0,0 +1,146 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; A singly linked list is the basic build block of Lisp. Each node of such a
;;; list is called a "cons cell" in Lisp. Each cons cell has two slots: a CAR,
;;; often used to hold an element of a list, and a CDR, often used to reference
;;; the next cons cell.
(define-test how-to-make-lists
(let (;; Literal lists can be passed by quoting them.
(fruits '(orange pomello clementine))
;; Freshly constructed lists can be passed using the LIST function.
(some-evens (list (* 2 1) (* 2 2) (* 2 3)))
;; Lists can also be passed using quotes and dot notation...
(long-numbers '(16487302 . (3826700034 . (10000000 . nil))))
;; ...or by using the function CONS.
(names (cons "Matthew" (cons "Mark" (cons "Margaret" '())))))
;; Try filling in the below blanks in different ways.
(assert-equal ____ fruits)
(assert-equal ____ some-evens)
(assert-equal ____ long-numbers)
(assert-equal ____ names)))
(define-test cons-tructing-lists
;; The function CONS can be used to add new elements at the beginning of
;; an existing list.
(let ((nums '()))
(setf nums (cons :one nums))
(assert-equal ____ nums)
(setf nums (cons :two nums))
(assert-equal ____ nums)
;; Lists can contain anything, even objects of different types.
(setf nums (cons 333 nums))
(assert-equal ____ nums)
;; Lists can contain other lists, too.
(setf nums (cons (list "some" "strings") nums))
(assert-equal ____ nums)))
(define-test car-and-cdr
;; We may use functions CAR and CDR (or, alternatively, FIRST and REST) to
;; access the two slots of a cons cell.
(let ((x (cons 1 2)))
(assert-equal ____ (car x))
(assert-equal ____ (cdr x)))
;; Calls to CAR and CDR are often intertwined to extract data from a nested
;; cons structure.
(let ((structure '((1 2) (("foo" . "bar")))))
(assert-equal ____ (car structure))
(assert-equal ____ (car (cdr structure)))
(assert-equal ____ (cdr (car (car (cdr structure)))))
;; Lisp defines shorthand functions for up to four such nested calls.
(assert-equal ____ (car structure))
(assert-equal ____ (cadr structure))
(assert-equal ____ (cdaadr structure))))
(define-test push-pop
;; PUSH and POP are macros similar to SETF, as both of them operate on places.
(let ((place '(10 20 30 40)))
;; PUSH sets the value of the place to a new cons cell containing some value
;; in its CAR.
(push 0 place)
(assert-equal ____ place)
;; POP removes a single cons cell from a place, sets the place to its CDR,
;; and returns the value from its CAR.
(let ((value (pop place)))
(assert-equal ____ value)
(assert-equal ____ place))
;; The return value of POP can be discarded to simply "remove" a single cons
;; cell from a place.
(pop place)
(let ((value (pop place)))
(assert-equal ____ value)
(assert-equal ____ place))))
(define-test append-nconc
;; The functions APPEND and NCONC appends one list to the end of another.
;; While APPEND creates new lists, NCONC modifies existing ones; therefore
;; APPEND can be used on literals, but NCONC needs fresh lists.
(assert-equal ____ (append '(:a :b) '(:c)))
(assert-equal ____ (nconc (list :a :b) (list :c)))
(let ((list-1 (list 1 2 3))
(list-2 (list 4 5 6)))
;; Both APPEND and NCONC return the appended list, but the interesting part
;; is what happens when we try to use the original variables passed to them.
(assert-equal ____ (append list-1 list-2))
(assert-equal ____ list-1)
(assert-equal ____ list-2)
(assert-equal ____ (nconc list-1 list-2))
(assert-equal ____ list-1)
(assert-equal ____ list-2)))
(define-test accessing-list-elements
(let ((noms '("peanut" "butter" "and" "jelly")))
;; Common Lisp defines accessor functions for lists: FIRST, SECOND, ...,
;; up to TENTH.
(assert-equal "peanut" (first noms))
(assert-equal ____ (second noms))
(assert-equal ____ (fourth noms))
;; The function LAST returns the last cons cell of a list.
(assert-equal ____ (last noms))
;; The function NTH returns the n-th element of a list.
(assert-equal "butter" (nth 1 noms))
(assert-equal ____ (nth 0 noms))
(assert-equal ____ (nth 3 noms))))
(define-test cons-tructing-improper-lists
;; A proper list is a list whose final CDR ends with NIL.
;; An improper list either has a non-NIL value in its final CDR or does not
;; have a final CDR due to a cycle in its structure.
(let (;; We can construct non-cyclic improper lists using LIST*...
(x (list* 1 2 3 4 5))
;; ...or pass them as literals via dot notation.
(y '(6 7 8 9 . 0)))
(assert-equal ____ (last x))
(assert-equal ____ (last y)))
;; We can create a cyclic list by changing the last CDR of a list to refer to
;; another cons cell
(let ((list (list 1 2 3 4 5))
(cyclic-list (list 1 2 3 4 5)))
(setf (cdr (last cyclic-list)) cyclic-list)
;; Function LIST-LENGTH returns NIL if a list is cyclic.
(assert-equal ____ (list-length list))
(assert-equal ____ (list-length cyclic-list))
;; Many Lisp functions operate only on proper lists.
;; The function NTH is not one of them; it can be used to retrieve elements
;; of cyclic lists.
(assert-equal ____ (nth 101 cyclic-list))))
(define-test slicing-lists
;; The function SUBSEQ returns a subsequence of a list.
(let ((noms (list "peanut" "butter" "and" "jelly")))
(assert-equal ____ (subseq noms 0 1))
(assert-equal ____ (subseq noms 0 2))
(assert-equal ____ (subseq noms 2 2))
(assert-equal ____ (subseq noms 2))))

140
lisp-koans/koans/loops.lisp Normal file
View File

@ -0,0 +1,140 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; The extended for of LOOP allows for advanced iteration.
;;; See http://www.gigamonkeys.com/book/loop-for-black-belts.html
(define-test loop-collect
;; LOOP can collect the results in various ways.
(let* ((result-1 (loop for letter in '(#\a #\b #\c #\d) collect letter))
(result-2 (loop for number in '(1 2 3 4 5) sum number))
(result-3 (loop for list in '((foo) (bar) (baz)) append list)))
(assert-equal ____ result-1)
(assert-equal ____ result-2)
(assert-equal ____ result-3)))
(define-test loop-multiple-variables
;; With multiple FOR clauses, the loop ends when any of the provided lists are
;; exhausted.
(let* ((letters '(:a :b :c :d))
(result (loop for letter in letters
for i from 1 to 1000
collect (list i letter))))
(assert-equal ____ result)))
(define-test loop-in-versus-loop-on
;; Instead of iterating over each element of a list, we can iterate over each
;; cons cell of a list.
(let* ((letters '(:a :b :c))
(result-in (loop for thing in letters collect thing))
(result-on (loop for thing on letters collect thing)))
(assert-equal ____ result-in)
(assert-equal ____ result-on)))
(define-test loop-for-by
;; Numeric iteration can go faster or slower if we use the BY keyword.
(let* ((result (loop for i from 0 to 30 by 5 collect i)))
(assert-equal ____ result)))
(define-test loop-counting-backwards
;; We can count downwards instead of upwards by using DOWNTO instead of TO.
(let ((result (loop for i from 5 downto -5 collect i)))
(assert-equal ____ result)))
(define-test loop-list-by
;; List iteration can go faster or slower if we use the BY keyword.
(let* ((letters '(:a :b :c :d :e :f))
(result (loop for letter in letters collect letter))
(result-cdr (loop for letter in letters by #'cdr collect letter))
(result-cddr (loop for letter in letters by #'cddr collect letter))
(result-cdddr (loop for letter in letters by #'cdddr collect letter)))
(assert-equal ____ result)
(assert-equal ____ result-cdr)
(assert-equal ____ result-cddr)
(assert-equal ____ result-cdddr)))
(define-test loop-across
;; LOOP can iterate over a vector with the ACROSS keyword.
(let* ((vector (make-array '(5) :initial-contents '(0 1 2 3 4)))
(result (loop for number across vector collect number)))
(assert-equal ____ result)))
(define-test loop-over-2d-array
(let ((array (make-array '(3 2) :initial-contents '((0 1) (2 3) (4 5)))))
;; LOOP can be combined with ROW-MAJOR-AREF to iterate over the contents of
;; a multidimensional array.
(let* ((result (loop for i from 0 below (array-total-size array)
collect (row-major-aref array i))))
(assert-equal ____ result))
;; It is always possible to resort to nested loops.
(let* ((result (loop with max-i = (array-dimension array 0)
for i from 0 below max-i
collect (loop with max-j = (array-dimension array 1)
for j from 0 below max-j
collect (expt (aref array i j) 2)))))
(assert-equal ____ result))))
(define-test loop-hash-table
(let ((book-heroes (make-hash-table :test 'equal)))
(setf (gethash "The Hobbit" book-heroes) "Bilbo"
(gethash "Where The Wild Things Are" book-heroes) "Max"
(gethash "The Wizard Of Oz" book-heroes) "Dorothy"
(gethash "The Great Gatsby" book-heroes) "James Gatz")
;; LOOP can iterate over hash tables.
(let ((pairs-in-table (loop for key being the hash-keys of book-heroes
using (hash-value value)
collect (list key value))))
(assert-equal ____ (length pairs-in-table))
(true-or-false? ____ (find '("The Hobbit" "Bilbo") pairs-in-table
:test #'equal)))))
(define-test loop-statistics
;; LOOP can perform basics statistics on the collected elements.
(let ((result (loop for x in '(1 2 4 8 16 32)
collect x into collected
count x into counted
sum x into summed
maximize x into maximized
minimize x into minimized
finally (return (list collected counted summed
maximized minimized)))))
(destructuring-bind (collected counted summed maximized minimized) result
(assert-equal ____ collected)
(assert-equal ____ counted)
(assert-equal ____ summed)
(assert-equal ____ maximized)
(assert-equal ____ minimized))))
(define-test loop-destructuring
;; LOOP can bind multiple variables on each iteration step.
(let* ((count 0)
(result (loop for (a b) in '((1 9) (2 8) (3 7) (4 6))
do (incf count)
collect (+ a b))))
(assert-equal ____ count)
(assert-equal ____ result)))
(define-test loop-conditional-execution
(let ((numbers '(1 1 2 3 5 8 13 21)))
;; LOOP can execute some actions conditionally.
(let ((result (loop for x in numbers
when (evenp x) sum x)))
(assert-equal ____ result))
(let ((result (loop for x in numbers
unless (evenp x) sum x)))
(assert-equal ____ result))
(flet ((greater-than-10-p (x) (> x 10)))
(let ((result (loop for x in numbers
when (greater-than-10-p x) sum x)))
(assert-equal ____ result)))))

View File

@ -0,0 +1,116 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; A Lisp macro is a function that accepts Lisp data and produces a Lisp form.
;;; When the macro is called, its macro function receives unevaluated arguments
;;; and may use them to produce a new Lisp form. This form is then spliced in
;;; place of the original macro call and is then evaluated.
(defmacro my-and (&rest forms)
;; We use a LABELS local function to allow for recursive expansion.
(labels ((generate (forms)
(cond ((null forms) 'nil)
((null (rest forms)) (first forms))
(t `(when ,(first forms)
,(generate (rest forms)))))))
(generate forms)))
(define-test my-and
;; ASSERT-EXPANDS macroexpands the first form once and checks if it is equal
;; to the second form.
(assert-expands (my-and (= 0 (random 6)) (error "Bang!"))
'(when (= 0 (random 6)) (error "Bang!")))
(assert-expands (my-and (= 0 (random 6))
(= 0 (random 6))
(= 0 (random 6))
(error "Bang!"))
____))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; A common macro pitfall is capturing a variable defined by the user.
(define-test variable-capture
(macrolet ((for ((var start stop) &body body)
`(do ((,var ,start (1+ ,var))
(limit ,stop))
((> ,var limit))
,@body)))
(let ((limit 10)
(result '()))
(for (i 0 3)
(push i result)
(assert-equal ____ limit))
(assert-equal ____ (nreverse result)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Another pitfall is evaluating some forms multiple times where they are only
;;; meant to be evaluated once.
(define-test multiple-evaluation
;; We use MACROLET for defining a local macro.
(macrolet ((for ((var start stop) &body body)
`(do ((,var ,start (1+ ,var)))
((> ,var ,stop))
,@body)))
(let ((side-effects '())
(result '()))
;; Our functions RETURN-0 and RETURN-3 have side effects.
(flet ((return-0 () (push 0 side-effects) 0)
(return-3 () (push 3 side-effects) 3))
(for (i (return-0) (return-3))
(push i result)))
(assert-equal ____ (nreverse result))
(assert-equal ____ (nreverse side-effects)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Yet another pitfall is not respecting the evaluation order of the macro
;;; subforms.
(define-test wrong-evaluation-order
(macrolet ((for ((var start stop) &body body)
;; The function GENSYM creates GENerated SYMbols, guaranteed to
;; be unique in the whole Lisp system. Because of that, they
;; cannot capture other symbols, preventing variable capture.
(let ((limit (gensym "LIMIT")))
`(do ((,limit ,stop)
(,var ,start (1+ ,var)))
((> ,var ,limit))
,@body))))
(let ((side-effects '())
(result '()))
(flet ((return-0 () (push 0 side-effects) 0)
(return-3 () (push 3 side-effects) 3))
(for (i (return-0) (return-3))
(push i result)))
(assert-equal ____ (nreverse result))
(assert-equal ____ (nreverse side-effects)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-test for
(macrolet ((for ((var start stop) &body body)
;; Fill in the blank with a correct FOR macroexpansion that is
;; not affected by the three macro pitfalls mentioned above.
____))
(let ((side-effects '())
(result '()))
(flet ((return-0 () (push 0 side-effects) 0)
(return-3 () (push 3 side-effects) 3))
(for (i (return-0) (return-3))
(push i result)))
(assert-equal '(0 1 2 3) (nreverse result))
(assert-equal '(0 3) (nreverse side-effects)))))

View File

@ -0,0 +1,97 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; Lisp supports several functional alternatives to imperative iteration.
(define-test mapcar
(let ((numbers '(1 2 3 4 5 6)))
;; Inside MAPCAR, he function 1+ will be applied to each element of NUMBERS.
;; A new list will be collected from the results.
(assert-equal '(2 3 4 5 6 7) (mapcar #'1+ numbers))
(assert-equal ____ (mapcar #'- numbers))
(assert-equal ____ (mapcar #'list numbers))
(assert-equal ____ (mapcar #'evenp numbers))
(assert-equal ____ (mapcar #'numberp numbers))
(assert-equal ____ (mapcar #'stringp numbers))
;; MAPCAR can work on multiple lists. The function will receive one argument
;; from each list.
(let ((other-numbers '(4 8 15 16 23 42)))
(assert-equal ____ (mapcar #'+ numbers other-numbers))
(assert-equal ____ (mapcar #'* numbers other-numbers))
;; The function MOD performs modulo division.
(assert-equal ____ (mapcar #'mod other-numbers numbers)))))
(define-test mapcar-lambda
;; MAPCAR is often used with anonymous functions.
(let ((numbers '(8 21 152 37 403 14 7 -34)))
(assert-equal ____ (mapcar (lambda (x) (mod x 10)) numbers)))
(let ((strings '("Mary had a little lamb"
"Old McDonald had a farm"
"Happy birthday to you")))
(assert-equal ____ (mapcar (lambda (x) (subseq x 4 12)) strings))))
(define-test map
;; MAP is a variant of MAPCAR that works on any sequences.
;; It allows to specify the type of the resulting sequence.
(let ((string "lorem ipsum"))
(assert-equal ____ (map 'string #'char-upcase string))
(assert-equal ____ (map 'list #'char-upcase string))
;; Not all vectors containing characters are strings.
(assert-equalp ____ (map '(vector t) #'char-upcase string))))
(define-test transposition
;; MAPCAR gives the function as many arguments as there are lists.
(flet ((transpose (lists) (apply #'mapcar ____ lists)))
(let ((list '((1 2 3)
(4 5 6)
(7 8 9)))
(transposed-list '((1 4 7)
(2 5 8)
(3 6 9))))
(assert-equal transposed-list (transpose list))
(assert-equal ____ (transpose (transpose list))))
(assert-equal ____ (transpose '(("these" "making")
("pretzels" "me")
("are" "thirsty"))))))
(define-test reduce
;; The function REDUCE combines the elements of a list by applying a binary
;; function to the elements of a sequence from left to right.
(assert-equal 15 (reduce #'+ '(1 2 3 4 5)))
(assert-equal ____ (reduce #'+ '(1 2 3 4)))
(assert-equal ____ (reduce #'expt '(1 2 3 4 5))))
(define-test reduce-from-end
;; The :FROM-END keyword argument can be used to reduce from right to left.
(let ((numbers '(1 2 3 4 5)))
(assert-equal ____ (reduce #'cons numbers))
(assert-equal ____ (reduce #'cons numbers :from-end t)))
(let ((numbers '(2 3 2)))
(assert-equal ____ (reduce #'expt numbers))
(assert-equal ____ (reduce #'expt numbers :from-end t))))
(define-test reduce-initial-value
;; :INITIAL-VALUE can supply the initial value for the reduction.
(let ((numbers '(1 2 3 4 5)))
(assert-equal ____ (reduce #'* numbers))
(assert-equal ____ (reduce #'* numbers :initial-value 0))
(assert-equal ____ (reduce #'* numbers :initial-value -1))))
(define-test inner-product
;; MAPCAR and REDUCE are powerful when used together.
;; Fill in the blanks to produce a local function that computes an inner
;; product of two vectors.
(flet ((inner-product (x y) (reduce ____ (mapcar ____ x y))))
(assert-equal 32 (inner-product '(1 2 3) '(4 5 6)))
(assert-equal 310 (inner-product '(10 20 30) '(4 3 7)))))

View File

@ -0,0 +1,41 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; In Lisp, it is possible for a function to return more than one value.
;;; This is distinct from returning a list or structure of values.
(define-test multiple-values
(let ((x (floor 3/2))
;; The macro MULTIPLE-VALUE-LIST returns a list of all values returned
;; by a Lisp form.
(y (multiple-value-list (floor 3/2))))
(assert-equal x 1)
(assert-equal y '(1 1/2)))
(assert-equal ____ (multiple-value-list (floor 99/4))))
(defun next-fib (a b)
;; The function VALUES allows returning multiple values.
(values b (+ a b)))
(define-test binding-and-setting-multiple-values
;; The macro MULTIPLE-VALUE-BIND is like LET, except it binds the variables
;; listed in its first argument to the values returned by the form that is its
;; second argument.
(multiple-value-bind (x y) (next-fib 3 5)
(let ((result (* x y)))
(assert-equal ____ result)))
;; SETF can also set multiple values if a VALUES form is provided as a place.
(let (x y)
(setf (values x y) (next-fib 5 8))
(assert-equal ____ (list x y))))

View File

@ -0,0 +1,52 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(define-test t-and-nil-are-opposites
;; NOT is a function which returns the boolean opposite of its argument.
(true-or-false? ____ (not nil))
(true-or-false? ____ (not t)))
(define-test nil-and-empty-list-are-the-same-thing
;; In Common Lisp, NIL is also the empty list.
(true-or-false? ____ '())
(true-or-false? ____ (not '())))
(define-test in-lisp-many-things-are-true
;; In Common Lisp, the canonical values for truth is T.
;; However, everything that is non-NIL is true, too.
(true-or-false? ____ 5)
(true-or-false? ____ (not 5))
(true-or-false? ____ "a string")
;; Even an empty string...
(true-or-false? ____ "")
;; ...or a list containing a NIL...
(true-or-false? ____ (list nil))
;; ...or an array with no elements...
(true-or-false? ____ (make-array 0))
;; ...or the number zero.
(true-or-false? ____ 0))
(define-test and
;; The logical operator AND can take multiple arguments.
(true-or-false? ____ (and t t t t t))
(true-or-false? ____ (and t t nil t t))
;; If all values passed to AND are true, it returns the last value.
(assert-equal ____ (and t t t t t 5)))
(define-test or
;; The logical operator OR can also take multiple arguments.
(true-or-false? ____ (or nil nil nil t nil))
;; OR returns the first non-NIL value it encounters, or NIL if there are none.
(assert-equal ____ (or nil nil nil))
(assert-equal ____ (or 1 2 3 4 5)))

View File

@ -0,0 +1,48 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(define-test shadowing
(assert-equal ____ (let ((z 4)) (list z (let ((z 2)) z)))))
(defun block-1 ()
(block here
(return-from here 4)
5))
(defun block-2 ()
(block outer
(block inner
(return-from outer 'space)
(return-from inner 'tube))
(return-from outer 'valve)))
(define-test block-return-from
(assert-equal ____ (block-1))
(assert-equal ____ (block-2)))
;;; See http://www.gigamonkeys.com/book/variables.html
(define-test lexical-variables-can-be-enclosed
(assert-equal ____ (let ((f (let ((x 10))
(lambda () x))))
(let ((x 20))
(funcall f)))))
(define-test dynamic-variables-are-affected-by-execution-path
(assert-equal ____ (let ((f (let ((x 10))
(declare (special x))
(lambda () x))))
(let ((x 20))
(declare (special x))
(funcall f)))))

View File

@ -0,0 +1,82 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; Greed is a dice game played among 2 or more players, using 5
;;; six-sided dice.
;;;
;;; Each player takes a turn consisting of one or more rolls of the dice.
;;; On the first roll of the game, a player rolls all five dice which are
;;; scored according to the following:
;;;
;;; Three 1's => 1000 points
;;; Three 6's => 600 points
;;; Three 5's => 500 points
;;; Three 4's => 400 points
;;; Three 3's => 300 points
;;; Three 2's => 200 points
;;; One 1 => 100 points
;;; One 5 => 50 points
;;;
;;; A single die can only be counted once in each roll. For example,
;;; a "5" can only count as part of a triplet (contributing to the 500
;;; points) or as a single 50 points, but not both in the same roll.
;;;
;;; Example Scoring
;;;
;;; Throw Score
;;; --------- ------------------
;;; 5 1 3 4 1 50 + 2 * 100 = 250
;;; 1 1 1 3 1 1000 + 100 = 1100
;;; 2 4 4 5 4 400 + 50 = 450
;;;
;;; The dice not contributing to the score are called the non-scoring
;;; dice. "3" and "4" are non-scoring dice in the first example. "3" is
;;; a non-scoring die in the second, and "2" is a non-score die in the
;;; final example.
;;;
;;; More scoring examples are given in the tests below.
;;;
;;; Your goal is to write the scoring function for Greed.
(defun score (&rest dice)
____)
(define-test score-of-an-empty-list-is-zero
(assert-equal 0 (score)))
(define-test score-of-a-single-roll-of-5-is-50
(assert-equal 50 (score 5)))
(define-test score-of-a-single-roll-of-1-is-100
(assert-equal 100 (score 1)))
(define-test score-of-multiple-1s-and-5s-is-the-sum-of-individual-scores
(assert-equal 300 (score 1 5 5 1)))
(define-test score-of-single-2s-3s-4s-and-6s-are-zero
(assert-equal 0 (score 2 3 4 6)))
(define-test score-of-a-triple-1-is-1000
(assert-equal 1000 (score 1 1 1)))
(define-test score-of-other-triples-is-100x
(assert-equal 200 (score 2 2 2))
(assert-equal 300 (score 3 3 3))
(assert-equal 400 (score 4 4 4))
(assert-equal 500 (score 5 5 5))
(assert-equal 600 (score 6 6 6)))
(define-test score-of-mixed-is-sum
(assert-equal 250 (score 2 5 2 2 3))
(assert-equal 550 (score 5 5 5 5)))

View File

@ -0,0 +1,220 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(defclass access-counter ()
((value :accessor value :initarg :value)
(access-count :reader access-count :initform 0)))
;;; The generated reader, writer, and accessor functions are generic functions.
;;; The methods of a generic function are combined using a method combination;
;;; by default, the standard method combination is used.
;;; This allows us to define :BEFORE and :AFTER methods whose code is executed
;;; before or after the primary method, and whose return values are discarded.
;;; The :BEFORE and :AFTER keywords used in this context are called qualifiers.
(defmethod value :after ((object access-counter))
(incf (slot-value object 'access-count)))
(defmethod (setf value) :after (new-value (object access-counter))
(incf (slot-value object 'access-count)))
(define-test defmethod-after
(let ((counter (make-instance 'access-counter :value 42)))
(assert-equal ____ (access-count counter))
(assert-equal ____ (value counter))
(assert-equal ____ (access-count counter))
(setf (value counter) 24)
(assert-equal ____ (access-count counter))
(assert-equal ____ (value counter))
(assert-equal ____ (access-count counter))
;; We read the value three more times and discard the result.
(value counter)
(value counter)
(value counter)
(assert-equal ____ (access-count counter))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; In addition to :BEFORE and :AFTER methods is also possible to write :AROUND
;;; methods, which execute instead of the primary methods. In such context, it
;;; is possible to call the primary method via CALL-NEXT-METHOD.
;;; In the standard method combination, the :AROUND method, if one exists, is
;;; executed first, and it may choose whether and how to call next methods.
(defgeneric grab-lollipop ()
(:method () :lollipop))
(defgeneric grab-lollipop-while-mom-is-nearby (was-nice-p)
(:method :around (was-nice-p) (if was-nice-p (call-next-method) :no-lollipop))
(:method (was-nice-p) (declare (ignore was-nice-p)) :lollipop))
(define-test lollipop
(assert-equal ____ (grab-lollipop))
(assert-equal ____ (grab-lollipop-while-mom-is-nearby t))
(assert-equal ____ (grab-lollipop-while-mom-is-nearby nil)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defclass countdown ()
;; The countdown object represents an ongoing countdown. Each time the
;; REMAINING-TIME function is called, it should return a number one less than
;; the previous time that it returned. If the countdown hits zero, :BANG
;; should be returned instead.
((remaining-time :reader remaining-time :initarg :time)))
(defmethod remaining-time :around ((object countdown))
(let ((time (call-next-method)))
(if (< 0 time)
;; DECF is similar to INCF. It decreases the value stored in the place
;; and returns the decreased value.
(decf (slot-value object 'remaining-time))
:bang)))
(define-test countdown
(let ((countdown (make-instance 'countdown :time 4)))
(assert-equal 3 (remaining-time countdown))
(assert-equal ____ (remaining-time countdown))
(assert-equal ____ (remaining-time countdown))
(assert-equal ____ (remaining-time countdown))
(assert-equal ____ (remaining-time countdown))
(assert-equal ____ (remaining-time countdown))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; It is possible for multiple :BEFORE, :AFTER, :AROUND, or primary methods to
;;; be executed in a single method call.
(defclass object ()
((counter :accessor counter :initform 0)))
(defclass bigger-object (object) ())
(defgeneric frobnicate (x)
(:method :around ((x bigger-object))
(incf (counter x) 8)
(call-next-method))
(:method :around ((x object))
(incf (counter x) 70)
(call-next-method))
(:method :before ((x bigger-object))
(incf (counter x) 600))
(:method :before ((x object))
(incf (counter x) 5000))
(:method ((x bigger-object))
(incf (counter x) 40000)
(call-next-method))
(:method ((x object))
(incf (counter x) 300000))
(:method :after ((x object))
(incf (counter x) 2000000))
(:method :after ((x bigger-object))
(incf (counter x) 10000000)))
(define-test multiple-methods
(let ((object (make-instance 'object)))
(frobnicate object)
(assert-equal ____ (counter object)))
(let ((object (make-instance 'bigger-object)))
(frobnicate object)
(assert-equal ____ (counter object))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; The method order of the standard combination is as follows:
;;; First, the most specific :AROUND method is executed.
;;; Second, all :BEFORE methods are executed, most specific first.
;;; Third, the most specific primary method is executed.
;;; Fourth, all :AFTER methods are executed, most specific last.
(defgeneric calculate (x)
(:method :around ((x bigger-object))
(setf (counter x) 40)
(call-next-method))
(:method :around ((x object))
(incf (counter x) 24)
(call-next-method))
(:method :before ((x bigger-object))
(setf (counter x) (mod (counter x) 6)))
(:method :before ((x object))
(setf (counter x) (/ (counter x) 4)))
(:method ((x bigger-object))
(setf (counter x) (* (counter x) (counter x)))
(call-next-method))
(:method ((x object))
(decf (counter x) 100))
(:method :after ((x object))
(setf (counter x) (/ 1 (counter x))))
(:method :after ((x bigger-object))
(incf (counter x) 2)))
(define-test standard-method-combination-order
(let ((object (make-instance 'object)))
(calculate object)
(assert-equal ____ (counter object)))
(let ((object (make-instance 'bigger-object)))
(calculate object)
(assert-equal ____ (counter object))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defclass programmer () ())
(defclass senior-programmer (programmer) ())
(defclass full-stack-programmer (programmer) ())
(defclass senior-full-stack-programmer (senior-programmer
full-stack-programmer)
())
;;; The :BEFORE, :AFTER, and :AROUND methods are only available in the standard
;;; method combination. It is possible to use other method combinations, such as
;;; +.
(defgeneric salary-at-company-a (programmer)
(:method-combination +)
(:method + ((programmer programmer)) 120000)
(:method + ((programmer senior-programmer)) 200000)
(:method + ((programmer full-stack-programmer)) 48000))
(define-test salary-at-company-a
(let ((programmer (make-instance 'programmer)))
(assert-equal ____ (salary-at-company-a programmer)))
(let ((programmer (make-instance 'senior-programmer)))
(assert-equal ____ (salary-at-company-a programmer)))
(let ((programmer (make-instance 'full-stack-programmer)))
(assert-equal ____ (salary-at-company-a programmer)))
(let ((programmer (make-instance 'senior-full-stack-programmer)))
(assert-equal ____ (salary-at-company-a programmer))))
;;; It is also possible to define custom method combinations.
(define-method-combination multiply :operator *)
(defgeneric salary-at-company-b (programmer)
(:method-combination multiply)
(:method multiply ((programmer programmer)) 120000)
(:method multiply ((programmer senior-programmer)) 2)
(:method multiply ((programmer full-stack-programmer)) 7/5))
(define-test salary-at-company-b
(let ((programmer (make-instance 'programmer)))
(assert-equal ____ (salary-at-company-b programmer)))
(let ((programmer (make-instance 'senior-programmer)))
(assert-equal ____ (salary-at-company-b programmer)))
(let ((programmer (make-instance 'full-stack-programmer)))
(assert-equal ____ (salary-at-company-b programmer)))
(let ((programmer (make-instance 'senior-full-stack-programmer)))
(assert-equal ____ (salary-at-company-b programmer))))

View File

@ -0,0 +1,73 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(define-test what-is-a-string
(let ((string "Do, or do not. There is no try."))
(true-or-false? ____ (typep string 'string))
;; Strings are vectors of characters.
(true-or-false? ____ (typep string 'array))
(true-or-false? ____ (typep string 'vector))
(true-or-false? ____ (typep string '(vector character)))
(true-or-false? ____ (typep string 'integer))))
(define-test multiline-string
;; A Lisp string can span multiple lines.
(let ((string "this is
a multi
line string"))
(true-or-false? ____ (typep string 'string))))
(define-test escapes-in-strings
;; Quotes and backslashes in Lisp strings must be escaped.
(let ((my-string "this string has one of these \" and a \\ in it"))
(true-or-false? ____ (typep my-string 'string))))
(define-test substrings
;; Since strings are sequences, it is possible to use SUBSEQ on them.
(let ((string "Lorem ipsum dolor sit amet"))
(assert-equal ____ (subseq string 12))
(assert-equal ____ (subseq string 6 11))
(assert-equal ____ (subseq string 1 5))))
(define-test strings-versus-characters
;; Strings and characters have distinct types.
(true-or-false? ____ (typep #\a 'character))
(true-or-false? ____ (typep "A" 'character))
(true-or-false? ____ (typep #\a 'string))
;; One can use both AREF and CHAR to refer to characters in a string.
(let ((my-string "Cookie Monster"))
(assert-equal ____ (char my-string 0))
(assert-equal ____ (char my-string 3))
(assert-equal ____ (aref my-string 7))))
(define-test concatenating-strings
;; Concatenating strings in Common Lisp is possible, if a little cumbersome.
(let ((a "Lorem")
(b "ipsum")
(c "dolor"))
(assert-equal ____ (concatenate 'string a " " b " " c))))
(define-test searching-for-characters
;; The function POSITION can be used to find the first position of an element
;; in a sequence. If the element is not found, NIL is returned.
(assert-equal ____ (position #\b "abc"))
(assert-equal ____ (position #\c "abc"))
(assert-equal ____ (position #\d "abc")))
(define-test finding-substrings
;; The function SEARCH can be used to search a sequence for subsequences.
(let ((title "A supposedly fun thing I'll never do again"))
(assert-equal ____ (search "supposedly" title))
(assert-equal 12 (search ____ title))))

View File

@ -0,0 +1,111 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; Lisp structures encapsulate data which belongs together. They are a template
;;; of sorts, providing a way to generate multiple instances of uniformly
;;; organized information
;;; Defining a structure also interns accessor functions to get and set the
;;; slots of that structure.
;;; The following form creates a new structure class named BASKETBALL-PLAYER
;;; with slots named NAME, TEAM, and NUMBER.
;;; This additionally creates functions MAKE-BASKETBALL-PLAYER,
;;; COPY-BASKETBALL-PLAYER, BASKETBALL-PLAYER-P, BASKETBALL-PLAYER-NAME,
;;; BASKETBALL-PLAYER-TEAM, and BASKETBALL-PLAYER-NUMBER.
(defstruct basketball-player
name team number)
(define-test make-struct
(let ((player (make-basketball-player :name "Larry" :team :celtics
:number 33)))
(true-or-false? ____ (basketball-player-p player))
(assert-equal ____ (basketball-player-name player))
(assert-equal ____ (basketball-player-team player))
(assert-equal ____ (basketball-player-number player))
(setf (basketball-player-team player) :retired)
(assert-equal ____ (basketball-player-team player))))
;;; Structure fields can have default values.
(defstruct baseball-player
name (team :red-sox) (position :outfield))
(define-test struct-defaults
(let ((player (make-baseball-player)))
;; We have not specified a default value for NAME, therefore we cannot
;; read it here - it would invoke undefined behaviour.
(assert-equal ____ (baseball-player-team player))
(assert-equal ____ (baseball-player-position player))))
;;; The accessor names can get pretty long. It's possible to specify a different
;;; prefix with the :CONC-NAME option.
(defstruct (american-football-player (:conc-name nfl-guy-))
name position team)
(define-test struct-access
(let ((player (make-american-football-player
:name "Drew Brees" :position :qb :team "Saints")))
(assert-equal ____ (nfl-guy-name player))
(assert-equal ____ (nfl-guy-team player))
(assert-equal ____ (nfl-guy-position player))))
;;; Structs can be defined to include other structure definitions.
;;; This form of inheritance allows composition of objects.
(defstruct (nba-contract (:include basketball-player))
salary start-year end-year)
(define-test structure-inheritance
(let ((contract (make-nba-contract :salary 136000000
:start-year 2004 :end-year 2011
:name "Kobe Bryant"
:team :lakers :number 24)))
(assert-equal ____ (nba-contract-start-year contract))
(assert-equal ____ (type-of contract))
;; Inherited structures follow the rules of type hierarchy.
(true-or-false? ____ (typep contract 'basketball-player))
;; One can access structure fields both with the structure's own accessors
;; and with the inherited accessors.
(assert-equal ____ (nba-contract-team contract))
(assert-equal ____ (basketball-player-team contract))))
;;; Copying a structure named FOO is handled with the COPY-FOO function.
;;; All such copies are shallow.
(define-test structure-equality-and-copying
(let ((manning-1 (make-american-football-player
:name "Manning" :team (list "Colts" "Broncos")))
(manning-2 (make-american-football-player
:name "Manning" :team (list "Colts" "Broncos"))))
;; MANNING-1 and MANNING-2 are different objects...
(true-or-false? ____ (eq manning-1 manning-2))
;;...but they contain the same information.
(true-or-false? ____ (equalp manning-1 manning-2))
(let ((manning-3 (copy-american-football-player manning-1)))
(true-or-false? ____ (eq manning-1 manning-3))
(true-or-false? ____ (equalp manning-1 manning-3))
;; Setting the slot of one instance does not modify the others...
(setf (nfl-guy-name manning-1) "Rogers")
(true-or-false? ____ (string= (nfl-guy-name manning-1)
(nfl-guy-name manning-3)))
(assert-equal ____ (nfl-guy-name manning-1))
(assert-equal ____ (nfl-guy-name manning-3))
;; ...but modifying shared structure may affect other instances.
(setf (car (nfl-guy-team manning-1)) "Giants")
(true-or-false? ____ (string= (car (nfl-guy-team manning-1))
(car (nfl-guy-team manning-3))))
(assert-equal ____ (car (nfl-guy-team manning-1)))
(assert-equal ____ (car (nfl-guy-team manning-3))))))

View File

@ -0,0 +1,161 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; This lesson group uses Quicklisp to load Bordeaux Threads, a portability
;;; library for working with threads. This is because threads are not a part of
;;; the Common Lisp standard and implementations do them differently.
;;; If you are using Quicklisp, please feel free to enable this lesson by
;;; following the instructions in the README.
;;; TODO: wait for Bordeaux Threads to implement a portable SEMAPHORE-COUNT
;;; and use it in the semaphore koans.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-test thread-return-value
;; When a thread object is constructed, it accepts a function to execute.
(let* ((thread (bt:make-thread (lambda () (+ 2 2))))
;; When the thread's function finishes, its return value becomes the
;; return value of BT:JOIN-THREAD.
(value (bt:join-thread thread)))
(assert-equal ____ value)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defvar *variable*)
(define-test thread-global-bindings
;; The global value of a variable is shared between all threads.
(setf *variable* 42)
(let ((thread (bt:make-thread (lambda ()
(when (= *variable* 42)
(setf *variable* 24)
t)))))
(assert-true (bt:join-thread thread))
(assert-equal ____ *variable*)))
(define-test thread-local-bindings
;; Newly established local bindings of a variable are visible only in the
;; thread that established these bindings.
(setf *variable* 42)
(let ((thread (bt:make-thread (lambda ()
(let ((*variable* 42))
(setf *variable* 24))))))
(bt:join-thread thread)
(assert-equal ____ *variable*)))
(define-test thread-initial-bindings
;; Initial dynamic bindings may be passed to the new thread.
(setf *variable* 42)
(let ((thread (bt:make-thread (lambda () (setf *variable* 24))
:initial-bindings '((*variable* . 42)))))
(bt:join-thread thread)
(assert-equal ____ *variable*)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-test thread-name
;; Threads can have names.
(let ((thread (bt:make-thread #'+ :name "Summing thread")))
(assert-equal ____ (bt:thread-name thread))
(assert-equal ____ (bt:join-thread thread))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-test thread-function-arguments
;; Passing arguments to thread functions requires closing over them.
(let* ((x 240)
(y 18)
(thread (bt:make-thread (lambda () (* x y)))))
(assert-equal ____ (bt:join-thread thread))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-test destroy-thread
;; Looping and renegade threads can usually be killed via BT:DESTROY-THREAD.
;; It is the last measure, since doing so might leave the Lisp system in an
;; unpredictable state if the thread was doing something complex.
(let ((thread (bt:make-thread (lambda () (loop (sleep 1))))))
(true-or-false? ____ (bt:thread-alive-p thread))
(bt:destroy-thread thread)
(true-or-false? ____ (bt:thread-alive-p thread))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defvar *another-variable*)
;; Preventing concurrent access to some data can be achieved via a lock in
;; order to avoid race conditions.
(defvar *lock* (bt:make-lock))
(define-test lock
(setf *another-variable* 0)
(flet ((increaser () (bt:with-lock-held (*lock*) (incf *another-variable*))))
(loop repeat 100
collect (bt:make-thread #'increaser) into threads
finally (loop until (notany #'bt:thread-alive-p threads))
(assert-equal ____ *another-variable*))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; We can further orchestrate threads by using semaphores.
(defvar *semaphore* (bt:make-semaphore))
(defun signal-our-semaphore ()
(bt:signal-semaphore semaphore))
(defun wait-on-our-semaphore ()
(bt:wait-on-semaphore semaphore :timeout 100))
(define-test semaphore
(assert-equal 1 (bt:join-thread (bt:make-thread #'signal-our-semaphore)))
(assert-equal ____ (bt:join-thread (bt:make-thread #'signal-our-semaphore)))
(assert-equal ____ (bt:join-thread (bt:make-thread #'signal-our-semaphore)))
(assert-equal 2 (bt:join-thread (bt:make-thread #'wait-on-our-semaphore)))
(assert-equal ____ (bt:join-thread (bt:make-thread #'wait-on-our-semaphore)))
(assert-equal ____ (bt:join-thread (bt:make-thread #'wait-on-our-semaphore)))
(assert-equal ____ (bt:join-thread (bt:make-thread #'wait-on-our-semaphore))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Semaphores can be used to manage resource allocation and to trigger some
;; threads to run when the semaphore value is above zero.
(defvar *foobar-semaphore* (bt:make-semaphore))
(defvar *foobar-list*)
(defun bar-pusher ()
(dotimes (i 10)
(sleep 0.01)
(push i (nth i *foobar-list*))
(push :bar (nth i *foobar-list*))
;; We push :BAR before :FOO, so the final list looks like (:FOO :BAR).
(bt:signal-semaphore *foobar-semaphore*)))
(defun foo-pusher ()
(dotimes (i 10)
(bt:wait-on-semaphore *foobar-semaphore*)
(push :foo (nth i *foobar-list*))))
(define-test list-of-foobars
(setf *foobar-list* (make-list 10))
(let ((bar-pusher (bt:make-thread #'bar-pusher))
(foo-pusher (bt:make-thread #'foo-pusher)))
(bt:join-thread foo-pusher))
(assert-equal ____ (nth 0 *foobar-list*))
(assert-equal ____ (nth 1 *foobar-list*))
(assert-equal ____ (nth 5 *foobar-list*)))

View File

@ -0,0 +1,65 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(define-condition triangle-error (error)
;; Fill in the blank with a suitable slot definition.
(____))
(defun triangle (a b c)
;; Fill in the blank with a function that satisfies the below tests.
____)
(define-test equilateral-triangles
;; Equilateral triangles have three sides of equal length,
(assert-equal :equilateral (triangle 2 2 2))
(assert-equal :equilateral (triangle 10 10 10)))
(define-test isosceles-triangles
;; Isosceles triangles have two sides of equal length,
(assert-equal :isosceles (triangle 3 4 4))
(assert-equal :isosceles (triangle 4 3 4))
(assert-equal :isosceles (triangle 4 4 3))
(assert-equal :isosceles (triangle 2 2 3))
(assert-equal :isosceles (triangle 10 10 2)))
(define-test scalene-triangles
;; Scalene triangles have three sides of different lengths.
(assert-equal :scalene (triangle 3 4 5))
(assert-equal :scalene (triangle 10 11 12))
(assert-equal :scalene (triangle 5 4 2)))
(define-test illegal-triangles
;; Not all triplets make valid triangles.
(flet ((triangle-failure (a b c)
(handler-case (progn (triangle a b c) (error "Test failure"))
(error (condition) condition))))
(let ((condition (triangle-failure 0 0 0)))
(assert-true (typep condition 'type-error))
(assert-equal 0 (type-error-datum condition))
;; The type (REAL (0)) represents all positive numbers.
(assert-true (subtypep (type-error-expected-type condition) '(real (0))))
;; If two type specifiers are SUBTYPEP of one another, then they represent
;; the same Lisp type.
(assert-true (subtypep '(real (0)) (type-error-expected-type condition))))
(let ((condition (triangle-failure 3 4 -5)))
(assert-true (typep condition 'type-error))
(assert-equal -5 (type-error-datum condition))
(assert-true (subtypep (type-error-expected-type condition) '(real (0))))
(assert-true (subtypep '(real (0)) (type-error-expected-type condition))))
(let ((condition (triangle-failure 1 1 3)))
(assert-true (typep condition 'triangle-error))
(assert-equal '(1 1 3) (triangle-error-sides condition)))
(let ((condition (triangle-failure 2 4 2)))
(assert-true (typep condition 'triangle-error))
(assert-equal '(2 4 2) (triangle-error-sides condition)))))

View File

@ -0,0 +1,153 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; There is a type hierarchy in Lisp, based on the set theory.
;;; An object may belong to multiple types at the same time.
;;; Every object is of type T. No object is of type NIL.
(define-test typep
;; TYPEP returns true if the provided object is of the provided type.
(true-or-false? ____ (typep "hello" 'string))
(true-or-false? ____ (typep "hello" 'array))
(true-or-false? ____ (typep "hello" 'list))
(true-or-false? ____ (typep "hello" '(simple-array character (5))))
(true-or-false? ____ (typep '(1 2 3) 'list))
(true-or-false? ____ (typep 99 'integer))
(true-or-false? ____ (typep nil 'NULL))
(true-or-false? ____ (typep 22/7 'ratio))
(true-or-false? ____ (typep 4.0 'float))
(true-or-false? ____ (typep #\a 'character))
(true-or-false? ____ (typep #'length 'function)))
(define-test type-of
;; TYPE-OF returns a type specifier for the object.
(assert-equal ____ (type-of '()))
(assert-equal ____ (type-of 4/6)))
(define-test overlapping-types
;; Because Lisp types are mathematical sets, they are allowed to overlap.
(let ((thing '()))
(true-or-false? ____ (typep thing 'list))
(true-or-false? ____ (typep thing 'atom))
(true-or-false? ____ (typep thing 'null))
(true-or-false? ____ (typep thing 't))))
(define-test fixnum-versus-bignum
;; In Lisp, integers are either fixnums or bignums. Fixnums are handled more
;; efficiently by the implementation, but some large integers can only be
;; represented as bignums.
;; Lisp converts between these two types on the fly. The constants
;; MOST-NEGATIVE-FIXNUM and MOST-POSITIVE-FIXNUM describe the limits for
;; fixnums.
(let ((integer-1 0)
(integer-2 most-positive-fixnum)
(integer-3 (1+ most-positive-fixnum))
(integer-4 (1- most-negative-fixnum)))
(true-or-false? ____ (typep integer-1 'fixnum))
(true-or-false? ____ (typep integer-1 'bignum))
(true-or-false? ____ (typep integer-2 'fixnum))
(true-or-false? ____ (typep integer-2 'bignum))
(true-or-false? ____ (typep integer-3 'fixnum))
(true-or-false? ____ (typep integer-3 'bignum))
(true-or-false? ____ (typep integer-4 'fixnum))
(true-or-false? ____ (typep integer-4 'bignum))
;; Regardless of whether an integer is a fixnum or a bignum, it is still
;; an integer.
(true-or-false? ____ (typep integer-1 'integer))
(true-or-false? ____ (typep integer-2 'integer))
(true-or-false? ____ (typep integer-3 'integer))
(true-or-false? ____ (typep integer-4 'integer))))
(define-test subtypep
(assert-true (typep 1 'bit))
(assert-true (typep 1 'fixnum))
(assert-true (typep 1 'integer))
(assert-true (typep 2 'integer))
;; The function SUBTYPEP attempts to answer whether one type specifier
;; represents a subtype of the other type specifier.
(true-or-false? ____ (subtypep 'bit 'integer))
(true-or-false? ____ (subtypep 'vector 'array))
(true-or-false? ____ (subtypep 'string 'vector))
(true-or-false? ____ (subtypep 'null 'list)))
(define-test list-type-specifiers
;; Some type specifiers are lists; this way, they carry more information than
;; type specifiers which are symbols.
(assert-true (typep (make-array 0) '(vector * 0)))
(assert-true (typep (make-array 42) '(vector * 42)))
(assert-true (typep (make-array 42 :element-type 'bit) '(vector bit 42)))
(assert-true (typep (make-array '(4 2)) '(array * (4 2))))
(true-or-false? ____ (typep (make-array '(3 3)) '(simple-array t (3 3))))
(true-or-false? ____ (typep (make-array '(3 2 1)) '(simple-array t (1 2 3)))))
(define-test list-type-specifiers-hierarchy
;; Type specifiers that are lists also follow hierarchy.
(true-or-false? ____ (subtypep '(simple-array t (3 3)) '(simple-array t *)))
(true-or-false? ____ (subtypep '(vector double-float 100) '(vector * 100)))
(true-or-false? ____ (subtypep '(vector double-float 100) '(vector double-float *)))
(true-or-false? ____ (subtypep '(vector double-float 100) '(vector * *)))
(true-or-false? ____ (subtypep '(vector double-float 100) '(array * *)))
(true-or-false? ____ (subtypep '(vector double-float 100) t)))
(define-test type-coercion
(assert-true (typep 0 'integer))
(true-or-false? ____ (typep 0 'short-float))
(true-or-false? ____ (subtypep 'integer 'short-float))
(true-or-false? ____ (subtypep 'short-float 'integer))
;; The function COERCE makes it possible to convert values between some
;; standard types.
(true-or-false? ____ (typep (coerce 0 'short-float) 'short-float)))
(define-test atoms-are-anything-thats-not-a-cons
;; In Lisp, an atom is anything that is not a cons cell. The function ATOM
;; returns true if its object is an atom.
(true-or-false? ____ (atom 4))
(true-or-false? ____ (atom '(1 2 3 4)))
(true-or-false? ____ (atom '(:foo . :bar)))
(true-or-false? ____ (atom 'symbol))
(true-or-false? ____ (atom :keyword))
(true-or-false? ____ (atom #(1 2 3 4 5)))
(true-or-false? ____ (atom #\A))
(true-or-false? ____ (atom "string"))
(true-or-false? ____ (atom (make-array '(4 4)))))
(define-test functionp
;; The function FUNCTIONP returns true if its arguments is a function.
(assert-true (functionp (lambda (a b c) (+ a b c))))
(true-or-false? ____ (functionp #'make-array))
(true-or-false? ____ (functionp 'make-array))
(true-or-false? ____ (functionp (lambda (x) (* x x))))
(true-or-false? ____ (functionp '(lambda (x) (* x x))))
(true-or-false? ____ (functionp '(1 2 3)))
(true-or-false? ____ (functionp t)))
(define-test other-type-predicates
;; Lisp defines multiple type predicates for standard types..
(true-or-false? ____ (numberp 999))
(true-or-false? ____ (listp '(9 9 9)))
(true-or-false? ____ (integerp 999))
(true-or-false? ____ (rationalp 9/99))
(true-or-false? ____ (floatp 9.99))
(true-or-false? ____ (stringp "nine nine nine"))
(true-or-false? ____ (characterp #\9))
(true-or-false? ____ (bit-vector-p #*01001)))
(define-test guess-that-type
;; Fill in the blank with a type specifier that satisfies the following tests.
(let ((type ____))
(assert-true (subtypep type '(simple-array * (* 3 *))))
(assert-true (subtypep type '(simple-array * (5 * *))))
(assert-true (subtypep type '(simple-array array *)))
(assert-true (typep (make-array '(5 3 9) :element-type 'string) type))
(assert-true (typep (make-array '(5 3 33) :element-type 'vector) type))))

View File

@ -0,0 +1,88 @@
;; Copyright 2013 Google Inc.
;;
;; Licensed under the Apache License, Version 2.0 (the "License");
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
(defun test-variable-assignment-with-setf ()
;; the let pattern allows us to create local variables with
;; lexical scope.
(let (var_name_1 (var_name_2 "Michael"))
;; variables may be defined with or without initial values.
(and
(equalp var_name_2 "Michael")
; new values may be assigned to variables with setf
(setf var_name_2 "Janet")
(equalp var_name_2 "Janet")
; setf may assign multiple variables in one form.
(setf var_name_1 "Tito"
var_name_2 "Jermaine")
(equalp var_name_1 "Tito")
(equalp var_name_2 "Jermaine"))))
(defun test-setf-for-lists ()
;; setf also works on list elements
(let (l)
(setf l '(1 2 3))
(equalp l '(1 2 3))
; First second and third are convenient accessor functions
; referring to the elements of a list
; For those interested, they are convenient to car, cadr, and caddr
(setf (first l) 10)
(setf (second l) 20)
(setf (third l) 30)
(equalp l '(10 20 30))))
(defparameter param_name_1 "Janet")
; defparameter requires an initial form. It is a compiler error to exclude it
;(defparameter param_no_init) ;; this will fail
(defconstant additive_identity 0)
; defconstant also requires an initial form
; (defconstant constant_no_init)
; reassigning parameters to new values is also ok, but parameters carry the
; connotation of immutability. If it's going to change frequently, it should
; be a var.
(setf param_name_1 "The other one")
; reassigning a constant is an error.
; this should result in a compile time error
; (setf additive_identity -1)
;; -------------------------------
;; below is necessary to run tests.
;; -------------------------------
(defvar failed-test-names nil)
(defun run-test (testfun)
(let ((fun-name (function-name testfun)))
(if (apply testfun '())
(format t ".")
(progn
(setf failed-test-names (cons fun-name failed-test-names))
(format t "F")))))
(defun function-name (function) (nth-value 2 (function-lambda-expression function)))
(run-test #'test-variable-assignment-with-setf)
(run-test #'test-setf-for-lists)
(format t "~%")
(defun report-failure (test-name)
(format t "~S failed.~%" test-name))
(if (endp failed-test-names) ; no failed tests
(format t "all tests pass.~%")
(mapcar #'report-failure failed-test-names))

View File

@ -0,0 +1,53 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; Vectors are one-dimensional arrays. This means that general array operations
;;; will work on vectors normally. However, Lisp also defines some functions for
;;; operating on sequences - which means, either vectors or lists.
(define-test vector-basics
;; #(...) is syntax sugar for defining literal vectors.
(let ((vector #(1 11 111)))
(true-or-false? ____ (typep vector 'vector))
(assert-equal ____ (aref vector 1))))
(define-test length
;; The function LENGTH works both for vectors and for lists.
(assert-equal ____ (length '(1 2 3)))
(assert-equal ____ (length #(1 2 3))))
(define-test bit-vector
;; #*0011 defines a bit vector literal with four elements: 0, 0, 1 and 1.
(assert-equal #*0011 (make-array 4 :element-type 'bit :initial-contents ____))
(true-or-false? ____ (typep #*1001 'bit-vector))
(assert-equal ____ (aref #*1001 1)))
(define-test bitwise-operations
;; Lisp defines a few bitwise operations that work on bit vectors.
(assert-equal ____ (bit-and #*1100 #*1010))
(assert-equal ____ (bit-ior #*1100 #*1010))
(assert-equal ____ (bit-xor #*1100 #*1010)))
(defun list-to-bit-vector (list)
;; Implement a function that turns a list into a bit vector.
____)
(define-test list-to-bit-vector
;; You need to fill in the blank in LIST-TO-BIT-VECTOR.
(assert-true (typep (list-to-bit-vector '(0 0 1 1 0)) 'bit-vector))
(assert-equal (aref (list-to-bit-vector '(0)) 0) 0)
(assert-equal (aref (list-to-bit-vector '(0 1)) 1) 1)
(assert-equal (length (list-to-bit-vector '(0 0 1 1 0 0 1 1))) 8))

133
lisp-koans/lisp-koans.lisp Normal file
View File

@ -0,0 +1,133 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
(defpackage #:lisp-koans.core
(:use #:common-lisp
#:lisp-koans.test)
(:export #:main))
(in-package :lisp-koans.core)
(defvar *all-koan-groups*
(with-open-file (in #p".koans")
(with-standard-io-syntax (read in))))
(defvar *collected-results* nil)
;;; Functions for loading koans
(defun package-name-from-group-name (group-name)
(format nil "LISP-KOANS.KOANS.~A" group-name))
(defun load-koan-group-named (dirname koan-group-name)
(let* ((koan-name (string-downcase (string koan-group-name)))
(koan-file-name (concatenate 'string koan-name ".lisp"))
(koan-package-name (package-name-from-group-name koan-group-name)))
(unless (find-package koan-package-name)
(make-package koan-package-name
:use '(#:common-lisp #:lisp-koans.test)))
(let ((*package* (find-package koan-package-name)))
(load (concatenate 'string dirname "/" koan-file-name)))))
(defun load-all-koans (dirname)
(loop for koan-group-name in *all-koan-groups*
do (load-koan-group-named dirname koan-group-name)))
;;; Functions for executing koans
(defun execute-koans ()
(loop for koan-group-name in *all-koan-groups*
for package-name = (package-name-from-group-name koan-group-name)
for kg-results = (run-koans package-name)
collect (list koan-group-name kg-results) into results
do (print-koan-group-progress koan-group-name kg-results)
while (every (lambda (x) (eq x :pass)) (second (first kg-results)))
finally (setf *collected-results* results)))
;;; Functions for printing progress
(defun print-koan-group-progress (name results)
(format t "~%Thinking about ~A~%" name)
(dolist (result (reverse results))
(destructuring-bind (test-name results) result
(let ((format-control (if (every (lambda (x) (equalp :pass x)) results)
" ~A has expanded your awareness.~%"
" ~A requires more meditation.~%")))
(format t format-control test-name)))))
;;; Functions for processing results
(defun n-passed-koans-overall (collected-results)
(flet ((all-asserts-passed-in-koan-p (result)
(every (lambda (x) (eq :pass x)) (second result))))
(loop for kg in collected-results
sum (count-if #'all-asserts-passed-in-koan-p (second kg)))))
(defun any-assert-non-pass-p ()
(dolist (k-group-result *collected-results*)
(dolist (result (second k-group-result))
(dolist (one-assert (second result))
(when (not (equal one-assert :pass))
(return-from any-assert-non-pass-p one-assert))))))
;;; Functions for printing results
(defun koan-status-message (koan-status)
(cond ((find :incomplete koan-status) "A koan is incomplete.")
((find :fail koan-status) "A koan is incorrect.")
((find :error koan-status) "A koan signaled an error.")
(t (format nil "Last koan status: ~A." koan-status))))
(defun print-next-suggestion-message (dirname)
(let ((filename (caar (last *collected-results*)))
(koan-name (caaadr (car (last (last *collected-results*)))))
(koan-status (reverse (cadaar (cdar (last (last *collected-results*)))))))
(format t "~&You have not yet reached enlightenment.
~A
Please meditate on the following code:
File \"~A/~(~A~).lisp\"
Koan \"~A\"
Current koan assert status is \"~A\"~%~%"
(koan-status-message koan-status) dirname filename koan-name koan-status)))
(defun print-completion-message ()
(format t "
*********************************************************
That was the last one, well done! ENLIGHTENMENT IS YOURS!
*********************************************************
If you demand greater challenge, take a look at extra-credit.lisp
Or, let the student become the teacher:
Write and submit your own improvements to https://github.com/google/lisp-koans!~%
"))
(defun print-progress-message ()
(format t "You are now ~A/~A koans and ~A/~A lessons closer to reaching enlightenment.~%~%"
(n-passed-koans-overall *collected-results*)
(test-total-count)
(1- (length *collected-results*))
(length *all-koan-groups*)))
(defun output-advice (dirname)
(cond ((any-assert-non-pass-p)
(print-next-suggestion-message dirname)
(print-progress-message))
(t (print-completion-message))))
;;; Main
(defun main (&optional (dirname "koans"))
(load-all-koans dirname)
(execute-koans)
(output-advice dirname))

Some files were not shown because too many files have changed in this diff Show More