This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-09-12
Channels
- # admin-announcements (3)
- # beginners (17)
- # boot (52)
- # braveandtrue (95)
- # cider (4)
- # cljs-dev (2)
- # clojars (118)
- # clojure (146)
- # clojure-art (4)
- # clojure-austin (1)
- # clojure-finland (20)
- # clojure-italy (33)
- # clojure-nl (1)
- # clojure-russia (49)
- # clojure-spec (136)
- # clojure-uk (28)
- # clojurescript (134)
- # clojutre (1)
- # conf-proposals (64)
- # cursive (3)
- # datomic (76)
- # hoplon (11)
- # ipfs (7)
- # jobs (1)
- # jobs-rus (1)
- # leiningen (4)
- # luminus (4)
- # mount (9)
- # om (34)
- # onyx (34)
- # proton (1)
- # re-frame (4)
- # reagent (35)
- # ring (2)
- # ring-swagger (6)
- # rum (15)
- # untangled (87)
@madarauchiha: are you still looking at the infix->prefix question?
I’m guessing you’re in a timezone that won’t see this until later, so I’ll dump in here for now
I’ll confess that there may well be a much better way (and when I did it myself I used parsatron, so I was already doing it differently)
first of all, I wanted to test for operators at each level. * and / have the same precedence, as do + and -
to get to this, we want to group (1 + 2) as a, (3 - 4) as b, etc ARGH! I did this back to front! It still works but I need to invert everything that follows! Please bear this in mind as you read!!!! I’ll fix this at the end, promise!!! 😳
a function for that is split-with. The test to use is anything that’s not a * or /. That can be done by complementing the set:
(def mtest (complement '#{* /}))
decent start. So now let’s loop to split it all up. We’ll want to pull those operators out too, so I’ll use some destructuring to get at it, and the remainder:
(loop [result [] ex test-expression]
(let [[next-operand [op & remaining]] (split-with mtest ex)
new-result (conj result next-operand)]
(if op
(recur (conj new-result op) remaining)
new-result)))
this let us split by the * character! For anyone watching… maybe there was an easier way?
anyway, this was useful. It also looks like something we could use to split by the + and - characters. So I can wrap it in a function and parameterize on that
(defn factor-out [the-test expression]
(loop [result [] ex expression]
(let [[next-operand [op & remaining]] (split-with the-test ex)
new-result (conj result next-operand)]
(if op
(recur (conj new-result op) remaining)
new-result))))
so, now I have a way to take an expression and pull it down into the form '(a op b op c)
. So now we want to process it into (op a (op b c))
remember, I said they start as binary operators, so that means whenever we see '(a op ….)
then we’re going to turn that into '(op a …)
If we don’t see an op (i.e. just have a single thing like '(a)
then we just want to return that
if we pull out the first item, the operator, and the remainder, then when there is a remainder still to be processed, we return a list of the operator, the first item and then everything else processed. When there is no remainder to be processed (the operator should be nil in this case) then just return that first item
(defn prefix-form [[item op & remainder]]
(if (seq remainder)
(list op item (prefix-form remainder))
item))
Turns out, they’re OK. The reason is clearer if we think about operands in the general sense. If we look at a language that supports ^ for raising to a power, then this has higher precedence than */ which has higher precedence than +-
basically, we want to apply our method at each level of precedence, and then do it again with new tests each time
=> (prefix-form (factor-out stest test-expression))
(+ (1) (- (2 * 3) (+ (4 * 5) (6))))
the precedence is fixed, and it’s all still working. This is what abstraction does for us 😄
each element in the prefix form (the things being added and subtracted) now just need to be operated on in the same way, except with */ instead of +-
so… let’s re-write prefix-form, this time taking a function saying what we want to do with the elements that show up as arguments:
(defn prefix-form-better [f [item op & remainder]]
(if (seq remainder)
(list op (f item) (prefix-form remainder))
(f item)))
Let me read it all, one moment 😄
OK… I have just 2 lines of code to the final solution now 🙂 (and they’re really simple lines)
What does this mean?
Complement of a set?
That's news to me.
I knew it was an iterable
So a set is also a predicate that accepts an argument and returns whether or not that argument is in the set
So complement will return the predicate which returns true when the argument isn't in the set
Gotcha
So you can do things like (filter set1 set2)
to get the intersection
Alright back to reading
Yeah, I know what complement does
I wrote my own before learning about it 😄
Then I thought "wait, there's no way a functional language doesn't already have complement, I'm an idiot"
if we look at the final steps first… evaluating things like ((2) * (3))
can be done by calling prefix-form-better
using identity, and we get:
(prefix-form-better identity '((2) * (3)))
(* (2) (3))
since we have this handy function that we’re applying, then instead of identity, we can use first
define that to be a function, and it can be the “f” that gets passed in to make the prefix-sum!
(def prefix-prod #(prefix-form-better first (factor-out mtest %)))
(def prefix-sum #(prefix-form-better prefix-prod (factor-out stest %)))
so when I did a copy/paste to Slack, I had to correct manually. Sometimes I stuffed up 🙂
This is another thing I wanted to ask, because I'm unfamiliar with how developing in a Lispy language works
Do you generally write code in the repl, and then copy it to a file?
Do you write the code in the file, load it in repl, then play with it? How do you persist the work from the REPL afterwards?
most people do it in emacs or another editor that lets you apply read/eval to parts of the buffer
that typically involves a learning curve on how to do it. It doesn’t take long, but it can be annoying
I hope I didn’t write at too simple a level for you. You were away and couldn’t ask questions, so I figured I should explain it to the simplest level that I could think of
That's fine, it's really helpful
Thanks a lot for your time!
I also apologize for getting the precedence wrong. No idea what I was thinking there! I think it’s very cool that it just involved a little bit of swapping of symbols and it worked again 🙂
you’re welcome on the time. Hopefully someone will offer a better solution than this one
(defn fp [t exp]
(loop [r [] ex exp]
(let [[n [op & rem]] (split-with t ex)
newr (conj r n)]
(if op
(recur (conj newr op) rem)
newr))))
(defn prefix [f [l op & r]] (if (seq r) (list op (f l) (prefix f r)) (f l)))
(def stest (complement '#{+ -}))
(def mtest (complement '#{* /}))
(def prefix-prod #(prefix first (fp mtest %)))
(def prefix-sum #(prefix prefix-prod (fp stest %)))
(prefix-sum ‘(1 + 2 * 3 - 4 * 5 + 6))
I think the result is wrong though 😛
user=> (prefix-sum '(1 + 2 * 3 - 4 * 5 + 6))
(+ 1 (- (* 2 3) (+ (* 4 5) 6)))
That should be something like
(+ 1 (+ (- (* 2 3) (* 4 5)) 6))