Skip to content

Latest commit

 

History

History
131 lines (96 loc) · 7.68 KB

2020-06-30.md

File metadata and controls

131 lines (96 loc) · 7.68 KB

Learning Clojure in Public - Week 2 Day 2 (9/35)

Expectations

Today the expectation was to finish the rocket that I started building yesterday, in Chapter 8. I also wanted to solve problem 28 on 4clojure. I have been trying in passing for the past couple of days, starting from scratch, but each time hitting errors that I have trouble debugging. A third time's the charm! Any other 4clojure problem I do is going to be a great bonus.

What I learned

As building the rocket was probably the most complex program I have written in Clojure (yes, as much as possible I have tried to re-write the functions instead of copy/pasting them -- which has made debugging much harder...), I have learned that you cannot depend on a function that hasn't been defined beforehand. There is no hoisting like in JavaScript. Where a function is defined does matter.

You can however use (declare function-name) at the top of the file to tell Clojure that this function will eventually be defined in this file, at a later time.

My first non-trivial program

This was the largest program I wrote to date with Clojure. I didn't really focus on the math: it's not too complicated and it's possible to figure it out with some effort, it was not really the goal here. So I took the words of the author as truth and simply tried to implement the functions without looking (sometimes, not most of the time). In the end I didn't learn a lot of new functions but I did learn a lot of tips and tricks on how to structure a program in clojure.

It was interesting working with immutable data structures and passing them to pure functions, which is a big departure from what I'm used to (Object Oriented Programming).

Chapter 8 problems

Unfortunately I had trouble making the example code work (and the code from the source also had issues) so I was only minimally able to complete the problems in this chapter.

2. We assumed the force of gravity resulted in a constant 9.8 meter/second/second acceleration towards the earth, but in the real world, gravity falls off with the inverse square law. Using the mass of the earth, mass of the spacecraft, and Newton’s constant, refine the gravitational force used in this simulation to take Newton’s law into account. How does this affect the apoapsis?

Here we're trying to use the inverse square law to determine the force. The force is F = G _ m1 _ m2/r^2. This is what we had originally:

(defn gravity-force
  "The force vector, each component in Newtons, due to gravity."
  [craft]
  ; Since force is mass times acceleration...
  (let [total-force (* g (mass craft))]
    (-> craft
        ; Now we'll take the craft's position
        :position
        ; in spherical coordinates,
        cartesian->spherical
        ; replace the radius with the gravitational force...
        (assoc :r total-force)
        ; and transform back to Cartesian-land
        spherical->cartesian)))

I am assuming that the distance between Earth and the rocket is going to be between the two centers, and Earth's center is at {:x 0, :y: 0, :z 0}. We can define a helper function that will compute that distance:

(defn distance-from-earth
  [coordinates]
  (Math.sqrt (+ (pow (:x coordinates) 2)
                (pow (:y coordinates) 2)
                (pow (:z coordinates) 2))))

So we can redefine the function computing the force as such:

(defn gravity-force
  "The force vector, each component in Newtons, due to gravity."
  [craft]
  (let [total-force (/ (* g (mass craft) 5.97219e24) (distance-from-earth (:position craft)))]
    (-> craft
        ; Now we'll take the craft's position
        :position
        ; in spherical coordinates,
        cartesian->spherical
        ; replace the radius with the gravitational force...
        (assoc :r total-force)
        ; and transform back to Cartesian-land
        spherical->cartesian)))

3. We ignored the atmosphere, which exerts drag on the craft as it moves through the air. Write a basic air-density function which falls off with altitude. Make some educated guesses as to how much drag a real rocket experiences, and assume that the drag force is proportional to the square of the rocket’s velocity. Can your rocket still reach orbit?

For this function we're going to assume that the drag is proportional to the square of the rocket's velocity, which we already have. So this is completely out of nowhere and not at all researched, but let's say the drag is taking into account the density of the atmosphere, a drag coefficient (0.04 for a streamlined body, and we assume both crafts have the same drag coefficient), the area against which this atmosphere is dragging and the square of the velocity. The highest we are the less atmosphere, the less dense the atmosphere. So this formula here could be a variable of only the altitude and the velocity of the craft. Let's say that the top of the mesophere (85km from the surface of the Earth) is when the atmosphere stops having a drag. So now we can write the function as such:

(defn drag
  [velocity craft]
  (let [[v2 (+ (pow (:x velocity) 2)
               (pow (:y velocity) 2)
               (pow (:z velocity) 2))]
        [density (max 0 (- 85000 (altitude craft)))]](* 0.5 0.04 10 ())))

This is definitely something I want to get back to after ClojureFam is over (making the code run and solving question 1 and 4)

4clojure's 28th problem

This problem was an issue for me and I tried a few times and ended up with StackOverflows. Today I tried again, from scratch and I got to the solution almost immediately. The key here was to start small with the end of the function (the concat) and gradually add more cases (is the element a collection? Are we in the base case?). So here is my solution:

(fn my-flatten
  [xs]
  (if (empty? xs)
    '()
    (concat (if (coll? (first xs))
              (my-flatten (first xs))
              (list (first xs)))
            (my-flatten (rest xs)))))

Other 4clojure problems

All in all, I completed 11 4clojure problems today which is much more than I anticipated. I wanted to reach my milestone of 60 problems (my goal is 100 and I have 4 more weeks to get there).

One interesting problem is 83. I solved it with a cond, that I learned today but this is somewhat brute forcing it:

(fn half-truth [& args]
  (cond
    (every? true? args) false
    (some true? args) true
    :else false))

At least it works.

However, there's a much smarter way to go about it, as my cohort-mates showed me and it is #(not= %&). So how does this work? We know that not= is "not equal" and %& is the rest of the arguments. Simply if all the arguments are the same, then it will return false (if they are all true or all false), and in the other case (some true, some false), it will return true.

Takeaways

While I didn't get the program to fully run today, I still learned a lot about how to structure my code. I don't think I am missing much and will be able to get back to it. I was also able to complete 11 4clojure challenges, including a couple that were head-scratchers for me.

Tomorrow will be Chapter 10 (there is no Chapter 9) and it will be the end of Clojure from the Ground Up. It's been a rewarding experience so far but I am super excited to tackle Clojure for the Brave and True: Learn the Ultimate Language and Become a Better Programmer.

Let's end the day with a tally of what I completed:

  • Chapter 8 of Clojure From the Ground Up
  • And two of its questions
  • 11 4clojure problems, which means I am 60% done with goal of 100 problems!