I’m starting a project based on the convergence of subjects I’m currently studying. As a high school dropout, I’m desperately trying to make up for the tremendous gaps in my knowledge due to lack of education, particularly in mathematics.
For example, I tried to do some Advent of Code but as soon as math concepts are mentioned that I am unfamiliar with, I immediately feel like I’m not yet ready for this stuff, and ought to be spending my time going back to the fundamentals.
So I’ve been using Khan Academy to learn basic algebra, and since I’m also learning Clojure, have naturally gravitated to using it to assist me in this process, by writing functions to solve the problems for me.
The resulting experience is so delightful that it seems like cheating - but then I realize that by forcing myself to write a program to solve the problem I have actually more thoroughly grasped the concepts, and may have hit upon a very interesting learning path that could be used by others.
I feel like it ought to be developed into some kind of platform for students to learn math and programming (in Clojure, obviously) simultaneously, while opening up opportunities for educational synergy. My vision is to design a Clojure-based computer algebra system in the form of skeleton functions that the student will fill in in order to solve the problem at hand.
I have the first part of it, the infix parser and some basic operations in a live notebook here. I’m very curious to receive some feedback from others on whether I’m going about it the right way, since this is about as naive as it gets. For example, the parsed equation comes in the form of nested vectors:
user=> (equation "1+x=65")
([:left [:number "1"] "+" [:variable "x"]] [:right [:number "65"]])
Say I just want to extract the 1
on the left. What I have so far works, but feels really awkward:
(defn left [s]
(rest (first (equation s))))
(defn right [s]
(rest (last (equation s))))
(defn left-num [s]
(cond
(= :number (first (first (left s))))
(Integer/parseInt (last (first (left s))))
(= :number (first (last (left s))))
(Integer/parseInt (last (last (left s))))
:else nil))
user=> (left-num "1+x=65")
1
How do people normally deal with nested vectors like this? Should I use something like specter? Or am I missing a much more idiomatic way to do this with another library or just the core? Since I’m already using instaparse to reduce the parsing job to nearly nil, it just looks odd to see the work involved in un-parsing it so disproportionally verbose. But more importantly, it totally obscures what’s going on… just looks like a mess of first
s, last
s, etc.
Thank you for your feedback!