This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2024-04-10
Channels
- # babashka (8)
- # beginners (38)
- # calva (28)
- # cider (7)
- # clj-kondo (6)
- # clojure (113)
- # clojure-austin (2)
- # clojure-berlin (11)
- # clojure-europe (12)
- # clojure-losangeles (5)
- # clojure-madison (1)
- # clojure-nl (1)
- # clojure-norway (17)
- # clojure-spec (5)
- # clojure-uk (4)
- # clojurescript (23)
- # events (2)
- # fulcro (6)
- # hyperfiddle (2)
- # missionary (5)
- # music (1)
- # off-topic (20)
- # portal (5)
- # reitit (4)
- # releases (1)
- # scittle (3)
- # sql (10)
- # squint (2)
The comment
macro loves repl driven development https://www.youtube.com/watch?v=8RpvJeSbgcI
In a few weeks I start teaching an intro course in functional programming techniques using clojure as the programming language. I'm struggling to finish preparation in time. But I have a series of 12 homework exercises for my students to complete. some are easy some are difficult. I would be really happy if anyone wants to volunteer to work through some or all of the problems and give me feedback. The course consists of 12 hours of classroom instruction, divided into 4 sessions of 3 hours each. If anyone would be interested in taking a look, I'll put a mirror here https://github.com/jimka2001/clojurein-student, Don't hesitate to let me know if you find something wrong. I'm sure there are plenty of typos, and probably plenty of other issues.
Just looked at hello-test
. I think you have some extra calls to =
in the tests. To make the tests pass I made this change:
diff --git a/clojurein-source-code/test/homework/hello_test.clj b/clojurein-source-code/test/homework/hello_test.clj
index 52082c1..5b78ef7 100644
--- a/clojurein-source-code/test/homework/hello_test.clj
+++ b/clojurein-source-code/test/homework/hello_test.clj
@@ -14,19 +14,19 @@
(testing-with-timeout "prefix"
(for [i (range 100)
:let [who (str i)]]
- (is (= (string/starts-with? (sut/hello who) "Hello, "))))))
+ (is (string/starts-with? (sut/hello who) "Hello, ")))))
(deftest t-suffix
(testing-with-timeout "suffix"
(for [i (range 100)
:let [who (str i)]]
- (is (= (string/ends-with? (sut/hello who) "."))))))
+ (is (string/ends-with? (sut/hello who) ".")))))
(deftest t-infix
(testing-with-timeout "infix"
(for [i (range 100)
:let [who (str i)]]
- (is (= (string/includes? (sut/hello who) who))))))
+ (is (string/includes? (sut/hello who) who)))))
thanks.
Also, I'd suggest reducing the number of values you test in these tests
(deftest t-times-identity
(testing-with-timeout "times_identity"
(doseq [re (range -100 100)
im (range -100 100)
:let [z [(float re) (float im)]]]
(is (= z (sut/times sut/times-identity z)))
(is (= z (sut/times z sut/times-identity))))))
They create a lot of garbage and take a long time when the gc kicks in, especially if you're still throwing exceptions in the implementation. I'd suggest that having to debug the jvm gc behaviour is only going to cause confusion amongst your students when they run these tests. Maybe creating a smaller collection of random numbers and checking those for the identity property would be a less confusing strategy?can you elaborate. I donāt understand your comment about students debugging jvm gc behavior.
if I start a repl I can't actually load the homework.reduce
namespace because it throws exceptions at the top level. (eg. (def times-identity ,,, (throw ,,,))
). So if I change those to a value that will end up throwing an exception in the t-times-identity
test (like 1
- ie. not a vector), I can now at least load the namespace without exceptions. If I run one of those tests without changing the code (so it still throws an exception) then the jvm locks up for several minutes
Test Summary
homework.reduce-test 120010 ms
t-times-identity 120010 ms
Tested 1 namespaces in 120010 ms
Ran 8632 assertions, in 1 test functions
1 failures
8631 errors
telling me that
Error in t-times-identity
times_identity
expected: (= z (sut/times sut/times-identity z))
error: java.lang.UnsupportedOperationException: nth not supported on this type: Long
120000 times. I assumed that the amount of time this took was caused by a gc pause and didn't wait for the process to finish to discover this answer... I've just done that now and misreported the symptoms. Apologies. I think as a student I would still want to run all the tests in that namespace and not have to wait 8-10 mins to find out I had an error.the intention is that the user cannot load the file until he fixes those issues.
youāll need to initialize times-identity
to [1.0 1.0]
and plus-identity
to [0.0 0.0]
I think moving the exception so that it is delayed is a bad idea. my idea is that the student reads the docstring, and figures out the default value. There is an extensive docstring.
On the other hand. perhaps the more clojure idiomatic way would be to not have a plus-identity
variable at all, but rather make plus
and times
be multi-variadic. calling plus
with zero arguments should return the [0.0 0.0]
, and calling times
with zero arguments should return [1.0 0.0]
potentially similar for the functions times-list
and plus-list
, they should be the 3 or more arg versions of times
and plus
I don't think that's more idiomatic. I think it's generally considered good practice to separate actions and data. But I also think it's idiomatic to not throw exceptions when loading the code. Tooling and so on will expect that the code with load without side effects.
you could create a function that returned the identity value, or use a delay
or just assert in the test (before the loop) some type checks on the value before running for 2 mins?
when I think of the order the the course will be presented. I talk about recursion in the first lecture. but I hadnāt planned to talk about var-arg functions until later. perhaps it will be good to talk about the 0-arg, 1-arg, 2-arg, and multi-arg behavior of +
and the like when talking about reduce
theyāre sort of baked-in together
also ... rather than throwing an exception, you could use a sentinel value like :not-implemented-yet
rather than throw an exception at the top level
BTW I find it strange that the clojure tester continues after an exception. Iād prefer the test to fail on first wrong assertion, and go on to the next test.
ahhh is that a bug in my testing-with-timeout
?
Hi @U0P0TMEFJ Iāve pushed some changes to reduce.clj
and the corresponding tests. It should make the problem of loading go away.
@U010VP3UY9X you have a bracket missing (I think)
diff --git a/clojurein-source-code/src/homework/reduce.clj b/clojurein-source-code/src/homework/reduce.clj
index 6327458..f5d4d18 100644
--- a/clojurein-source-code/src/homework/reduce.clj
+++ b/clojurein-source-code/src/homework/reduce.clj
@@ -82,7 +82,7 @@
;; you may look closely at the test cases;
;; you should be able to reverse engineer the value of
;; times_identity by looking at the test case.
- ([] (throw (ex-info "Missing single expression, not yet implemented" {}))
+ ([] (throw (ex-info "Missing single expression, not yet implemented" {})))
([a] a)
([[a b] [c d]]
[(- (* a c) (* b d))
thanks. I need a way to figure out whether parens balance.
I was using lein check
but it does a lot more than paren checking
I run tests on the original code, but lines (thow (ex-info ā¦))
are inserted by a python script.
fixed!
Generating lisp from python seems like an "interesting" choice š ... but you can use clj-kondo (https://github.com/clj-kondo/clj-kondo/blob/master/doc/install.md) to lint the generated code in batch. Maybe something like
clj-kondo --lint clojurein-source-code/{src,test} | grep -i 'Unmatched bracket'
????The reason Iām using python is because I already have the basic python script. I teach several classes using very similar grading models. 1. graph theory, students must write python code to implement standard graph algorithms. 2. into to scala, students write scala code 3. intro to functional programming for apprentices. students write scala code 4. techniques in functional programming, students write clojure code In each case I have assignments where students need to write code to solve certain problems, and test suites the student code must pass. In each case I have the solution code which I wrote, which I use to test the test cases. The test code has annotations that indicate certain parts of the code to omit when constructing templates for the students. When I build the student repository, I run a script (in python) which builds the student templates from the golden code omitting certain sections and replacing with some sort of exception code. The promise is that the student can run the test cases to find the exceptions, and replace each such exception with valid code to pass the tests. The clojure gold code looks like this.
(defn hello
"This function computes a String, but does not print it.
Given a string, who, such as \"Jim\", the function
computes the string \"Hello, Jim.\", i.e.,
Beginning with \"Hello\" with a capital H
then a comma, \",\",
then a space \" \",
then the value of who
and finally a period, \".\""
[who]
#_challenge (cl-format false "Hello, ~A." who)
)
(defn poly-evaluate
"given a polynomial and a numerical value evaluate the polynomial.
E.g. (poly-evaluate {2 2.2, 0, -1.1) 4.3))
==> 2.2*pow(4.3 2) - 1.1*pow(4.3 0)"
[a x]
;; CHALLENGE: student must complete the implementation.
(reduce-kv (fn [acc exponent coef]
(+ acc (* coef (Math/pow x exponent))))
0
a)
)
the golden code works and passes the tests
the filter finds the beginning of a TO-BE-OMITTED block by searching for a certain string ā;; CHALLENGEā (and others) and omits lines until the indention decreases.
If I forget and put too many matching parens on an indented line, the python script removes too many lines.
Iām currently finding these in testing.
Hence my use of the word "interesting" ... The python code has no knowledge of the structure of the data it's processing. You see the same problem using helm to process yaml files in k8s. If you parsed the data as edn and used list processing functions, you'd never get these type of syntax errors. This is why we have lisp macros and not c++ templates š ... but clj-kondo might be a quick way to find all the places where the templates are broken
perhaps I could mirror it on github to make it easy for people to peruse?
That would be nice!